首页 文章

在Selectize.js项目中渲染角度指令

提问于
浏览
4

我正在使用angular-selectize在我的角度项目中使用Selectize.js .

要在Selectize.js选择器中使用自定义项,我使用的是Selectize.js'render选项:

render: {
  item: function(item, escape) {
    var avatar = '<div>' +
        '<span avatars="\'' + escape(item._id) +'\'" class="avatars">' +
        '</span>' +
        escape(item.nick) +
      '</div>';
    var compiledAvatar =  $compile(avatar)($rootScope);
    $timeout();
    return compiledAvatar.html();
  },

其中avatar是具有异步行为的自定义指令

问题是 render.item 函数需要HTML字符串作为输出,但是:

  • 无法按照 render.item 方法的预期以同步方式返回呈现的或“ $compile ed”HTML字符串 .

  • 我不知道如何在项目元素已添加到DOM后再渲染它 .

请注意,尽管调用了$ compile,但由于$ compile的异步特性,返回的字符串不是预期的编译结果,而是编译前的字符串 .

2 回答

  • 1

    一个想法是使用DOM操作,这不是最推荐的Angular方式,但我得到它working on this plunker.second one with custom directive and randomized data to simulate your compiled avatar.

    为了模拟你的异步调用,我使用ngResource . 我的render函数返回一个带有特殊类标记 compiledavatar 的字符串 "<div class='compiledavatar'>Temporary Avatar</div>" . 对于一两秒,您将在选择元素时看到临时头像 . 当ngResource调用结束时,我查找带有类 compiledavatar 的元素,然后用我下载的内容替换html . 这是完整的代码:

    var app = angular.module('plunker', ['selectize', 'ngResource']);
    
    app.controller('MainCtrl', function($scope, $resource, $document) {
      var vm = this;
      vm.name = 'World';
      vm.$resource = $resource;
      vm.myModel = 1;
      vm.$document = $document;
    
      vm.myOptions = [{
        id: 1,
        title: 'Spectrometer'
      }, {
        id: 2,
        title: 'Star Chart'
      }, {
        id: 3,
        title: 'Laser Pointer'
      }];
    
      vm.myConfig = {
        create: true,
        valueField: 'id',
        labelField: 'title',
        delimiter: '|',
        placeholder: 'Pick something',
        onInitialize: function(selectize) {
          // receives the selectize object as an argument
        },
        render: {
          item: function(item, escape) {
            var label = item.title;
            var caption = item.id;
            var Stub = vm.$resource('mydata', {});
            // This simulates your asynchronous call
    
            Stub.get().$promise.then(function(s) {
              var result = document.getElementsByClassName("compiledavatar")
              angular.element(result).html(s.compiledAvatar);
    
              // Once the work is done, remove the class so next time this element wont be changed
              // Remove class
              var elems = document.querySelectorAll(".compiledavatar");
              [].forEach.call(elems, function(el) {
                  el.className = el.className.replace(/compiledavatar/, "");
              });
    
            });
    
            return "<div class='compiledavatar'>Temporary Avatar</div>"
          }
        },
        // maxItems: 1
      };
    });
    

    为了模拟JSON API,我刚刚在plunker mydata 中创建了一个文件:

    {
        "compiledAvatar": "<div><span style='display: block; color: black; font-size: 14px;'>an avatar</span></div>"
      }
    

    当然,你编译的函数应该返回每个调用不同的东西 . 我给了我相同的证明原理 .

    此外,如果您的动态代码是Agular指令,那么这里有一个带有自定义指令和随机数据的second plunker,因此您可以更好地查看解决方案:

    数据包括自定义指令 my-customer

    [{
        "compiledAvatar": "<div><span style='display: block; color: black; font-size: 14px;'>an avatar  #1  <my-customer></my-customer></span></div>"
      },
      {
        "compiledAvatar": "<div><span style='display: block; color: black; font-size: 14px;'>an avatar  #2  <my-customer></my-customer></span></div>"
      },
    (...)
    

    该指令定义为:

    app.directive('myCustomer', function() {
      return {
        template: '<div>and a custom directive</div>'
      };
    });
    

    应用程序的主要区别在于,您必须在替换HTML时添加$ compile,并且文本应显示 An avatar #(number) and a custom directive . 我得到一个json值数组并使用一个简单的随机值来选择一个值 . 替换HTML后,我删除了类,因此下次只更改最后添加的元素 .

    Stub.query().$promise.then(function(s) {
          var index = Math.floor(Math.random() * 10);
          var result = document.getElementsByClassName("compiledavatar")
          angular.element(result).html($compile(s[index].compiledAvatar)($scope));
    
          // Remove class
          var elems = document.querySelectorAll(".compiledavatar");
          [].forEach.call(elems, function(el) {
            el.className = el.className.replace(/compiledavatar/, "");
          });
    });
    

    另外,我查看了selectize库,你不能返回一个promise ...因为它在render返回的值上执行了html.replace . 这就是为什么我去了一个带有类的临时字符串的路由以便稍后检索并更新 . 如果有帮助,请告诉我 .

  • 0

    这个答案基于@gregori的有用答案,但有以下不同之处:

    • 考虑到Selectize.js的渲染缓存 . Selectize.js的标准行为是,项目被缓存为render函数返回的,而不是我们对它们所做的修改 . 添加和删除某些元素后,如果我们不按顺序更新渲染缓存,则会显示缓存而非修改版本 .

    • 使用随机id来标识要从DOM操作的元素 .

    • 使用观察者知道编译何时完成

    首先,我们定义一个修改selectize.js渲染缓存的方法:

    scope.selectorCacheUpdate = function(key, value, type){
    
      var cached = selectize.renderCache[type][key];
    
      // update cached element
      var newValue = angular.element(cached).html(value);
    
      selectize.renderCache[type][key] = newValue[0].outerHTML;
    
      return newValue.html();
    };
    

    然后,render函数定义如下:

    function renderAvatar(item, escape, type){
    
      // Random id used to identify the element
      var randomId = Math.floor(Math.random() * 0x10000000).toString(16);
    
      var avatar = 
        '<div id="' + randomId + '">' +
          '<span customAvatarTemplate ...></span>' +
          ...
        '</div>';
    
      var compiled = $compile(avatar)($rootScope);
    
      // watcher to see when the element has been compiled
      var destroyWatch = $rootScope.$watch(
        function (){
          return compiled[0].outerHTML;
        },
        function (newValue, oldValue){
          if(newValue !== oldValue){
    
            var elem = angular.element(document.getElementById(randomId));
    
            var rendered = elem.scope().selectorCacheUpdate(item._id, compiled.html(), type);
    
            // Update DOM element
            elem.html(rendered);
    
            destroyWatch();
          }
        }
      );
    });
    
    return avatar;
    

    }

    注意:渲染缓存的关键是选择项的 valueField ,在本例中为 _id

    最后,我们在selectize配置对象中添加此函数作为选择性渲染函数:

    config = {
      ...
      render: {
        item: function(i,e){
          return renderAvatar(i, e, 'item');
        },
        option: function(i,e){
          return renderAvatar(i, e, 'option');
        }
      },
      ...
    }
    

    有关更多详细信息,请参阅如何将此解决方案添加到激发此问题的应用程序中:https://github.com/P2Pvalue/teem/commit/968a437e58c5f1e70e80cc6aa77f5aefd76ba8e3 .

相关问题