angularJS: come chiamare la funzione dell'ambito figlio nello scopo padre


95

Come si può chiamare un metodo definito nell'ambito figlio dal suo ambito padre?

function ParentCntl() {
    // I want to call the $scope.get here
}

function ChildCntl($scope) {
    $scope.get = function() {
        return "LOL";    
    }
}

http://jsfiddle.net/wUPdW/


2
La soluzione migliore, definire un servizio, iniettare quel servizio in entrambi i controller. Oppure usa il rootcope.
tymeJV

Risposte:


145

Puoi usare $broadcastdal genitore a un bambino:

function ParentCntl($scope) {

    $scope.msg = "";
    $scope.get = function(){
        $scope.$broadcast ('someEvent');
        return  $scope.msg;        
    }
}

function ChildCntl($scope) {               
    $scope.$on('someEvent', function(e) {  
        $scope.$parent.msg = $scope.get();            
    });

    $scope.get = function(){
        return "LOL";    
    }
}

Violino funzionante: http://jsfiddle.net/wUPdW/2/

AGGIORNAMENTO : esiste un'altra versione, meno accoppiata e più testabile:

function ParentCntl($scope) {
    $scope.msg = "";
    $scope.get = function(){
        $scope.$broadcast ('someEvent');
        return  $scope.msg;        
    }

    $scope.$on('pingBack', function(e,data) {  
        $scope.msg = data;        
    });
}

function ChildCntl($scope) {               
    $scope.$on('someEvent', function(e) {  
        $scope.$emit("pingBack", $scope.get());        
    });

    $scope.get = function(){
        return "LOL";    
    }
}

Fiddle: http://jsfiddle.net/uypo360u/


È davvero fantastico! Ma cosa succede se non (vuoi) sapere dove si trova l'ambito del chiamante (intendo in $scope.$parento in $scope.$parent.$parent, ecc.)? Ah, sì: passa un callback in params! :)
user2173353

@ user2173353 hai ragione; c'è un altro modo: $emitda bambino a genitore. Penso che sia il momento di aggiornare la mia risposta ..
Cherniv

È una buona idea chiamare l'ascoltatore globale dov'è da qualche parte, controllore figlio in questo caso? Non è antipattern e difficile da testare in seguito? Non dovremmo usare iniezioni o smth?
calmbird

@calmbird, ogni cosa deve essere usata con attenzione, questo è certo .. Alcuni preferiscono usare i servizi in casi simili. Ad ogni modo, ho aggiunto una versione più elegante (senza fastidiosi $parent)
Cherniv

9
Questo approccio è più pulito. Passa una richiamata al $broadcaste puoi eliminare del pingBacktutto.
più elegante

34

Lasciami suggerire un'altra soluzione:

var app = angular.module("myNoteApp", []);


app.controller("ParentCntl", function($scope) {
    $scope.obj = {};
});

app.controller("ChildCntl", function($scope) {
    $scope.obj.get = function() {
            return "LOL";    
    };
});

Meno codice e utilizzo dell'ereditarietà prototipica.

Plunk


Qualcuno potrebbe spiegare se questo approccio è migliore dell'approccio basato sugli eventi menzionato sopra, in quali casi e se lo è, perché? cosa succede se si dispone di una gerarchia multi lvl con controller figlio? funzionerebbe se obj.get fosse definito all'interno di un controller figlio all'interno di un controller figlio purché nessuno dei controller abbia lo stesso nome di funzione definito? Grazie in anticipo.
ZvKa

1
Vorrei correggere quello che ho detto che non è del tutto vero. Hai ragione in quello che hai detto: l'ereditarietà dell'ambito è applicata solo a una sola direzione .... figlio -> genitore. Quello che sta realmente accadendo qui è che se si definisce $ scope.get nell'ambito figlio, "get" verrà definito solo nell'ambito figlio (a volte indicato come definizione primitiva). Ma quando definisci $ scope.obj.get, l'ambito figlio cercherà obj che è definito sull'ambito padre, che è in cima alla gerarchia.
Canttouchit

3
Come funzionerà se il bambino ha il suo ambito isolato?
Dinesh

2
La domanda non faceva riferimento a un ambito isolato, tuttavia, se si dispone di un ambito isolato, è possibile utilizzare l'associazione dell'ambito isolato. Imo, la trasmissione è un po 'complicata.
Canttouchit

2
Non penso che questo esempio è possibile se è necessario accedere alla funzione del controller figlio dal genitore di controllo , non è modello. Questo esempio funziona solo a causa dell'associazione a due vie sul modello, che presumo continui a eseguire il polling fino a quando non $scope.obj.get()è una funzione valida.
danyim

11

Registrare la funzione del bambino sul genitore quando il bambino sta inizializzando. Ho usato la notazione "as" per chiarezza nel modello.

MODELLO

<div ng-controller="ParentCntl as p">
  <div ng-controller="ChildCntl as c" ng-init="p.init(c.get)"></div>
</div>

CONTROLLORI

...
function ParentCntl() {
  var p = this;
  p.init = function(fnToRegister) {
    p.childGet = fnToRegister;
  };
 // call p.childGet when you want
}

function ChildCntl() {
  var c = this;
  c.get = function() {
    return "LOL";    
  };
}

"Ma", dici, " ng-init non dovrebbe essere usato in questo modo !". Ebbene sì, ma

  1. quella documentazione non spiega perché no, e
  2. Non credo che gli autori della documentazione abbiano considerato TUTTI i possibili casi d'uso.

Dico che questo è un buon uso per questo. Se vuoi farmi un downvote, per favore commenta con i motivi! :)

Mi piace questo approccio perché mantiene i componenti più modulari. Le uniche associazioni sono nel modello e significa che

  • il Controller figlio non deve sapere nulla su quale oggetto aggiungere la sua funzione (come nella risposta di @ canttouchit)
  • il controllo genitore può essere utilizzato con qualsiasi altro controllo figlio che abbia una funzione get
  • non richiede la trasmissione, che diventerà molto brutta in una grande app a meno che tu non controlli strettamente lo spazio dei nomi degli eventi

Questo approccio si avvicina più da vicino all'idea di Tero di modularizzare con le direttive (si noti che nel suo esempio modularizzato,contestants è passato dalla direttiva genitore a quella "figlia" NEL MODELLO).

In effetti, un'altra soluzione potrebbe essere quella di considerare l'implementazione ChildCntldi una direttiva e utilizzare l' &associazione per registrare il initmetodo.


1
So che questo è un vecchio post, ma sembra una cattiva idea perché il genitore può mantenere un riferimento al bambino dopo che il bambino è stato distrutto.
Amy Blankenship

Questa è una soluzione molto più pulita rispetto alla trasmissione di eventi. Non perfetto, ma migliore. Anche se la pulizia è davvero un problema.
mcv

-1

Puoi creare un oggetto figlio.

var app = angular.module("myApp", []);


app.controller("ParentCntl", function($scope) {
    $scope.child= {};
    $scope.get = function(){
      return $scope.child.get(); // you can call it. it will return 'LOL'
    }
   // or  you can call it directly like $scope.child.get() once it loaded.
});

app.controller("ChildCntl", function($scope) {
    $scope.obj.get = function() {
            return "LOL";    
    };
});

Qui il bambino si sta dimostrando destinazione del metodo get.

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.