Zmienne globalne w AngularJS

348

Mam problem z inicjowaniem zmiennej w zakresie w kontrolerze. Następnie zmienia się w innym kontrolerze, gdy użytkownik loguje się. Ta zmienna służy do kontrolowania takich rzeczy, jak pasek nawigacyjny i ogranicza dostęp do części witryny w zależności od typu użytkownika, dlatego ważne jest, aby zachował swoją wartość. Problem polega na tym, że kontroler, który ją inicjuje, zostaje ponownie wywołany przez ujęcie kątowe, a następnie resetuje zmienną do wartości początkowej.

Zakładam, że to nie jest poprawny sposób deklarowania i inicjowania zmiennych globalnych, no cóż, to nie jest naprawdę globalny, więc moje pytanie brzmi: jaki jest prawidłowy sposób i czy są jakieś dobre przykłady wokół tej pracy z obecną wersją angulara?

Żarówka 1
źródło
12
Ponieważ jest to wynik Google nr 1: możesz teraz używać app.constant () i app.value () do tworzenia stałych i zmiennych dla całej aplikacji. Więcej tutaj: bit.ly/1P51PED
Mroz

Odpowiedzi:

491

Masz zasadniczo 2 opcje dla zmiennych „globalnych”:

$rootScopejest rodzicem wszystkich zakresów, więc ujawnione tam wartości będą widoczne we wszystkich szablonach i kontrolerach. Korzystanie z $rootScopejest bardzo łatwe, ponieważ możesz po prostu wstrzyknąć go do dowolnego kontrolera i zmienić wartości w tym zakresie. Może to być wygodne, ale ma wszystkie problemy związane ze zmiennymi globalnymi .

Usługi są singletonami, które można wstrzykiwać do dowolnego kontrolera i ujawniać ich wartości w zakresie kontrolera. Usługi, będąc singletonami, są nadal „globalne”, ale masz znacznie lepszą kontrolę nad tym, gdzie są one używane i narażone.

Korzystanie z usług jest nieco bardziej złożone, ale nie aż tak bardzo, oto przykład:

var myApp = angular.module('myApp',[]);
myApp.factory('UserService', function() {
  return {
      name : 'anonymous'
  };
});

a następnie w kontrolerze:

function MyCtrl($scope, UserService) {
    $scope.name = UserService.name;
}

Oto działający jsFiddle: http://jsfiddle.net/pkozlowski_opensource/BRWPM/2/

pkozlowski.opensource
źródło
141
Z Angular FAQ : I odwrotnie, nie twórz usługi, której jedynym celem w życiu jest przechowywanie i zwracanie bitów danych.
Jakob Stoeck
7
Używam nasion Express + Angular firmy Btford . Jeśli ustawię zmienną w $ rootScope w Controller1 say i przejdę do innego adresu URL (obsługiwanego przez Controller2 say), mogę uzyskać dostęp do tej zmiennej. Jeśli jednak odświeżę stronę w Controller2, zmienna nie będzie już dostępna w $ rootScope. Jeśli zapisuję dane użytkownika podczas logowania, jak mogę się upewnić, że dane te są dostępne w innym miejscu, nawet podczas odświeżania strony?
Craig Myles,
19
@JakobStoeck Połącz to z tą odpowiedzią i możesz pozostawić dane w rootScope. Nie sądzę też, żeby to było właściwe. Jaki jest kątowy sposób przechowywania i zwracania globalnie używanych fragmentów danych?
user2483724,
11
Ciekawe, jakie są wady usługi służącej do przechowywania wartości. Izolowany, wstrzykiwany tylko tam, gdzie jest potrzebny i łatwo testowalny. Co jest wadą?
Brian
12
o Boże, dlaczego wszyscy boją się realnych zmiennych globalnych, jeśli wiesz, z czego będzie się składać Twoja aplikacja, po prostu stwórz prawdziwą globalną zmienną js zamiast nadmiernie komplikujących rzeczy
Max Yari
93

Jeśli chcesz tylko zapisać wartość, zgodnie z dokumentacją Angular dotyczącą dostawców , powinieneś użyć receptury Value:

var myApp = angular.module('myApp', []);
myApp.value('clientId', 'a12345654321x');

Następnie użyj go w kontrolerze takim jak ten:

myApp.controller('DemoController', ['clientId', function DemoController(clientId) {
    this.clientId = clientId;
}]);

To samo można osiągnąć za pomocą dostawcy, fabryki lub usługi, ponieważ są one „tylko składniowym cukrem na podstawie przepisu dostawcy”, ale użycie Value osiągnie to, co chcesz, przy minimalnej składni.

Inną opcją jest użycie $rootScope, ale tak naprawdę nie jest to opcja, ponieważ nie powinieneś jej używać z tych samych powodów, dla których nie powinieneś używać zmiennych globalnych w innych językach. To zaleca się stosować oszczędnie.

