24

Sto usando AngularJS con il modello di controller di alias. Non riesco ad accedere (o non so come) i metodi di direttiva da un controller genitore.Chiamare i metodi della direttiva dal controller principale in AngularJS

Ho una funzione all'interno del controller che dovrebbe chiamare un metodo di direttiva ma questo metodo di direttiva non è disponibile all'interno del valore del controller this.

Questo è quello che ho. Cosa sto facendo di sbagliato?

JS

angular.module('myApp', []). 

controller('MyCtrl', function(){ 
    this.text = 'Controller text'; 

    this.dirText = 'Directive text'; 

    this.click = function(){ 
    this.changeText(); 
    } 
}) 

.directive('myDir', function(){ 
    return { 
    restrict: 'E', 
    scope: { 
     text: '=' 
    }, 
    link: function(scope, element, attrs){ 
     scope.changeText = function(){ 
     scope.text = 'New directive text'; 
     }; 
    }, 
    template: '<h2>{{text}}</h2>' 
    }; 
}); 

HTML

<div ng-app="myApp"> 
    <div ng-controller="MyCtrl as ctrl"> 
    <h1>{{ctrl.text}}</h1> 
    <my-dir text="ctrl.dirText"></my-dir> 
    <button ng-click="ctrl.click()">Change Directive Text</button> 
    </div> 
</div> 

Here un codepen con il codice.

+3

dovrebbe essere a ritroso, definire il metodo nel controller e quindi utilizzarlo all'interno della direttiva. – levi

+0

chiama il metodo controller dalla direttiva.un buon approccio –

+0

dovrebbe utilizzare il servizio invece – maxisam

risposta

28

Se si desidera utilizzare rigorosamente isolato scope all'interno di una direttiva allora il metodo direttiva può essere chiamato solo utilizzando gli eventi angolari quali $broadcast & $emit

Nel tuo caso, è necessario utilizzare per inviare $broadcast evento per tutta la $rootScope

Il codice diventerà così.

Working Code Pen

HTML

<div ng-app="myApp"> 
    <div ng-controller="MyCtrl as ctrl"> 
    <h1>{{ctrl.text}}</h1> 
    <my-dir text="ctrl.dirText"></my-dir> 
    <button ng-click="ctrl.click()">Change Directive Text</button> 
    </div> 
</div> 

CODICE

angular.module('myApp', []). 

controller('MyCtrl', function($rootScope){ 
    var that = this; 

    this.text = 'Controller text'; 

    this.dirText = 'Directive text'; 

    this.click = function(){ 
     $rootScope.$broadcast('changeText',{}); 
    } 
}). 

directive('myDir', function(){ 
    return { 
    restrict: 'E', 
    scope: { 
     text: '=' 
    }, 
    link: function(scope, element, attrs){ 
     scope.changeText = function(){ 
     scope.text = 'New directive text'; 
     }; 
     scope.$on('changeText',function(event, data){ 
      scope.changeText() 
     }); 
    }, 
    template: '<h2>{{text}}</h2>' 
    }; 
}); 

Invece di metodo di portata bambino chiamare, è necessario trasmettere un evento e che dovrà essere ascoltato dalla direttiva scope & lo farà attivare il metodo changeText dopo aver ascoltato quell'evento.

NOTA

Uso del servizio/fabbrica sarebbe approccio migliore.

Questo si spera che possa aiutarti. Grazie.

+3

@ ianaya89 Grazie. :) felice di aiutarti. ma l'approccio non è molto buono .. –

+0

perché no? Non capisco come si possa ottenere un comportamento simile usando i servizi. – ianaya89

+0

@ ianaya89 si desidera chiamare un metodo diverso da una direttiva diversa ... per il quale si è creato un ambito isolato.che è overhead..strato isolato per definire un metodo diverso che mi incuriosisce .. –

2

Si stanno isolando la portata quando si scrive:

scope: { 
     text: '=' 
    }, 

Ecco una versione leggermente modificata del codice, questa volta, ti permette di chiamare il metodo direttiva. Per lo più ho appena sono liberato di 'scope' nella direttiva, e cambiato in utilizzando $ ambito del controllore, invece di questo, e il modello Alias ​​..

ATTENZIONE: Questo potrebbe non rispecchiare il comportamento corretto, per quanto riguarda di per quali variabili vengono modificate, ma risponde alla tua domanda mostrando come puoi accedere al metodo della direttiva dal controller. Questo di solito non è una buona idea disegno ..

http://codepen.io/anon/pen/azwJBm

angular.module('myApp', []). 

controller('MyCtrl', function($scope){ 
    var that = this; 

    $scope.text = 'Controller text'; 

    $scope.dirText = 'Directive text'; 

    $scope.click = function(){ 
    $scope.changeText(); 
    } 
}). 

