//main controller
angular.module('myApp')
.controller('mainCtrl', function ($scope){
$scope.loadResults = function(){
console.log($scope.searchFilter);
};
});
// directive
angular.module('myApp')
.directive('customSearch', function() {
return {
scope: {
searchModel: '=ngModel',
searchChange: '&ngChange',
},
require: 'ngModel',
template: '<input type="text" ng-model="searchModel" ng-change="searchChange()"/>',
restrict: 'E'
};
});
// html
<custom-search ng-model="searchFilter" ng-change="loadResults()"></custom-search>
Ecco una direttiva semplificata per illustrare. Quando digito l'input, mi aspetto che lo console.log
in loadResults
esegua correttamente il logout di ciò che ho già digitato. In realtà registra un carattere dietro perché loadResults
è in esecuzione poco prima che la variabile searchFilter
nel controller principale stia ricevendo il nuovo valore dalla direttiva. La registrazione all'interno della direttiva tuttavia, tutto funziona come previsto. Perché sta succedendo?ngChange si attiva prima che il valore lo raggiunga dall'isolato isolato
mia soluzione
Dopo aver ottenuto una comprensione di ciò che stava accadendo con ngChange nel mio semplice esempio, ho realizzato il mio vero problema era complicato un po 'di più per il fatto che il ngModel Sono in realtà passando è un oggetto, le cui proprietà sto cambiando, e anche che sto usando la convalida del modulo con questa direttiva come uno degli input. Ho trovato che l'uso di $ timeout e $ eval all'interno della direttiva ha risolto tutti i miei problemi:
//main controller
angular.module('myApp')
.controller('mainCtrl', function ($scope){
$scope.loadResults = function(){
console.log($scope.searchFilter);
};
});
// directive
angular.module('myApp')
.directive('customSearch', function ($timeout) {
return {
scope: {
searchModel: '=ngModel'
},
require: 'ngModel',
template: '<input type="text" ng-model="searchModel.subProp" ng-change="valueChange()"/>',
restrict: 'E',
link: function ($scope, $element, $attrs, ngModel)
{
$scope.valueChange = function()
{
$timeout(function()
{
if ($attrs.ngChange) $scope.$parent.$eval($attrs.ngChange);
}, 0);
};
}
};
});
// html
<custom-search ng-model="searchFilter" ng-change="loadResults()"></custom-search>
Buona risposta, stavo eseguendo il debug di Angular per l'ultima ora per vedere cosa succede. Puoi spiegare perché ci vuole un ciclo di digest aggiuntivo per la direttiva per aggiornare l'ambito genitore? È perché il digest inizia da '$ rootScope' e si propaga verso il basso? Se ci fosse un terzo 'ng-change' lungo la strada, ci vorrebbe più cicli di digest per il valore per raggiungere il controller? – Artless
L'ambito è isolato, la direttiva non modifica direttamente l'ambito del genitore. Il modo per mantenere '=' vars synchronized è usando il famoso "binding" angolare dei dati .. che è fondamentalmente un '$ watch'. Ogni ciclo di digest angolare eseguirà il controllo sporco e quando rileva una modifica, copia il valore "manualmente". Quindi, quando la variabile direttiva cambia, ha bisogno di un $ digest da copiare nel 'genitore' (mentre il & è un accesso diretto) –
A nitpick, 'input' * innesca * il ciclo digest (non" accade ") durante un digest), e nella sua prima iterazione 'searchModel' è impostato, e' searchChange() '(che risulta in' loadResults() ') viene attivato ... Quindi, nella seconda iterazione,' searchFilter' è impostato. .. Ma questa soluzione è molto hacky. –