Biến toàn cục trong AngularJS


348

Tôi có một vấn đề trong đó tôi đang khởi tạo một biến trên phạm vi trong bộ điều khiển. Sau đó, nó được thay đổi trong bộ điều khiển khác khi người dùng đăng nhập. Biến này được sử dụng để kiểm soát những thứ như thanh điều hướng và hạn chế quyền truy cập vào các phần của trang web tùy thuộc vào loại người dùng, vì vậy điều quan trọng là nó giữ giá trị của nó. Vấn đề với nó là bộ điều khiển khởi tạo nó, được gọi lại bởi một số góc và sau đó đặt lại biến trở về giá trị ban đầu.

Tôi cho rằng đây không phải là cách khai báo và khởi tạo các biến toàn cầu chính xác, nó không thực sự toàn cầu, vì vậy câu hỏi của tôi là cách nào là đúng và có bất kỳ ví dụ hay nào xung quanh hoạt động với phiên bản hiện tại không?


12
Vì đây là kết quả số 1 của google: Bây giờ bạn có thể sử dụng app.constant () và app.value () để tạo các hằng và biến toàn ứng dụng. Xem thêm tại đây: bit.ly/1P51PED
Mroz

Câu trả lời:


491

Về cơ bản, bạn đã có 2 tùy chọn cho các biến "toàn cầu":

$rootScopelà cha mẹ của tất cả các phạm vi nên các giá trị được hiển thị sẽ hiển thị trong tất cả các mẫu và bộ điều khiển. Sử dụng $rootScoperất dễ dàng vì bạn có thể chỉ cần đưa nó vào bất kỳ bộ điều khiển nào và thay đổi giá trị trong phạm vi này. Nó có thể thuận tiện nhưng có tất cả các vấn đề của các biến toàn cục .

Các dịch vụ là các singletons mà bạn có thể đưa vào bất kỳ bộ điều khiển nào và hiển thị các giá trị của chúng trong phạm vi của bộ điều khiển. Các dịch vụ, là những người độc thân vẫn là "toàn cầu" nhưng bạn đã kiểm soát tốt hơn nhiều về những nơi được sử dụng và tiếp xúc.

Sử dụng dịch vụ phức tạp hơn một chút, nhưng không nhiều, đây là một ví dụ:

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

và sau đó trong một bộ điều khiển:

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

Đây là jsFiddle hoạt động: http://jsfiddle.net/pkozlowski_opensource/BRWPM/2/


141
Từ Câu hỏi thường gặp về góc cạnh : Ngược lại, không tạo ra một dịch vụ có mục đích duy nhất trong cuộc sống là lưu trữ và trả lại các bit dữ liệu.
Jakob Stoeck

7
Tôi đang sử dụng hạt giống Express + Angular của Btford . Nếu tôi đặt một biến trên $ rootScope trong Bộ điều khiển1 nói và chuyển sang một url khác (được cung cấp bởi Bộ điều khiển2 nói), tôi có thể truy cập vào biến này. Tuy nhiên, nếu tôi làm mới trang trên Controller2 thì biến không còn truy cập được trên $ rootScope. Nếu tôi đang lưu dữ liệu người dùng khi đăng nhập, làm thế nào tôi có thể đảm bảo dữ liệu này có thể truy cập được ở nơi khác ngay cả khi làm mới trang?
Craig Myles

19
@JakobStoeck Kết hợp câu trả lời này với câu trả lời này và bạn còn lại với việc đưa dữ liệu lên rootScope. Tôi cũng không nghĩ điều đó đúng. Cách thức lưu trữ và trả lại các bit dữ liệu được sử dụng trên toàn cầu là gì?
dùng2483724

11
Tôi tò mò về những nhược điểm của dịch vụ dành riêng cho việc lưu trữ các giá trị. Bị cô lập, chỉ được tiêm ở nơi bạn cần và dễ dàng kiểm tra. Nhược điểm là gì?
Brian

12
trời ơi, tại sao mọi người lại sợ các biến toàn cầu thực sự, nếu bạn biết ứng dụng của bạn sẽ bao gồm những gì, chỉ cần tạo một biến js toàn cầu thực sự thay vì các công cụ quá phức tạp
Max Yari

93

Nếu bạn chỉ muốn lưu trữ một giá trị, theo tài liệu Angular trên Nhà cung cấp , bạn nên sử dụng công thức Giá trị:

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

Sau đó sử dụng nó trong một bộ điều khiển như thế này:

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

