AngularJS - Acceso al ámbito secundario

110

Si tengo los siguientes controladores:

function parent($scope, service) {
    $scope.a = 'foo';

    $scope.save = function() {
        service.save({
            a:  $scope.a,
            b:  $scope.b
        });
    }
}

function child($scope) {
    $scope.b = 'bar';
}

¿Cuál es la forma correcta de dejar salir la parentlectura ? Si es necesario definir en , ¿no lo haría semánticamente incorrecto asumiendo que es una propiedad que describe algo relacionado y no ?bchildbparentbchildparent

Actualización: Pensando más en ello, si más de un niño lo tuviera b, se crearía un conflicto por parentel cual brecuperar. Mi pregunta sigue siendo, ¿cuál es la forma correcta de acceder bdesde parent?

Aziz Alfoudari
fuente
1
Además, asegúrese de leer esto: stackoverflow.com/questions/14049480/… Una descripción general muy buena y detallada de los alcances y la herencia en angularJS.
Martijn

Respuestas:

162

Los ámbitos en AngularJS utilizan la herencia prototípica, al buscar una propiedad en un ámbito secundario, el intérprete buscará la cadena del prototipo a partir del elemento secundario y continuará hasta los padres hasta que encuentre la propiedad, no al revés.

Consulte los comentarios de Vojta sobre el problema https://groups.google.com/d/msg/angular/LDNz_TQQiNE/ygYrSvdI0A0J

En pocas palabras: no puede acceder a los ámbitos secundarios desde un ámbito principal.

Tus soluciones:

  1. Defina propiedades en los padres y acceda a ellas desde los niños (lea el enlace de arriba)
  2. Utilizar un servicio para compartir el estado
  3. Pasar datos a través de eventos. $emitenvía eventos hacia arriba a los padres hasta el alcance raíz y $broadcastenvía eventos hacia abajo. Esto podría ayudarlo a mantener las cosas semánticamente correctas.
jaime
fuente
8
Además, la diferencia entre $ emit y $ broadcast es donde $ emit es cancelable y $ broadcast no.
Azri Jamil
Un lugar en el que probablemente desee obtener el alcance secundario es en las directivas de prueba unitaria. Si tiene una directiva transcluida, el alcance es un elemento secundario del alcance utilizado para compilar el elemento. Obtener acceso al alcance de la directiva compilada para las pruebas es un desafío.
pmc
Gracias. Otra opción podría ser localStoragecon una dependencia como ngStorage.
Akash
81

Si bien la respuesta de jm-es la mejor manera de manejar este caso, para referencia futura es posible acceder a los ámbitos secundarios utilizando los miembros $$ childHead, $$ childTail, $$ nextSibling y $$ prevSibling de un alcance. Estos no están documentados, por lo que pueden cambiar sin previo aviso, pero están ahí si realmente necesita atravesar los ámbitos.

// get $$childHead first and then iterate that scope's $$nextSiblings
for(var cs = scope.$$childHead; cs; cs = cs.$$nextSibling) {
    // cs is child scope
}

Violín

Jussi Kosunen
fuente
49

Puedes probar esto:

$scope.child = {} //declare it in parent controller (scope)

luego en el controlador secundario (alcance) agregue:

var parentScope = $scope.$parent;
parentScope.child = $scope;

Ahora el padre tiene acceso al alcance del niño.

