AngularJS accede al ámbito primario desde el controlador secundario

382

He configurado mis controladores usando data-ng-controller="xyzController as vm"

Tengo un escenario con controladores anidados padre / hijo. No tengo ningún problema para acceder a las propiedades primarias en el html anidado mediante el uso $parent.vm.property, pero no puedo entender cómo acceder a la propiedad primaria desde mi controlador secundario.

He intentado inyectar $ scope y luego usarlo $scope.$parent.vm.property, pero ¿esto no funciona?

¿Alguien puede ofrecer consejos?

zpydee
fuente

Respuestas:

620

Si su HTML es el siguiente, puede hacer algo como esto:

<div ng-controller="ParentCtrl">
    <div ng-controller="ChildCtrl">
    </div>
</div>

Luego puede acceder al ámbito principal de la siguiente manera

function ParentCtrl($scope) {
    $scope.cities = ["NY", "Amsterdam", "Barcelona"];
}

function ChildCtrl($scope) {
    $scope.parentcities = $scope.$parent.cities;
}

Si desea acceder a un controlador principal desde su vista, debe hacer algo como esto:

<div ng-controller="xyzController as vm">
   {{$parent.property}}
</div>

Ver jsFiddle: http://jsfiddle.net/2r728/

Actualizar

En realidad, desde que definió citiesen el controlador principal, su controlador secundario heredará todas las variables de alcance. Teóricamente no tienes que llamar $parent. El ejemplo anterior también se puede escribir de la siguiente manera:

function ParentCtrl($scope) {
    $scope.cities = ["NY","Amsterdam","Barcelona"];
}

function ChildCtrl($scope) {
    $scope.parentCities = $scope.cities;
}

Los documentos de AngularJS utilizan este enfoque, aquí puede leer más sobre el $scope.

Otra actualización

Creo que esta es una mejor respuesta al póster original.

HTML

<div ng-app ng-controller="ParentCtrl as pc">
    <div ng-controller="ChildCtrl as cc">
        <pre>{{cc.parentCities | json}}</pre>
        <pre>{{pc.cities | json}}</pre>
    </div>
</div>

JS

function ParentCtrl() {
    var vm = this;
    vm.cities = ["NY", "Amsterdam", "Barcelona"];
}

function ChildCtrl() {
    var vm = this;
    ParentCtrl.apply(vm, arguments); // Inherit parent control

    vm.parentCities = vm.cities;
}

Si utiliza el controller asmétodo, también puede acceder al ámbito principal de la siguiente manera

function ChildCtrl($scope) {
    var vm = this;
    vm.parentCities = $scope.pc.cities; // note pc is a reference to the "ParentCtrl as pc"
}

Como puede ver, hay muchas formas diferentes de acceder $scopes.

Violín actualizado

function ParentCtrl() {
    var vm = this;
    vm.cities = ["NY", "Amsterdam", "Barcelona"];
}
    
function ChildCtrl($scope) {
    var vm = this;
    ParentCtrl.apply(vm, arguments);
    
    vm.parentCitiesByScope = $scope.pc.cities;
    vm.parentCities = vm.cities;
}
    
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.20/angular.min.js"></script>
<div ng-app ng-controller="ParentCtrl as pc">
  <div ng-controller="ChildCtrl as cc">
    <pre>{{cc.parentCities | json}}</pre>
    <pre>{{cc.parentCitiesByScope | json }}</pre>
    <pre>{{pc.cities | json}}</pre>
  </div>
</div>

Dieterg
fuente
66
Creo que los dos problemas con su última actualización son 1. Heredar el ámbito primario puede tener conflictos potenciales de espacio de nombres, y 2. Requiere saber que el alias del controlador primario es 'pc'. Esto hace que la reutilización sea más difícil.
tchen
2
Usted definió los controladores como la función CtrlName (...) {} pero ¿cómo podemos lograr eso usando la nomenclatura angular? como: angular.module (MdlName) .controller (CtrlName, función (...) {});
Pedro Justo
1
¿Qué quieres decir? ¿Un controlador es solo una función? es decirangular.module('app').controller('ParentCtrl', ParentCtrl);
Dieterg
1
lo siento, tienes razon! En el método 'controlador como', usando '$ scope.pc.cities;' acceder a las ciudades de los padres me parece un 'paso atrás' porque si en childCtrl no tenemos la propiedad 'ciudades', accederá automáticamente a las ciudades de los padres. ¿No hay otra forma de resolver este problema?
Pedro Justo el
46

Acabo de comprobar

$scope.$parent.someProperty

funciona para mi.

y será

{{$parent.someProperty}}

para la vista