directive('myDir', function(){ 
    return { 
    restrict: 'E', 
    /* scope: { 
     text: '=' 
    },*/ 
    link: function(scope, element, attrs){ 
     scope.changeText = function(){ 
     scope.text = 'New directive text'; 
     }; 
    }, 
    template: '<h2>{{text}}</h2>' 
    }; 
}); 


<div ng-app="myApp"> 
    <div ng-controller="MyCtrl"> 
    <h1>{{text}}</h1> 
    <my-dir text="dirText"></my-dir> 
    <button ng-click="click()">Change Directive Text</button> 
    </div> 
</div> 
+1

tali direttive sono un po 'inutili (ad esempio non può essere 2 a pagina –

+0

Yessir .. e quindi l'avviso .. :) – Shaunak

+0

@Petr Averyanov dipende dal vostro uso . Alcune direttive verranno utilizzate una sola volta per pagina – AlainIb

11

Ho un'altra soluzione, che consente di utilizzare l'ambito isolato e non fare affidamento sulla trasmissione. In javascript i metodi possono essere usati come variabili, puoi semplicemente passare il metodo che vuoi alla direttiva.

così in html:

<my-dir text="ctrl.dirText" change-text="ctrl.changeText"></my-dir> 

e nella direttiva

scope: { 
    text: '=', 
    changeText: '=' 
} 

Here è leggermente modyfied codepen, dove si può vedere quello che ho in mente.

+2

stai passando il ** metodo di babbo ** alla direttiva, ma la domanda riguardava la direzione inversa –

15

È possibile ottenere i metodi di direttiva di chiamata senza fare affidamento su $ broadcast o sulla rimozione dell'ambito di isolamento. Approcci simili che sono stati pubblicati qui fino ad ora si interromperanno se ci sono più di 2 istanze della direttiva su una pagina (rifletteranno tutte le stesse modifiche).

This codepen dimostra un modo più efficace per farlo.

angular.module('myApp', []) 
 
.controller('myChat', function($scope) { 
 
    
 
    function room() {return { accessor:{} }; } 
 
    $scope.rooms = { 'RoomA': new room, 'RoomB': new room, 'RoomC': new room }; 
 

 
    $scope.addMessageTo = function(roomId, msg) { 
 
     if ($scope.rooms[roomId].accessor.setMessage) 
 
     $scope.rooms[roomId].accessor.setMessage(msg); 
 
    }; 
 

 
    $scope.addMessages = function() { 
 
     $scope.addMessageTo("RoomA", "A message"); 
 
     $scope.addMessageTo("RoomB", "Two messages"); 
 
     $scope.addMessageTo("RoomC", "More messages"); 
 
    } 
 
    
 
}).directive('myChatRoom', function() { 
 

 
    return { 
 
     template: '<div>{{room}} message = {{message}}<div />', 
 
     scope: { accessor: "=", room: "@" }, 
 
     link: function (scope) { 
 
     if (scope.accessor) { 
 
      scope.accessor.setMessage = function(msg) { 
 
      scope.message = msg; 
 
      }; 
 
     } 
 
     } 
 
    }; 
 
    
 
});
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.23/angular.min.js"></script> 
 
<div ng-app="myApp"> 
 
    <div ng-controller="myChat"> 
 

 
    <div ng-repeat="(roomId, room) in rooms"> 
 
     <div my-chat-room room="{{roomId}}" accessor="room.accessor"></div> 
 
    </div> 
 

 
    <button ng-click="addMessages()">Add messages to rooms</button> 
 

 
    </div> 
 
</div>

+0

Non volevo davvero usare l'approccio di trasmissione, mi hai salvato la giornata. Cheers –

+0

@BernardV: puoi approfondire come funziona la tua soluzione e cosa fa effettivamente? – user1438038

+0

@ user1438038: Mi dispiace, ma a malapena vedo cos'altro può essere aggiunto qui. Il codice definisce interamente come funziona. E per vedere cosa fa, basta cliccare su "Esegui codice tagliato" – BernardV

0

Dopo la prova sia la $broadcast e le soluzioni oggetto control, avrei effettivamente consiglio cercando di impegnare valori o matrici. L'oggetto control è un modo semplice per ottenere il risultato desiderato, ma nei miei test è molto introvabile e soggetto a errori.

This Codepen build BernardV esempio, ma utilizza una matrice di messaggi come un binding di controllo molto visibile. Se lo desideri, puoi facilmente $watch l'array di messaggi all'interno della direttiva. L'idea di base è quella di nell'uso direttiva:

scope: { messages: "=", room: "@" }, 

da un controllore (con una serie di "stanze") si dovrebbe fare questo:

$scope.addMessages = function() { 
    angular.forEach($scope.rooms, function(room, index) { 
    room.messages.push("A new message! # " + (index+1); 
    }) 
} 

direttive indipendenti, messaggi indipendenti e altamente rilevabile. Ovviamente nella direttiva è possibile visualizzare solo l'ultimo messaggio o semplicemente associare una stringa anziché una matrice. Questa soluzione ha funzionato molto meglio per noi almeno.