Farzin Mo
fuente
1
Muy limpio y sencillo. Para agregar a esto, simplemente tenga en cuenta que no hay vinculación, por lo que cuando lo declara como arriba, parentScope.child está sosteniendo el alcance del niño en ese momento. Para solucionar esto, puede agregar $ scope. $ Watch (function () {parentScope.child = $ scope}); para que, después de cada resumen, lleve el nuevo alcance al padre. En el extremo principal, si está utilizando cualquiera de los ámbitos secundarios para las imágenes en el html, deberá agregar un reloj en su variable secundaria y decirle que actualice el alcance de la siguiente manera: $ scope. $ Watch ('child', function ( ) {$ alcance. $ evalAsync ();}); . ¡Espero que esto le ahorre a alguien algo de tiempo!
IfTrue
2
@IfTrue Creo que $ watch () no es necesario. Estos son objetos y pasan por referencia.
Swanidhi
2
@Swanidhi = Sé lo que quieres decir y también lo pensé, pero no funcionó cuando estaba experimentando en el momento de escribir el comentario. Por lo que vale, cambié de hacer esto a emitir / transmitir.
IfTrue
1
¿Cómo hacer esto en Type Script?
ATHER
¡¡La mejor respuesta para mí !! El mejor ejemplo de objetos tratados como referencia en javascript
Vikas Gautam
4

Una posible solución es inyectar el controlador secundario en el controlador principal mediante una función de inicio.

Posible implementación:

<div ng-controller="ParentController as parentCtrl">
   ...

    <div ng-controller="ChildController as childCtrl" 
         ng-init="ChildCtrl.init()">
       ...
    </div>
</div>

¿Dónde ChildControllertienes:

app.controller('ChildController',
    ['$scope', '$rootScope', function ($scope, $rootScope) {
    this.init = function() {
         $scope.parentCtrl.childCtrl = $scope.childCtrl;
         $scope.childCtrl.test = 'aaaa';
    };

}])

Entonces ahora en el ParentControllerpuedes usar:

app.controller('ParentController',
    ['$scope', '$rootScope', 'service', function ($scope, $rootScope, service) {

    this.save = function() {
        service.save({
            a:  $scope.parentCtrl.ChildCtrl.test
        });
     };

}])

Importante:
Para que funcione correctamente, debe usar la directiva ng-controllery cambiar el nombre de cada controlador ascomo lo hice en el html, por ejemplo.

Consejos:
Utilice el plugin ng-inspector de Chrome durante el proceso. Te ayudará a entender el árbol.

borracciaBlu
fuente
3

Usando $ emit y $ broadcast , (como lo menciona walv en los comentarios anteriores)

Para disparar un evento hacia arriba (de niño a padre)

$scope.$emit('myTestEvent', 'Data to send');

Para disparar un evento hacia abajo (de padre a hijo)

$scope.$broadcast('myTestEvent', {
  someProp: 'Sending you some data'
});

y finalmente escuchar

$scope.$on('myTestEvent', function (event, data) {
  console.log(data);
});

Para más detalles: - https://toddmotto.com/all-about-angulars-emit-broadcast-on-publish-subscribing/

Disfruta :)

Anjana Silva
fuente
1

Sí, podemos asignar variables del controlador secundario a las variables del controlador principal. Esta es una forma posible:

Descripción general: el objetivo principal del código, a continuación, es asignar $ scope.variable del controlador secundario al $ scope.assign del controlador principal

Explicación: Hay dos controladores. En el html, observe que el controlador principal incluye el controlador secundario. Eso significa que el controlador principal se ejecutará antes que el controlador secundario. Entonces, primero se definirá setValue () y luego el control irá al controlador secundario. $ scope.variable se asignará como "hijo". Luego, este alcance secundario se pasará como un argumento a la función del controlador principal, donde $ scope.assign obtendrá el valor como "secundario"

<!DOCTYPE html>
<html>
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.6.4/angular.min.js"></script>
<script type="text/javascript">
    var app = angular.module('myApp',[]);

    app.controller('child',function($scope){
        $scope.variable = "child";
        $scope.$parent.setValue($scope);
    });

    app.controller('parent',function($scope){
        $scope.setValue = function(childscope) {
            $scope.assign = childscope.variable;
        }
    });

</script>
<body ng-app="myApp">
 <div ng-controller="parent">
    <p>this is parent: {{assign}}</p>
    <div ng-controller="child">
        <p>this is {{variable}}</p>
    </div>
 </div>
</body>
</html>
rupali317
fuente