Stepan Suvorov
fuente
hmmm, no funciona para mi. Me pregunto si tiene que ver con el controlador como sintaxis vm.
zpydee
Si ha nombrado al controlador principal, entonces colocaría $ parent en la plantilla y tendría {{vm.someProperty}}
solbs
8

Cuando usa la assintaxis, como ParentController as parentCtrl, para definir un controlador, para acceder a la variable de alcance principal en el controlador secundario, use lo siguiente:

var id = $scope.parentCtrl.id;

Donde parentCtrles el nombre del controlador principal que usa la assintaxis y ides una variable definida en el mismo controlador.

Rubi Saini
fuente
2

Algunas veces es posible que deba actualizar las propiedades principales directamente dentro del ámbito secundario. por ejemplo, necesita guardar una fecha y hora de control primario después de los cambios realizados por un controlador secundario. por ejemplo, código en JSFiddle

HTML

<div ng-app>
<div ng-controller="Parent">
    event.date = {{event.date}} <br/>
    event.time = {{event.time}} <br/>
    <div ng-controller="Child">
        event.date = {{event.date}}<br/>
        event.time = {{event.time}}<br/>
        <br>
        event.date: <input ng-model='event.date'><br>
        event.time: <input ng-model='event.time'><br>
    </div>
</div>

JS

    function Parent($scope) {
       $scope.event = {
        date: '2014/01/1',
        time: '10:01 AM'
       }
    }

    function Child($scope) {

    }
Gayan Pathirage
fuente
1

También puede eludir la herencia del alcance y almacenar cosas en el alcance "global".

Si tiene un controlador principal en su aplicación que envuelve todos los demás controladores, puede instalar un "enlace" en el ámbito global:

function RootCtrl($scope) {
    $scope.root = $scope;
}

Luego, en cualquier controlador secundario, puede acceder al ámbito "global" con $scope.root. Todo lo que establezca aquí será visible globalmente.

Ejemplo:

function RootCtrl($scope) {
  $scope.root = $scope;
}

function ChildCtrl($scope) {
  $scope.setValue = function() {
    $scope.root.someGlobalVar = 'someVal';
  }
}

function OtherChildCtrl($scope) {
}
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.23/angular.min.js"></script>

<div ng-app ng-controller="RootCtrl">
  
  <p ng-controller="ChildCtrl">
    <button ng-click="setValue()">Set someGlobalVar</button>
  </p>
  
  <p ng-controller="OtherChildCtrl">
    someGlobalVar value: {{someGlobalVar}}
  </p>

</div>

rustyx
fuente
Esto realmente no escala. Es como definir variables globales que deben ser únicas en muchos archivos / contextos.
ZachB
No veo un problema de escala, pero llamar a la variable de alcance algo diferente a 'raíz' podría ser mejor en otros contextos
Nico Westerdale
También es un poco molesto tener un estado que se mantenga por más tiempo que la vida útil del subárbol de componentes que lo necesita.
Roboprog
0

Creo que tuve un dilema similar recientemente

function parentCtrl() {
   var pc = this; // pc stands for parent control
   pc.foobar = 'SomeVal';
}

function childCtrl($scope) {

   // now how do I get the parent control 'foobar' variable?
   // I used $scope.$parent

   var parentFoobarVariableValue = $scope.$parent.pc.foobar;

   // that did it
}

Mi configuración fue un poco diferente, pero lo mismo probablemente debería funcionar

Benjamin Thvedt
fuente
0

Desde un componente secundario puede acceder a las propiedades y métodos del componente primario con 'require'. Aquí hay un ejemplo:

Padre:

.component('myParent', mymodule.MyParentComponent)
...
controllerAs: 'vm',
...
var vm = this;
vm.parentProperty = 'hello from parent';

Niño:

require: {
    myParentCtrl: '^myParent'
},
controllerAs: 'vm',
...
var vm = this;
vm.myParentCtrl.parentProperty = 'hello from child';
Donato Szilagyi
fuente
0

Súper fácil y funciona, pero no estoy seguro de por qué ...

angular.module('testing')
  .directive('details', function () {
        return {
              templateUrl: 'components/details.template.html',
              restrict: 'E',                 
              controller: function ($scope) {
                    $scope.details=$scope.details;  <=== can see the parent details doing this                     
              }
        };
  });
John Peters
fuente
-1

Tal vez esto sea poco convincente, pero también puede señalarlos a ambos en algún objeto externo:

var cities = [];

function ParentCtrl() {
    var vm = this;
    vm.cities = cities;
    vm.cities[0] = 'Oakland';
}

function ChildCtrl($scope) {
    var vm = this;
    vm.cities = cities;
}

El beneficio aquí es que las ediciones en ChildCtrl ahora se propagan de nuevo a los datos en el padre.

Peter Hollingsworth
fuente
Introducir variables globales es peligroso.
Demencial