2014-11-14 16 views
8

Ho una direttiva con ambito isolato che prende una variabile portata per riferimentoAngularJs: Perché l'ambito all'interno della mia direttiva non si aggiorna quando arrivano i dati asincroni?

angular.module('myApp') 
    .directive('myDirective', function() { 
     return { 
      scope: { 
       items: '=' 
      }, 
      templateUrl: 'template.html', 
      replace: true, 
      controller: 'myDirectiveCtrl', 
      controllerAs: 'ctrl' 
     }; 
    }) 
    .controller('myDirectiveCtrl', function($scope) { 
     this.items = $scope.items; 
    }); 

Questo viene fatto passare in questo modo:

<div my-directive items='items'></div> 

Nei dati controllore esterno è asincrono caricato e gli articoli ambito passato nella direttiva aggiornata:

angular.module('myApp', []) 
    .controller('myCtrl', function($scope) { 

    $scope.setItems = function() { 
     $scope.items = [ 
     'Here', 
     'There', 
     'Everywhere' 
     ]; 
    }; 
    }); 

Quando i dati viene caricato, il campo di applicazione al di fuori i miei aggiornamenti direttiva, ma dentro lo fa Non

mio html:

 <div my-directive items='items'></div> <!-- this doesn't update --> 

     Outside directive 
     <ul ng-repeat='i in items'>   <!-- this does update --> 
     <li>{{i}}</lu> 
     </ul> 

     <button ng-click="setItems()">Set items</button> 

Come posso ottenere la mia portata dentro la mia direttiva per aggiornare? Ho

Plunker here

risposta

4

Quando angolare prima corre funzione di regolatore del vostro direttiva, il vostro $scope.items === undefined, in modo che quando si fa this.items = $scope.items, la tua this.items === undefined pure.

Questo è tutto. Dopo di ciò non cambia nulla this.items.

Questo è diverso da $scope.items. $scope.items è a due vie associato all'ambito esterno, pertanto ogniqualvolta Angular rileva una modifica esternamente, imposta la variabile scope isolata.

Il modo più semplice (e più adatto, a mio parere) è quello di utilizzare la proprietà $scope direttamente nella direttiva:

<div> 
    Inside directive 
    <ul ng-repeat="i in items"> 
     <li>{{ i }}</li> 
    </ul> 
</div> 

Se si desidera utilizzare il controller come ViewModel al posto del campo di applicazione (I don' so perché si dovrebbe), si potrebbe fare:

$scope.$watchCollection("items", function(newVal, oldVal) { 
    ctrl.items = newVal; 
}); 

EDIT:

In angolare 1.3 è anche possibile fare bindToController: true nella definizione della direttiva, in modo che la proprietà del controller "items" ottenga il binding a due vie che ottiene $scope.items. Quindi, non è nemmeno necessario fare this.items = $scope.items;:

Il tuo forked plunker per illustrare.

+0

Grazie per questo. L'uso del controller è stato influenzato da questo post sul blog: http://teropa.info/blog/2014/10/24/how-ive-improved-my-angular-apps-by-banning-ng-controller.html –

+0

Non ho detto che non dovresti usare un controller. Intendevo che la variabile esposta all'oscillazione non doveva essere assegnata alla variabile del controllore. –

+1

@SteveLorimer, ho modificato la risposta con un altro approccio con 'bindToController' per far funzionare il tuo esempio –

3

Se è isolato ambito Non si può cambiare ciò che è dentro la direttiva dopo aver creato una variabile separata all'interno del controllore direttiva.

Here è il plunker aggiornato che rimuove il controller per la direttiva.

'use strict'; 

angular.module('myApp') 
    .directive('myDirective', function() { 
     return { 
      scope: { 
       items: '=' 
      }, 
      templateUrl: 'template.html', 
      replace: true 
     }; 
    }); 
1

Prova a mettere i tuoi oggetti in un oggetto. See this example at Plunker

index.html

<div my-directive items='allItems'></div> 

    Outside directive 
    <ul ng-repeat='i in allItems.items'> 
    <li>{{i}}</lu> 
    </ul> 

    <button ng-click="setItems()">Set items</button> 
</div> 

directive.js:

'use strict'; 

angular.module('myApp') 
.directive('myDirective', function() { 
    return { 
    scope: { 
     items: '=' 
    }, 
    templateUrl: 'template.html', 
    replace: true, 
    controller: 'myDirectiveCtrl', 
    controllerAs: 'ctrl' 
    }; 
}) 
.controller('myDirectiveCtrl', function($scope) { 
    this.items = $scope.items; 
}); 

template.html:

<div> 
    Inside directive 
    <ul ng-repeat="i in ctrl.items.items"> 
    <li>{{ i }}</li> 
    </ul 
</div> 

script.js:

angular.module('myApp', []) 
.controller('myCtrl', function($scope) { 
    $scope.allItems={}; 
    $scope.setItems = function() { 
    $scope.allItems.items = [ 
     'Here', 
     'There', 
     'Everywhere' 
    ]; 
    }; 
}); 

C'è una spiegazione migliore qui:

Angular - ngModel not updating when called inside ngInclude

Problemi correlati