Вопрос

У меня возникли проблемы с использованием watch внутри директивы вместе со сторонним плагином selectize.

Я много читал о $digest /$watch, но у меня все еще возникают проблемы.

Мой пример ниже "работает", но я пытаюсь предотвратить $digest already in progress ошибки.

Возможно, есть лучший способ подойти к этому, я просто тоже не уверен, как именно.

поршень: http://plnkr.co/edit/3JjTsEU2BlxPWHtw6HaW?p=preview

app.directive('selectize', function($parse) {
return {
  restrict: 'A',
  require: ['ngModel'],
  scope: {
    ngModel: '=',
    options: '='
  },
  link: function(scope, el, attrs) {

    var $select = el.selectize({
      valueField: 'id',
      labelField: 'name'
    });

    var selectize = $select[0].selectize;

    // add options
    angular.forEach('options', function(tag) {
      selectize.addOption(tag);
    });

    scope.$watchCollection('options', function(newTags, oldTags) {

      // why are these the same objects?
      console.log('newTags', newTags);
      console.log('oldTags', oldTags);

      if (newTags !== oldTags) {
        // clear options
        selectize.clear();
        selectize.clearOptions();

        // add options
        angular.forEach(newTags, function(tag) {
          selectize.addOption(tag);
        });
      }

    });

    // if value changes without selecting an option,
    // set the option to the new model val
    scope.$watch('ngModel', function(val) {
      console.log('val', val);
      // selectize.setValue(val);
    });
  }
};
});
Это было полезно?

Решение

Попробуйте обернуть вызовы третьей стороне внутри $timeout следующим образом:

$timeout(function() {
    // clear options
    selectize.clear();
    selectize.clearOptions();

    // add options
    angular.forEach(newTags, function(tag) {
      selectize.addOption(tag);
    });

}, 0);

И не забудьте ввести $timeout.

С таймаутом, равным нулю (также не учитывается значение по умолчанию, равное 0...), я верить это гарантированно сработает во время следующего цикла дайджеста, таким образом предотвращая уже существующие ошибки.Кто-нибудь, пожалуйста, сообщите, если это правильно, но я использовал этот трюк для устранения ошибок дайджеста при вызове некоторых сторонних функций javascript (TinyMCE).

Смотрите объяснение betaorbust в этом посте SO: AngularJS :Предотвращение ошибки $digest, которая уже выполняется при вызове $scope.$apply()

Другие советы

Недавно я создал директиву для Selectize, которая поддерживает двустороннюю привязку модели и параметров.Мне также пришлось использовать $timeout.

https://github.com/machineboy2045/angular-selectize

http://plnkr.co/edit/twGAfU?p=preview

Вот основные части директивы.Я убрал некоторые дополнительные функции, которые есть в полной версии.

app.directive('selectize', function($timeout) {
  return {
    restrict: 'A',
    require: '^ngModel',
    link: function(scope, element, attrs, ngModel) {

      var config = scope.$eval(attrs.selectize);
      config.options = scope.$eval(attrs.options) || [];
      element.selectize(config);
      var selectize = element[0].selectize;

      selectize.on('option_add', refreshAngularOptions);
      scope.$watch(function(){ return ngModel.$modelValue}, refreshSelectize, true)

      function refreshAngularOptions(value, data) {
        config.options = selectize.options;
      }

      function createOption(opt){
        var newOpt = {};
        newOpt[selectize.settings.valueField] = opt;
        newOpt[selectize.settings.labelField] = opt;
        selectize.addOption(newOpt);
      }

      function refreshSelectize(value){
        $timeout(function(){
          if(angular.isArray(value))
            angular.forEach(value, createOption);
          else
            createOption(value);

          selectize.refreshOptions(false);
          selectize.setValue(value); 
        })
      }
    }
  };
});
Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top