AngularJS中的全局变量

358

我有一个问题,我在控制器的作用域中初始化了一个变量。然后当用户登录时,在另一个控制器中更改它。这个变量用于控制诸如导航栏之类的东西,并根据用户类型限制对站点的某些部分的访问,因此保持其值非常重要。问题在于,初始化它的控制器会被 Angular 再次调用,然后将变量重置为其初始值。

我认为这不是声明和初始化全局变量的正确方法,实际上它并不是真正的全局变量,所以我的问题是什么是正确的方法,是否有任何好的示例可以与当前版本的 Angular 一起使用?


13
由于这是谷歌的第一搜索结果:现在您可以使用app.constant()和app.value()创建应用程序范围内的常量和变量。更多信息请参见:http://bit.ly/1P51PED - Mroz
12个回答

500

你基本上有两个选项用于“全局”变量:

$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 FAQ中得知:相反,不要创建一个唯一目的是存储和返回数据片段的服务。 - Jakob Stoeck
7
我正在使用Btford的Express + Angular seed。如果我在Controller1中设置了一个$rootScope变量并移动到另一个URL(由Controller2操作),我可以访问这个变量。但是,如果我在Controller2上刷新页面,那么$rootScope上的变量将不再可用。如果我在登录时保存用户数据,如何确保在其他地方甚至在页面刷新时也能够访问这些数据? - Craig Myles
20
结合这个回答,你需要将数据放在rootScope上。但我认为这也不是正确的方式。在Angular中,如何存储和返回全局使用的数据? - user2483724
11
我很好奇专门用于存储值的服务有哪些缺点。它是隔离的,只在需要时注入,并且易于测试。那么它的缺点是什么? - notbrain
15
大家为什么都害怕真正的全局变量呢?如果你知道你的应用程序由什么组成,那就直接创建一个真正的全局JavaScript变量,而不是让事情变得过于复杂化。 - Max Yari
显示剩余14条评论

100

根据Angular Providers文档,如果您只想存储一个值,则应使用Value recipe:

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是传递引用副本的:"copy of a reference"

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 - Blaise
@DeanOr 是否有可能在页面刷新之间保留更新后的值?如果我刷新页面,该值将被重新初始化。 - Nabarun
你需要使用其他的方式,比如cookie、本地存储或数据库。 - Dean Or
1
我真的很喜欢这个。现在我可以为开发、阶段和生产修改我的构建过程。 - jemiloii
1
这里有一篇简单的文章,解释了常量和值的使用,类似于全局变量:https://ilikekillnerds.com/2014/11/constants-values-global-variables-in-angularjs-the-right-way/ - RushabhG
另一个选择是使用$rootScope,但这并不是一个真正的选择,因为出于与其他语言中不应使用全局变量相同的原因,您不应该使用它。建议适度使用。适度使用并不意味着从不使用。如果您有一个真正和适当的用途来使用全局变量,那么您应该这样做。这就是全局变量的全部意义,这就是它们存在的原因。 - pgpv

37

使用$rootScope演示AngularJS的“全局变量”示例:

控制器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
这不适用于孤立作用域指令的视图。请参见http://jsfiddle.net/3XT3F/7/。但您可以使用$root来解决它。请参见http://jsfiddle.net/3XT3F/10/。感谢[@natefaubion](https://github.com/natefaubion)指出。 - Tony Lâmpada
2
刷新后,$rootScope 的值将为空。 - xyonme
硬编码$rootScope.name等于某个值..实际上我们需要读取它并在那里进行设置。 - user2136053

25

为了在wiki池中增加另一个想法,那么AngularJS的valueconstant模块怎么样?我刚开始使用它们自己,但我认为这些可能是最好的选择。

注意:截至撰写本文时,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上会很快变得太混乱。


我发现value模块非常有用和简洁,特别是当你将它绑定到控制器内的$scope变量时,这样控制器逻辑或视图中的更改就会绑定到全局变量/值/服务。 - Ben Wheeler

22
// 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(),需要时再用eval()执行它! - Shaz
正如@Shaz所提到的,这个程序存在持久性问题。 - Żubró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

试试这个,你不需要在控制器中强制注入$rootScope

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

你只能在run块中使用它,因为config块不允许你使用$rootScope服务。


0

我无意中发现了另一种方法:

我所做的是在应用程序声明之前声明一个var db = null,然后在app.js中进行修改,当我在controller.js中访问它时,我能够毫无问题地访问它。也许这种方法存在一些我不知道的问题,但我想这是一个不错的解决方案。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接