Ponieważ wszystkie zakresy dziedziczą po $rootScope, jeśli masz zmienną $rootScope.datai ktoś zapomina, że datajest już zdefiniowany i tworzy $scope.dataw zasięgu lokalnym, napotkasz problemy.


Jeśli chcesz zmodyfikować tę wartość i zachować ją na wszystkich kontrolerach, użyj obiektu i zmodyfikuj właściwości, pamiętając, że JavaScript jest przekazywany przez „kopię odwołania” :

myApp.value('clientId', { value: 'a12345654321x' });
myApp.controller('DemoController', ['clientId', function DemoController(clientId) {
    this.clientId = clientId;
    this.change = function(value) {
        clientId.value = 'something else';
    }
}];

Przykład JSFiddle

Dean Or
źródło
1
Kiedy zmieniłem wartość w jednym widoku, nowa wartość nie jest trwała. Dlaczego? Czy możesz zaktualizować swoją odpowiedź i zobaczyć, jak clientIdmożesz ją zaktualizować?
Blaise
@DeanOr Czy można zachować zaktualizowaną wartość między odświeżeniami strony? Wartość zostanie ponownie zainicjowana, jeśli odświeżę stronę.
Nabarun,
Będziesz musiał użyć do tego czegoś innego, takiego jak plik cookie, pamięć lokalna lub baza danych.
Dean Or
1
Najbardziej podoba mi się ten. Mogę teraz modyfikować przez proces kompilacji dla
deweloperów
1
Jest prosty artykuł wyjaśniający użycie stałych i wartości podobnych do zmiennych globalnych: ilikekillnerds.com/2014/11/…
RushabhG
36

Przykład „zmiennych globalnych” AngularJS przy użyciu $rootScope:

Kontroler 1 ustawia zmienną globalną:

function MyCtrl1($scope, $rootScope) {
    $rootScope.name = 'anonymous'; 
}

Kontroler 2 odczytuje zmienną globalną:

function MyCtrl2($scope, $rootScope) {
    $scope.name2 = $rootScope.name; 
}

Oto działający jsFiddle: http://jsfiddle.net/natefriedman/3XT3F/1/

NateFriedman
źródło
2
Nie działa to w widokach izolowanych dyrektyw zakresu. Zobacz jsfiddle.net/3XT3F/7 . Ale możesz użyć $ root do obejścia tego. Zobacz jsfiddle.net/3XT3F/10 . Dzięki @natefaubion za zwrócenie na to uwagi
Tony Lâmpada,
2
podczas odświeżania wartość $ rootScope byłaby pusta.
xyonme,
zakodowanie na stałe $ rootScope.name równa się pewnej wartości .. w rzeczywistości musimy go przeczytać i tam ustawić.
25

W celu dodania kolejnego pomysłu do puli wiki, ale co z AngularJS valuei constantmodułami? Dopiero zaczynam ich używać, ale wydaje mi się, że są to prawdopodobnie najlepsze opcje tutaj.

Uwaga: w chwili pisania tego tekstu, Angular 1.3.7 jest najnowszą stabilną wersją, myślę, że zostały one dodane w wersji 1.2.0, jednak nie potwierdziły tego w dzienniku zmian.

W zależności od tego, ile musisz zdefiniować, możesz utworzyć dla nich osobny plik. Ale ogólnie definiuję je tuż przed .config()blokiem mojej aplikacji dla łatwego dostępu. Ponieważ są to nadal efektywne moduły, będziesz musiał polegać na wstrzykiwaniu zależności, aby z nich korzystać, ale są one uważane za „globalne” dla modułu aplikacji.

Na przykład:

angular.module('myApp', [])
  .value('debug', true)
  .constant('ENVIRONMENT', 'development')
  .config({...})

Następnie wewnątrz dowolnego kontrolera:

angular.module('myApp')
  .controller('MainCtrl', function(debug, ENVIRONMENT), {
    // here you can access `debug` and `ENVIRONMENT` as straight variables
  })

Z pytania początkowego wynika, że ​​i tak wymagane są tutaj właściwości statyczne, zarówno zmienne (wartość), jak i końcowe (stałe). To bardziej moja osobista opinia niż cokolwiek innego, ale uważam, że umieszczanie elementów konfiguracji środowiska wykonawczego na $rootScopezbyt szybko staje się nieuporządkowane.


źródło
Uważam, że moduł wartości jest bardzo przydatny i zwięzły. szczególnie, gdy powiążesz go ze zmienną $ scope w kontrolerze, aby zmiany w logice lub widoku tego kontrolera były powiązane ze zmienną globalną / wartością / usługą
Ben Wheeler
20
// app.js or break it up into seperate files
// whatever structure is your flavor    
angular.module('myApp', [])    

.constant('CONFIG', {
    'APP_NAME' : 'My Awesome App',
    'APP_VERSION' : '0.0.0',
    'GOOGLE_ANALYTICS_ID' : '',
    'BASE_URL' : '',
    'SYSTEM_LANGUAGE' : ''
})

.controller('GlobalVarController', ['$scope', 'CONFIG', function($scope, CONFIG) {

    // If you wish to show the CONFIG vars in the console:
    console.log(CONFIG);

    // And your CONFIG vars in .constant will be passed to the HTML doc with this:
    $scope.config = CONFIG;
}]);

W swoim HTML:

<span ng-controller="GlobalVarController">{{config.APP_NAME}} | v{{config.APP_VERSION}}</span>

źródło
8
localStorage.username = 'blah'

Jeśli masz gwarancję nowoczesnej przeglądarki. Chociaż wiedz, że twoje wartości zostaną zamienione na ciągi.

Ma również przydatną zaletę buforowania między przeładowaniami.

Kevin
źródło
5
Heh, zamierzam przechowywać wszystkie zmienne poprzez localStorage. Będziemy wtedy nawet wytrwać! Ponadto, aby obejść ograniczenie łańcucha, zapiszmy .toString () funkcji, a następnie ewaluujmy ją w razie potrzeby!
Shaz
jak już wspomniano @Shaz, ma to problem z trwałością
Żubrówka,
7

Popraw mnie, jeśli się mylę, ale po wydaniu Angular 2.0 nie wierzę $rootScope, że będzie w pobliżu. Moje przypuszczenie opiera się również na fakcie, który $scopejest usuwany. Oczywiście kontrolery nadal będą istnieć, ale nie w ng-controllermodzie. Zamiast tego pomyślmy o wprowadzeniu kontrolerów do dyrektyw. W związku ze zbliżającą się edycją najlepiej będzie używać usług jako zmiennych globalnych, jeśli chcesz łatwiej przejść z wersji 1.X na 2.0.

jason328
źródło
Zgadzam się, umieszczanie danych bezpośrednio w $ rootScope jest sprzeczne z całym celem Angulara. Poświęć trochę czasu i odpowiednio zorganizuj swój kod, a pomoże ci to nie tylko w aktualizacji AngularJS 2.x, ale ogólnie w całym rozwoju aplikacji, ponieważ staje się ona coraz bardziej złożona.
CatalinBerta
3
Jeśli $ rootScope zostanie usunięty, po prostu napisz nową usługę o nazwie „$ rootScope” :)
uylmz
3

