Dlaczego nie można uzyskać dostępu do $ rootScope w szablonie dyrektywy z izolowanym zakresem?

81

Wydaje się, że w przypadku zakresu izolowanego szablon dyrektywy nie ma dostępu do zmiennej kontrolera ('Ctrl') $ rootScope, która jednak pojawia się w kontrolerze dyrektywy. Rozumiem, dlaczego zmienna kontrolera („Ctrl”) $ scope nie jest widoczna w zakresie izolowanym.

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

Dostęp do zmiennej uzyskuje się bez zakresu izolowanego - co można zobaczyć, komentując wiersz zakresu izolowanego:

        // scope: {},
camden_kid
źródło
Czy próbowałeś wstrzyknąć $ rootScope do dyrektywy ... directive('myTemplate', function($rootScope) { ... })?
Marc Kline
@MarcKline Właśnie tego próbowałem i bez powodzenia.
camden_kid
1
Czy istnieje powód, dla którego korzystanie z usługi nie jest wystarczające do Twoich celów?
Marc Kline
1
@Kalyan - Osobiście uważam, że $ rootScope powinno być używane tylko do zdarzeń, a Factory do przekazywania danych do dyrektyw. Jednym z powodów jest to, że używanie $ rootScope jest jak używanie zmiennych globalnych, które nie są idealne. Ponadto fabryka może być dobrze zdefiniowanym opakowaniem, które można rozszerzyć w późniejszym terminie.
camden_kid

Odpowiedzi:

164

Możesz wypróbować ten sposób za pomocą $root.blah

Kodeks roboczy

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
źródło
6
Oznaczam to jako odpowiedź, ponieważ „rozwiązuje” to, co chciałem osiągnąć (nie wiedziałem o „$ root” ani że można go użyć w ten sposób). Sugerowałbym jednak , że odpowiedź Marka Kline'a jest generalnie najlepszym rozwiązaniem.
camden_kid
5
niesamowity! warto wiedzieć, że $ rootScope zmienia się w $ root na widoki, wielkie dzięki!
Cris R
Jest to idealne rozwiązanie, ponieważ potrzebowałem dostępu do funkcji zdefiniowanej w rootScope
Alfredo A.
Dobry. Tutaj też działa. Czy możesz wyjaśnić, dlaczego $ root zamiast $ rootScope? Dodałem również $ rootScope, ale jest on niezdefiniowany podczas wywołania funkcji.
Unknown_Coder
32

Ogólnie rzecz biorąc, należy unikać używania $rootScopedo przechowywania wartości, które należy udostępniać między kontrolerami i dyrektywami. To jak używanie globali w JS. Zamiast tego skorzystaj z usługi:

Stała (lub wartość ... użycie jest podobne):

.constant('blah', 'blah')

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

Fabryka (lub usługa lub dostawca):

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

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

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

    return blah;
})

Oto widelec twojego Fiddle pokazujący, jak możesz użyć jednego z nich

Marc Kline
źródło
3
+1 Bardzo dziękuję za to i za wskazanie mi właściwego kierunku do tego, co próbuję osiągnąć. Myślę, że NidhishKrishnan należy zaakceptować jako „odpowiedź” z powodu podanego w moim komentarzu.
camden_kid
1
+1 dla przypadku użycia stałych, ponieważ są one rzadko używane. Uwaga dotycząca niekorzystania z $ rootScope była również wskazówką dla profesjonalistów.
Farzad YZ
23

1) Z powodu izolowanego zakresu $scopew kontrolerze Ctrl iw dyrektywie kontroler nie odwołuje się do tego samego zakresu - powiedzmy, że mamy zakres1 w Ctrl, a zakres2 w dyrektywie.

2) Ze względu na izolowany zakres zakresu2 nie dziedziczą prototypowo z $rootScope; więc jeśli zdefiniujesz, $rootScope.blahże nie ma szans, możesz to zobaczyć w zakresie 2 .

3) To, do czego możesz uzyskać dostęp w swoim szablonie dyrektywy, to scope2

Jeśli podsumuję, oto schemat dziedziczenia

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


$rootScope.blah
> "Hello"
scope1.blah
> "Hello"
scope2.blah
> undefined
Thomas Guillory
źródło
1
Bardzo pomocne, ale obejście nidhishkrishnan działa, jeśli w jakiś sposób konieczne jest użycie wartości rootScope. To niezły hack.
Marc Kline
1
Cóż, powiedziałeś logikę, aby odpowiedzieć, dlaczego nie mogę używać zmiennych $ rootScope w html (bez $ root.), Ale kiedy używam wtyczki Batarang, aby zobaczyć $ scopes, wyraźnie widzę, że $ rootScope jest nadrzędnym zakresem $ wszystkich innych (w tym zakresem izolowanym w dyrektywach). Ponadto definicja z oficjalnej dokumentacji kątowej mówi: „Każda aplikacja ma jeden zakres główny. Wszystkie inne zakresy są zakresami podrzędnymi zakresu głównego” ( docs.angularjs.org/api/ng/service/$rootScope )
IsraGab
1

Wiem, że to stare pytanie. Ale nie zadowoliło mnie to, dlaczego izolowany zakres nie może uzyskać dostępu do właściwości w $ rootscope.

Więc pogrzebałem w kanciastej libi i znalazłem -

$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();
  }

Jest to funkcja wywoływana przez angular za każdym razem, gdy tworzony jest nowy zakres. Tutaj jest jasne, że żaden izolowany zakres nie dziedziczy prototypowo zakresu głównego. raczej tylko zakres główny jest dodawany jako właściwość „$ root” w nowym zakresie. Dlatego możemy uzyskać dostęp do właściwości rootscope tylko z właściwości $ root w nowym izolowanym zakresie.

Adarsh ​​Sharma
źródło