angularJS: Cómo llamar a la función de alcance secundario en el alcance principal

95

¿Cómo se puede llamar a un método definido en el ámbito secundario desde su ámbito principal?

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

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

http://jsfiddle.net/wUPdW/

9azul
fuente
2
Lo mejor es definir un servicio, inyectar ese servicio en ambos controladores. O usa el raicescopio.
tymeJV

Respuestas:

145

Puede usar $broadcastde padre a hijo:

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";    
    }
}

Violín de trabajo: http://jsfiddle.net/wUPdW/2/

ACTUALIZACIÓN : Hay otra versión, menos acoplada y más comprobable:

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";    
    }
}

Violín: http://jsfiddle.net/uypo360u/

Cherniv
fuente
¡Eso es muy bonito! Pero, ¿qué pasa si no (quiere) saber dónde está el alcance de la persona que llama (me refiero en $scope.$parento en $scope.$parent.$parent, etc.)? Ah, sí: ¡pasa una devolución de llamada en params! :)
user2173353
@ user2173353 tienes mucha razón; hay una forma más: $emitde un niño a un padre. Creo que es el momento de actualizar mi respuesta ..
Cherniv
¿Es una buena idea llamar al oyente global dónde está algún lugar, controlador secundario en este caso? ¿No es antipatrón y difícil de probar más tarde? ¿No deberíamos usar inyecciones o algo?
calmbird
@calmbird, cada cosa debe usarse con cuidado, eso es seguro .. Algunos prefieren usar servicios en casos similares. De todos modos, agregué una versión más elegante (sin molestar $parent)
Cherniv
9
Este enfoque es más limpio. Pase una devolución de llamada al $broadcasty puede eliminar el por pingBackcompleto.
poshest
34

Permítanme sugerirles otra solución:

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


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

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

Menos código y usando herencia prototípica.

Ruido sordo

Canttouchit
fuente
¿Alguien podría explicar si este enfoque es mejor que el enfoque basado en eventos mencionado anteriormente en qué casos y si lo es, por qué? ¿Qué pasa si tiene una jerarquía de niveles múltiples con controladores secundarios? ¿Funcionaría esto si obj.get se define dentro de un controlador secundario dentro de un controlador secundario siempre que ninguno de los controladores tenga definido el mismo nombre de función? Gracias de antemano.
ZvKa
1
Permítanme corregir lo que dije que no es del todo cierto. Tienes razón en lo que dijiste: la herencia del alcance solo se aplica en una sola dirección .... hijo -> padre. Lo que realmente está sucediendo aquí es que si define $ scope.get en el alcance secundario, 'get' se definirá solo en el alcance secundario (a veces denominado definición primitiva). Pero cuando define $ scope.obj.get, entonces el alcance secundario buscará obj que está definido en el alcance principal, que está en la jerarquía.
Canttouchit
3
¿Cómo funcionará esto si el niño tiene su propio alcance aislado?
Dinesh
2
La pregunta no se refería a un alcance aislado. Sin embargo, si tiene un alcance aislado, puede usar el enlace de alcance aislado. Imo, la transmisión es un poco complicada.
Canttouchit
2
No creo que este ejemplo sea posible si necesita acceder a la función del controlador secundario desde el controlador principal , no desde la plantilla. Este ejemplo solo funciona debido al enlace bidireccional en la plantilla, que supongo que sigue sondeando hasta que $scope.obj.get()es una función válida.
danyim
11

Registre la función del niño en el padre cuando el niño se está inicializando. Usé la notación "como" para mayor claridad en la plantilla.

MODELO

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

CONTROLADORES

...
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";    
  };
}

"Pero", dices, "¡ ng-init no se supone que se use de esta manera !". Bueno si pero

  1. que la documentación no explica por qué no, y
  2. No creo que los autores de la documentación hayan considerado TODOS los posibles casos de uso.

Digo que este es un buen uso para él. Si quieres rechazarme, ¡comenta con las razones! :)

Me gusta este enfoque porque mantiene los componentes más modulares. Los únicos enlaces están en la plantilla y significa que

  • el controlador secundario no tiene que saber nada sobre a qué objeto agregar su función (como en la respuesta de @ canttouchit)
  • el control principal se puede usar con cualquier otro control secundario que tenga una función de obtención
  • no requiere transmisión, lo que se pondrá muy feo en una gran aplicación a menos que controle estrictamente el espacio de nombres del evento

Este enfoque se acerca más a la idea de Tero de modularizar con directivas (nótese que en su ejemplo modularizado, contestantsse pasa de directiva padre a "hijo" EN LA PLANTILLA).

De hecho, otra solución podría ser considerar implementar el ChildCntlcomo una directiva y usar el &enlace para registrar el initmétodo.

más elegante
fuente
1
Sé que esta es una publicación antigua, pero parece una mala idea porque el padre puede mantener una referencia al niño después de que el niño sea destruido.
Amy Blankenship
Esta es una solución mucho más limpia que la transmisión de eventos. No perfecto, pero mejor. Aunque la limpieza es un problema.
mcv
-1

Puedes hacer un objeto secundario.

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";    
    };
});

Aquí el niño está probando el destino del método get.

Ramu Agrawal
fuente