Điều tương tự có thể đạt được bằng cách sử dụng Nhà cung cấp, Nhà máy hoặc Dịch vụ vì chúng "chỉ là đường cú pháp trên đầu công thức của nhà cung cấp" nhưng sử dụng Giá trị sẽ đạt được những gì bạn muốn với cú pháp tối thiểu.

Tùy chọn khác là sử dụng $rootScope, nhưng nó không thực sự là một tùy chọn vì bạn không nên sử dụng nó cho cùng một lý do bạn không nên sử dụng các biến toàn cục trong các ngôn ngữ khác. Đó là lời khuyên sẽ được sử dụng một cách tiết kiệm.

Vì tất cả các phạm vi kế thừa từ $rootScope, nếu bạn có một biến $rootScope.datavà ai đó quên datađã được xác định và tạo $scope.datatrong phạm vi cục bộ, bạn sẽ gặp vấn đề.


Nếu bạn muốn sửa đổi giá trị này và duy trì nó trên tất cả các bộ điều khiển của bạn, hãy sử dụng một đối tượng và sửa đổi các thuộc tính ghi nhớ Javascript được chuyển qua "bản sao của một tham chiếu" :

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

Ví dụ về JSFiddle


1
Khi tôi thay đổi giá trị trên một chế độ xem, giá trị mới không còn tồn tại. Làm thế nào mà? Bạn có thể vui lòng cập nhật câu trả lời của bạn và cho chúng tôi xem làm thế nào bạn clientIdcó thể được cập nhật?
Blaise

@DeanOr Có thể giữ nguyên giá trị cập nhật giữa các lần làm mới của trang không? Giá trị được xác định lại nếu tôi làm mới trang.
Nabarun

Bạn sẽ phải sử dụng một cái gì đó khác cho cái đó như cookie, bộ nhớ cục bộ hoặc cơ sở dữ liệu.
Trưởng khoa hoặc

1
Tôi thực sự thích cái này nhất Bây giờ tôi có thể sửa đổi bằng cách xây dựng quy trình cho dev, giai đoạn và prod
jemiloii

1
Có một bài viết đơn giản giải thích việc sử dụng các hằng số và giá trị tương tự như các biến toàn cầu: ilikekillnerds.com/2014/11/ mẹo
RushabhG

36

Ví dụ về AngularJS "biến toàn cục" bằng cách sử dụng $rootScope:

Bộ điều khiển 1 đặt biến toàn cục:

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

Bộ điều khiển 2 đọc biến toàn cục:

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

Đây là một jsFiddle hoạt động: http://jsfiddle.net/natefriedman/3XT3F/1/


2
Điều này không hoạt động trong quan điểm của các chỉ thị phạm vi bị cô lập. Xem jsfiddle.net/3XT3F/7 . Nhưng bạn có thể sử dụng $ root để làm việc xung quanh nó. Xem jsfiddle.net/3XT3F/10 . Nhờ @natefaubion cho trỏ nó ra
Tony Lâmpada

2
khi làm mới, giá trị $ rootScope sẽ trống.
xyonme

mã hóa $ rootScope.name bằng với một số giá trị..thực tế chúng ta cần đọc nó và đặt nó ở đó.

25

Quan tâm đến việc thêm một ý tưởng khác vào nhóm wiki, nhưng còn AngularJS ' valueconstantcác mô-đun thì sao? Tôi chỉ mới bắt đầu sử dụng chúng cho mình, nhưng có vẻ như đây là những lựa chọn tốt nhất ở đây.

Lưu ý: tại thời điểm viết bài, Angular 1.3.7 là bản ổn định mới nhất, tôi tin rằng những thứ này đã được thêm vào 1.2.0, mặc dù chưa xác nhận điều này với thay đổi.

Tùy thuộc vào số lượng bạn cần xác định, bạn có thể muốn tạo một tệp riêng cho họ. Nhưng tôi thường xác định những điều này ngay trước ứng dụng của tôi.config() khối để dễ truy cập. Vì đây vẫn là các mô-đun hiệu quả, bạn sẽ cần dựa vào nội dung phụ thuộc để sử dụng chúng, nhưng chúng được coi là "toàn cầu" cho mô-đun ứng dụng của bạn.

Ví dụ:

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

Sau đó, bên trong bất kỳ bộ điều khiển:

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

