在Selectize.js项

时间:2016-06-29 17:28:43

标签: javascript angularjs angular-directive selectize.js

我正在使用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();
  },

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

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

  • 无法按照$compile方法的预期以同步方式返回呈现的或“render.item ed”HTML字符串。
  • 我不知道如果将项目的元素添加到DOM中后如何呈现该项目的元素。

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

2 个答案:

答案 0 :(得分:1)

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

要模拟异步调用,我使用ngResource。我的render函数返回一个带有特殊类标记"<div class='compiledavatar'>Temporary Avatar</div>"的字符串compiledavatar。对于一两秒,您将在选择元素时看到临时头像。当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库,你无法返回一个承诺...因为它对render返回的值执行了html.replace。这就是为什么我去了一个带有类的临时字符串的路由以便稍后检索并更新。 如果有帮助,请告诉我。

答案 1 :(得分:0)

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

  1. 考虑到Selectize.js&#39;渲染缓存。 Selectize.js的标准行为是项目被缓存为render函数返回的,而不是我们对它们所做的修改。添加和删​​除某些元素后,如果我们不按顺序更新渲染缓存,则会显示缓存而非修改版本。
  2. 使用随机ID来识别要从DOM操作的元素。
  3. 使用观察者知道编辑何时完成
  4. 首先,我们定义一个修改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();
    };
    

    然后,渲染函数定义如下:

    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