Глобальные переменные в AngularJS


348

У меня проблема, когда я инициализирую переменную в области видимости в контроллере. Затем он изменяется в другом контроллере, когда пользователь входит в систему. Эта переменная используется для управления такими вещами, как панель навигации, и ограничивает доступ к частям сайта в зависимости от типа пользователя, поэтому важно, чтобы он сохранял свое значение. Проблема в том, что контроллер, который его инициализирует, снова вызывается с помощью angular, а затем сбрасывает переменную до ее первоначального значения.

Я предполагаю, что это не правильный способ объявления и инициализации глобальных переменных, ну, на самом деле, он не является глобальным, поэтому мой вопрос в том, как правильно и есть ли хорошие примеры, которые работают с текущей версией angular?


12
Поскольку это # ​​1 результат Google: теперь вы можете использовать app.constant () и app.value () для создания констант и переменных для всего приложения. Подробнее здесь: bit.ly/1P51PED
Mróz

Ответы:


491

У вас есть в основном 2 варианта для «глобальных» переменных:

$rootScopeявляется родителем всех областей, поэтому значения, отображаемые там, будут видны во всех шаблонах и контроллерах. Использовать $rootScopeочень просто, так как вы можете просто вставить его в любой контроллер и изменить значения в этой области. Это может быть удобно, но имеет все проблемы глобальных переменных .

Сервисы - это синглтоны, которые вы можете внедрить в любой контроллер и выставить их значения в области видимости контроллера. Службы, будучи одиночками, по-прежнему «глобальны», но вы гораздо лучше контролируете, где они используются и предоставляются.

Использование сервисов немного сложнее, но не так много, вот пример:

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

а затем в контроллере:

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

Вот рабочий jsFiddle: http://jsfiddle.net/pkozlowski_opensource/BRWPM/2/


141
Из часто задаваемых вопросов об Angular : наоборот, не создавайте сервис, единственная цель которого - хранить и возвращать биты данных.
Якоб Стоек

7
Я использую семя Express + Angular от Btford . Если я установлю переменную в $ rootScope в Controller1 скажем и перейду на другой URL (питание от Controller2 скажем), я получу доступ к этой переменной. Однако если я обновлю страницу на Controller2, переменная больше не будет доступна в $ rootScope. Если я сохраняю данные пользователя при входе в систему, как я могу гарантировать, что эти данные доступны в другом месте даже при обновлении страницы?
Крейг Майлз,

19
@JakobStoeck Объедините это с этим ответом, и у вас останется поместить данные в rootScope. Я тоже не думаю, что это правильно. Каков угловой способ хранения и возврата глобально используемых битов данных?
user2483724

11
Мне любопытно, в чем недостатки сервиса, специально предназначенного для хранения ценностей. Изолированный, вводится только там, где это необходимо, и легко тестируется. Какой минус?
Брайан

12
о боже, почему все боятся использовать глобальные переменные, если вы знаете, из чего будет состоять ваше приложение, просто создайте настоящую глобальную переменную js, а не слишком усложняйте вещи
Макс Яри

93

Если вы просто хотите сохранить значение, в соответствии с документацией Angular по провайдерам , вы должны использовать рецепт Value:

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

Затем используйте его в контроллере следующим образом:

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

То же самое может быть достигнуто с помощью Provider, Factory или Service, поскольку они «просто синтаксический сахар поверх рецепта провайдера», но использование Value позволит достичь того, что вы хотите, с минимальным синтаксисом.

Другой вариант - использовать $rootScope, но на самом деле это не вариант, потому что вы не должны использовать его по тем же причинам, по которым вы не должны использовать глобальные переменные в других языках. Это рекомендуется , чтобы использовать с осторожностью.

Поскольку все области наследуются $rootScope, если у вас есть переменная, $rootScope.dataи кто-то забывает, что dataона уже определена и создает $scope.dataв локальной области, вы столкнетесь с проблемами.


Если вы хотите изменить это значение и сохранить его на всех ваших контроллерах, используйте объект и изменяйте свойства, помня, что Javascript передается как «копия ссылки» :

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

Пример JSFiddle


1
Когда я изменил значение в одном представлении, новое значение не является постоянным. Как придешь? Не могли бы вы обновить свой ответ и дать нам посмотреть, как clientIdможно обновить ваш ответ ?
Блез

@DeanOr Можно ли сохранить обновленное значение между обновлениями страницы? Значение будет повторно инициализировано, если я обновлю страницу.
Набарун

Для этого вам придется использовать что-то другое, например, cookie, локальное хранилище или базу данных.
Дин Или

1
Мне очень нравится этот. Теперь я могу изменить процесс сборки для dev, stage и prod
jemiloii

1
Есть простая статья, объясняющая использование констант и значений, аналогичных глобальным переменным: ilikekillnerds.com/2014/11/…
RushabhG

36

Пример AngularJS "глобальные переменные" с использованием $rootScope:

Контроллер 1 устанавливает глобальную переменную:

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