Từ câu hỏi ban đầu thực sự nghe có vẻ như các thuộc tính tĩnh được yêu cầu ở đây, dù là có thể thay đổi (giá trị) hoặc cuối cùng (không đổi). Đó là ý kiến ​​cá nhân của tôi hơn bất cứ điều gì khác, nhưng tôi thấy việc đặt các mục cấu hình thời gian chạy $rootScopetrở nên quá lộn xộn, quá nhanh.


Tôi thấy các mô-đun giá trị rất hữu ích và súc tích. đặc biệt là khi bạn liên kết nó với biến $ scope trong bộ điều khiển, để những thay đổi trong logic hoặc khung nhìn của bộ điều khiển đó bị ràng buộc với biến / giá trị / dịch vụ toàn cầu
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;
}]);

Trong HTML của bạn:

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

8
localStorage.username = 'blah'

Nếu bạn được đảm bảo trên một trình duyệt hiện đại. Mặc dù biết tất cả các giá trị của bạn sẽ được chuyển thành chuỗi.

Cũng có lợi ích của việc được lưu trữ giữa các lần tải lại.


5
Heh, tôi sẽ lưu trữ tất cả các biến thông qua localStorage. Chúng tôi thậm chí sẽ có một số loại kiên trì sau đó! Ngoài ra, để vượt qua giới hạn chuỗi, hãy lưu trữ .toString () của hàm sau đó đánh giá nó khi cần!
Shaz

như đã được đề cập bởi @Shaz, điều này có vấn đề về sự bền bỉ
ubrówka

7

Vui lòng sửa lại cho tôi nếu tôi sai, nhưng khi Angular 2.0 được phát hành, tôi không tin $rootScopesẽ có mặt. Phỏng đoán của tôi dựa trên thực tế $scopecũng đang bị loại bỏ. Rõ ràng bộ điều khiển, vẫn sẽ tồn tại, chỉ là không theo ng-controllermốt. Thay vào đó, hãy tiêm bộ điều khiển vào chỉ thị. Khi việc phát hành sắp xảy ra, tốt nhất nên sử dụng các dịch vụ làm biến toàn cục nếu bạn muốn có thời gian dễ dàng hơn để chuyển từ verison 1.X sang 2.0.


Tôi đồng ý, việc đưa dữ liệu trực tiếp lên $ rootScope là trái với toàn bộ mục đích của Angular. Hãy dành một chút thời gian và sắp xếp mã của bạn đúng cách và nó sẽ giúp bạn, không chỉ trong bản nâng cấp AngularJS 2.x, mà nói chung trong suốt quá trình phát triển ứng dụng của bạn khi nó phát triển phức tạp hơn.
CatalinBerta

3
Nếu $ rootScope bị xóa, chỉ cần viết một dịch vụ mới có tên là "$ rootScope" :)
uylmz

3

Bạn cũng có thể sử dụng biến môi trường $windowđể có thể kiểm tra biến toàn cục bên ngoài bộ điều khiển bên trong$watch

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

Trở nên đáng chú ý, chu trình tiêu hóa dài hơn với các giá trị toàn cầu này, vì vậy nó không phải lúc nào cũng được cập nhật theo thời gian thực. Tôi cần điều tra về thời gian tiêu hóa với cấu hình này.


0

Tôi chỉ tìm thấy một phương pháp khác do nhầm lẫn:

Những gì tôi đã làm là khai báo một tuyên bố var db = nullứng dụng ở trên và sau đó sửa đổi nó app.jskhi tôi truy cập nó trong đó controller.js tôi có thể truy cập nó mà không gặp vấn đề gì. Có thể có một số vấn đề với phương pháp này mà tôi không biết nhưng đó là Tôi đoán là một giải pháp tốt.


0

Hãy thử điều này, bạn sẽ không buộc phải tiêm $rootScopetrong bộ điều khiển.

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

Bạn chỉ có thể sử dụng nó trong khối chạy vì khối cấu hình sẽ không cung cấp cho bạn sử dụng dịch vụ của $ rootScope.


0

Nó thực sự khá dễ dàng. (Nếu bạn đang sử dụng Angular 2+.)

Đơn giản chỉ cần thêm

declare var myGlobalVarName;

Ở đâu đó trong đầu tệp thành phần của bạn (chẳng hạn như sau câu lệnh "nhập") và bạn sẽ có thể truy cập "myGlobalVarName" bất cứ nơi nào trong thành phần của bạn.


-2

Bạn cũng có thể làm một cái gì đó như thế này ..

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

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

3
$ rootScope không được xác định khi bạn sử dụng nó theo cách này.
Ahmad Ahmadi
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.