Możesz także użyć zmiennej środowiskowej, $windowaby zmienna globalna zadeklarowana poza kontrolerem mogła zostać sprawdzona wewnątrz$watch

var initWatch = function($scope,$window){
    $scope.$watch(function(scope) { return $window.globalVar },
        function(newValue) {
            $scope.updateDisplayedVar(newValue);
    });
}

Uważaj, cykl podsumowania jest dłuższy przy tych globalnych wartościach, więc nie zawsze jest aktualizowany w czasie rzeczywistym. Muszę zbadać ten czas trawienia przy tej konfiguracji.

Mega człowiek
źródło
0

Właśnie przypadkowo znalazłem inną metodę:

Zadeklarowałem var db = nullpowyższą deklarację aplikacji, a następnie zmodyfikowałem ją w momencie, app.jsgdy uzyskałem do niej dostęp, mogłem uzyskać controller.js do niej dostęp bez problemu. Mogą być pewne problemy z tą metodą, o których nie wiem, ale to chyba dobre rozwiązanie.

Czarna Mamba
źródło
0

Spróbuj tego, nie będziesz zmuszać do wstrzykiwania $rootScopekontrolera.

app.run(function($rootScope) {
    $rootScope.Currency = 'USD';
});

Możesz go używać tylko w bloku uruchamiania, ponieważ blok konfiguracji nie zapewni ci korzystania z usługi $ rootScope.

Rahul Murari
źródło
0

To jest naprawdę dość łatwe. (Jeśli mimo to używasz Angulara 2+).

Po prostu dodaj

declare var myGlobalVarName;

Gdzieś w górnej części pliku komponentu (na przykład po instrukcjach „importowania”), będziesz mieć dostęp do „myGlobalVarName” w dowolnym miejscu w komponencie.

Andy Corman
źródło
-2

Możesz także zrobić coś takiego ..

function MyCtrl1($scope) {
    $rootScope.$root.name = 'anonymous'; 
}

function MyCtrl2($scope) {
    var name = $rootScope.$root.name;
}
pkdkk
źródło
3
$ rootScope jest niezdefiniowany, gdy używasz go w ten sposób.
Ahmad Ahmadi