Pergunta

Estou tendo problemas para usar watch dentro de uma diretiva junto com um plugin de terceiros chamado selectize.

Eu li muito sobre $digest/$watch, mas ainda estou tendo problemas.

Meu exemplo abaixo "funciona", mas estou tentando evitar que o $digest already in progress erros.

Pode haver uma maneira melhor de abordar isso, mas não tenho certeza de como também.

desentupidor: 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);
    });
  }
};
});
Foi útil?

Solução

Tente encerrar as chamadas para terceiros dentro de um $timeout como este:

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

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

}, 0);

E não se esqueça de injetar $timeout.

Com um tempo limite de zero (deixando de fora o valor padrão 0 também…), eu acreditar é garantido que isso seja executado durante o próximo loop de digestão, evitando assim os erros já em andamento.Alguém, por favor, diga se isso estiver correto, mas usei esse truque para resolver os erros de resumo ao chamar algumas funções javascript de terceiros (tinyMce).

Veja a explicação do betaorbust nesta postagem do SO: AngularJS:Evitar erro $digest já em andamento ao chamar $scope.$apply()

Outras dicas

Recentemente criei uma diretiva para Selectize que suporta ligação bidirecional do modelo e das opções.Eu também tive que usar $timeout.

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

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

Aqui estão as partes essenciais da diretiva.Retirei algumas funcionalidades adicionais que estão na versão completa.

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); 
        })
      }
    }
  };
});
Licenciado em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top