¿Por qué no se puede acceder a $ rootScope en la plantilla de una directiva con alcance aislado?

81

Con alcance aislado, la plantilla de la directiva no parece poder acceder al controlador ('Ctrl') $ rootScope variable que, sin embargo, aparece en el controlador de la directiva. Entiendo por qué la variable $ scope del controlador ('Ctrl') no es visible en el ámbito aislado.

HTML:

<div ng-app="app">
    <div ng-controller="Ctrl">
        <my-template></my-template>
    </div>

    <script type="text/ng-template" id="my-template.html">
        <label ng-click="test(blah)">Click</label>
    </script>
</div>

JavaScript:

angular.module('app', [])
    .controller('Ctrl', function Ctrl1($scope,  $rootScope) {
        $rootScope.blah = 'Hello';
        $scope.yah = 'World'
    })
    .directive('myTemplate', function() {
        return {
            restrict: 'E',
            templateUrl: 'my-template.html',
            scope: {},
            controller: ["$scope", "$rootScope", function($scope, $rootScope) {
                console.log($rootScope.blah);
                console.log($scope.yah);,

                $scope.test = function(arg) {
                    console.log(arg);
                }
            }]
        };
    });

JSFiddle

Se accede a la variable sin alcance aislado, como se puede ver al comentar la línea de alcance aislado:

        // scope: {},
camden_kid
fuente
¿Ha intentado inyectar $ rootScope en la directiva ... directive('myTemplate', function($rootScope) { ... })?
Marc Kline
@MarcKline Lo intenté y no tuve suerte.
camden_kid
1
¿Existe alguna razón por la que usar un servicio no sea suficiente para sus propósitos?
Marc Kline
1
@Kalyan: personalmente creo que $ rootScope solo debe usarse para eventos y Factory para pasar datos a directivas. Una razón es que usar $ rootScope es como usar variables globales, lo cual no es ideal. Además, una fábrica puede ser un contenedor bien definido que se puede extender en una fecha posterior.
camden_kid

Respuestas:

164

Puedes probar esta forma usando $root.blah

Código de trabajo

html

 <label ng-click="test($root.blah)">Click</label>

javascript

  angular.module('app', [])
    .controller('Ctrl', function Ctrl1($scope,  $rootScope) {
        $rootScope.blah = 'Hello';
        $scope.yah = 'World'
    })
    .directive('myTemplate', function() {
        return {
            restrict: 'E',
            templateUrl: 'my-template.html',
            scope: {},
            controller: ["$scope", "$rootScope", function($scope, $rootScope) {
                console.log($rootScope.blah);
                console.log($scope.yah);

                $scope.test = function(arg) {
                    console.log(arg);
                }
            }]
        };
    });
Nidhish Krishnan
fuente
6
Estoy marcando esto como la respuesta ya que 'resuelve' lo que quería lograr (no sabía de '$ root' o que podría usarse así). Sin embargo , sugeriría que la respuesta de Mark Kline es generalmente la mejor solución.
camden_kid
5
¡asombroso! Es muy útil saber que $ rootScope cambia a $ root en las vistas, ¡muchas gracias!
Cris R
Esto es perfecto ya que lo que tenía que hacer era acceder a una función definida en rootScope
Alfredo A.
Buena. También está funcionando aquí. ¿Puede explicar por qué $ root en lugar de $ rootScope? También he inyectado $ rootScope pero no está definido mientras se llama a la función.
Unknown_Coder
32

Generalmente, debe evitar usar $rootScopepara almacenar valores que necesita compartir entre controladores y directivas. Es como usar globales en JS. Utilice un servicio en su lugar:

Una constante (o valor ... el uso es similar):

.constant('blah', 'blah')

https://docs.angularjs.org/api/ng/type/angular.Module

Una fábrica (o servicio o proveedor):

.factory('BlahFactory', function() {
    var blah = {
        value: 'blah'
    };

    blah.setValue = function(val) {
      this.value = val;
    };

    blah.getValue = function() {
        return this.value;
    };

    return blah;
})

Aquí hay una bifurcación de su Fiddle que demuestra cómo puede usar

Marc Kline
fuente
3
+1 Muchas gracias por esto y por señalarme en la dirección correcta para lo que estoy tratando de lograr. Creo que NidhishKrishnan debería aceptarse como la "respuesta" por la razón que se indica en mi comentario.
camden_kid
1
+1 para el caso de uso de constantes, ya que son raras de usar. Además, la nota sobre no usar $ rootScope fue un consejo profesional.
Farzad YZ
23

1) Debido al alcance aislado $scopeen su controlador, Ctrl y en la directiva, el controlador no se refieren al mismo alcance; digamos que tenemos alcance1 en Ctrl y alcance2 en la directiva.

2) Debido al alcance aislado, scope2 no hereda de manera prototípica $rootScope; por lo que si define $rootScope.blahno hay posibilidad de que pueda verlo en scope2 .

3) A lo que puede acceder en su plantilla de directiva es scope2

Si lo resumo, aquí está el esquema de herencia

    _______|______
    |            |
    V            V
$rootScope     scope2
    |
    V
  scope1


$rootScope.blah
> "Hello"
scope1.blah
> "Hello"
scope2.blah
> undefined
Thomas Guillory
fuente
1
Muy útil, pero la solución alternativa de nidhishkrishnan funciona si de alguna manera es necesario usar valores de rootScope. Es un buen truco.
Marc Kline
1
Bueno, lo que dijiste es lógica para responder por qué no puedo usar las variables $ rootScope en html (sin $ root.), Pero cuando uso el complemento Batarang para ver los $ scopes, puedo ver claramente que $ rootScope es el alcance $ padre de todos los demás (incluido el alcance aislado en las directivas). Además, la definición de los documentos oficiales angulares decía: "Cada aplicación tiene un único alcance raíz. Todos los demás alcances son alcances descendientes del alcance raíz" ( docs.angularjs.org/api/ng/service/$rootScope )
IsraGab
1

Sé que esta es una vieja pregunta. Pero no satisfizo mi pregunta sobre por qué el alcance aislado no podrá acceder a las propiedades en $ rootscope.

Así que cavé en la biblioteca angular y encontré:

$new: function(isolate) {
  var ChildScope,
      child;

  if (isolate) {
    child = new Scope();
    child.$root = this.$root;
    child.$$asyncQueue = this.$$asyncQueue;
    child.$$postDigestQueue = this.$$postDigestQueue;
  } else {

    if (!this.$$childScopeClass) {
      this.$$childScopeClass = function() {
        // blah blah...
      };
      this.$$childScopeClass.prototype = this;
    }
    child = new this.$$childScopeClass();
  }

Esta es la función llamada por angular cada vez que se crea un nuevo alcance. Aquí está claro que cualquier endoscopio aislado no hereda prototípicamente el radioscopio. más bien, solo se agrega el alcance raíz como una propiedad '$ root' en el nuevo alcance. Por lo tanto, solo podemos acceder a las propiedades de rootscope desde la propiedad $ root en el nuevo alcance aislado.

Adarsh ​​Sharma
fuente