Контроллер 2 читает глобальную переменную:

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

Вот рабочий jsFiddle: http://jsfiddle.net/natefriedman/3XT3F/1/


2
Это не работает с точки зрения изолированных директив области видимости. См. Jsfiddle.net/3XT3F/7 . Но вы можете использовать $ root, чтобы обойти это. См. Jsfiddle.net/3XT3F/10 . Спасибо @natefaubion за указание на это
Тони Лампада

2
при обновлении значение $ rootScope будет пустым.
ксьонме

жесткое кодирование $ rootScope.name равно некоторому значению .. на самом деле нам нужно прочитать его и установить там.

25

В интересах добавления еще одной идеи в пул вики, но как насчет AngularJS ' valueи constantмодулей? Я только начинаю использовать их сам, но для меня это звучит так, как будто это, вероятно, лучшие варианты здесь.

Примечание: на момент написания этой статьи, Angular 1.3.7 является последней стабильной версией, я думаю, что они были добавлены в 1.2.0, но не подтвердили это в журнале изменений.

В зависимости от того, сколько вам нужно определить, вы можете создать для них отдельный файл. Но я обычно определяю их непосредственно перед .config()блоком моего приложения для легкого доступа. Поскольку они по-прежнему являются эффективными модулями, вам нужно полагаться на внедрение зависимостей, чтобы использовать их, но они считаются «глобальными» для вашего модуля приложения.

Например:

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

Тогда внутри любого контроллера:

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

Исходный вопрос на самом деле звучит так, как будто статические свойства здесь необходимы в любом случае, либо как изменяемые (значение), либо как конечные (постоянные). Это больше мое личное мнение, чем что-либо еще, но я нахожу размещение элементов конфигурации времени выполнения на$rootScope слишком запутанно и слишком быстро.


Я считаю модуль ценностей очень полезным и лаконичным. особенно когда вы связываете его с переменной $ scope в контроллере, чтобы изменения в логике или представлении этого контроллера были связаны с глобальной переменной / значением / службой
Бен Уилер

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;
}]);

В вашем HTML:

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

8
localStorage.username = 'blah'

Если вы гарантированно находитесь в современном браузере. Хотя знайте, что все ваши ценности будут превращены в строки.

Также имеет удобную возможность кэширования между перезагрузками.


5
Хех, я собираюсь хранить все переменные через localStorage. Тогда у нас даже будет какая-то настойчивость! Кроме того, чтобы обойти ограничение строки, давайте сохраним функцию .toString (), а затем оценим ее при необходимости!
Shaz

как уже упоминалось @Shaz, это проблема постоянства
rubrówka

7

Пожалуйста, поправьте меня, если я ошибаюсь, но когда выйдет Angular 2.0, я не верю, что $rootScopeон будет рядом. Моя гипотеза основана на том факте, $scopeчто удаляется также. Очевидно, что контроллеры все еще будут существовать, просто не в ng-controllerмоде. Подумайте о введении контроллеров в директивы. Так как выпуск скоро будет готов, будет лучше использовать сервисы в качестве глобальных переменных, если вы хотите более легкое время перейти с версии 1.X на 2.0.


Я согласен, размещение данных непосредственно в $ rootScope противоречит всей цели Angular. Потратьте немного времени и правильно упорядочите код, и он поможет вам не только в обновлении AngularJS 2.x, но и в целом в процессе разработки вашего приложения по мере его усложнения.
CatalinBerta

3
Если $ rootScope удален, просто напишите новый сервис, который называется «$ rootScope» :)
uylmz

3

Вы также можете использовать переменную окружения, $windowчтобы глобальная переменная, объявленная вне контроллера, могла быть проверена внутри$watch

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

Обратите внимание, что цикл дайджеста с этими глобальными значениями длиннее, поэтому он не всегда обновляется в реальном времени. Мне нужно исследовать время этого дайджеста с этой конфигурацией.


0

Я просто нашел другой метод по ошибке:

Что я сделал, так это объявил var db = nullвышеупомянутое объявление приложения и затем изменил его в app.jsто время, когда я получил к нему доступ, controller.js я смог получить к нему доступ без каких-либо проблем. Могут быть некоторые проблемы с этим методом, о которых я не знаю, но это хорошее решение, я думаю.


0

Попробуйте это, вы не будете принудительно вводить $rootScopeв контроллер.

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

Вы можете использовать его только в блоке запуска, потому что блок конфигурации не предоставит вам возможность использовать сервис $ rootScope.


0

Это на самом деле довольно легко. (Если вы все равно используете Angular 2+.)

Просто добавь

declare var myGlobalVarName;

Где-то в верхней части файла вашего компонента (например, после операторов «import»), и вы сможете получить доступ к «myGlobalVarName» в любом месте вашего компонента.


-2

Вы также можете сделать что-то вроде этого ..

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

function MyCtrl2($scope) {
    var name = $rootScope.$root.name;
}

3
$ rootScope не определен, когда вы используете его таким образом.
Ахмад Ахмади
Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.