Diretiva de seleção do Angularjs, problemas de $ digest
-
20-12-2019 - |
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);
});
}
};
});
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);
})
}
}
};
});