ตัวแปรทั่วโลกใน AngularJS


348

ฉันมีปัญหาที่ฉันกำลังเริ่มต้นตัวแปรในขอบเขตในคอนโทรลเลอร์ จากนั้นจะได้รับการเปลี่ยนแปลงในตัวควบคุมอื่นเมื่อผู้ใช้เข้าสู่ระบบตัวแปรนี้จะใช้ในการควบคุมสิ่งต่าง ๆ เช่นแถบนำทางและ จำกัด การเข้าถึงส่วนต่าง ๆ ของเว็บไซต์ขึ้นอยู่กับประเภทของผู้ใช้จึงเป็นสิ่งสำคัญที่จะถือค่าของมัน ปัญหาที่เกิดขึ้นคือมันเป็นตัวควบคุมที่เริ่มต้นมันได้รับการเรียกอีกครั้งโดยเชิงมุมบางวิธีแล้วรีเซ็ตตัวแปรกลับไปเป็นค่าเริ่มต้น

ฉันคิดว่านี่ไม่ใช่วิธีที่ถูกต้องในการประกาศและกำหนดค่าเริ่มต้นให้กับตัวแปรระดับโลก แต่มันไม่ได้เป็นค่าทั่วโลกดังนั้นคำถามของฉันคือวิธีที่ถูกต้องและมีตัวอย่างที่ดีเกี่ยวกับการทำงานของมุมเชิงมุมรุ่นปัจจุบันหรือไม่


12
เนื่องจากนี่คือผลลัพธ์อันดับ # 1 ของ Google: ขณะนี้คุณสามารถใช้ app.constant () และ app.value () เพื่อสร้างค่าคงที่และตัวแปรทั่วทั้งแอป เพิ่มเติมที่นี่: bit.ly/1P51PED
Mroz

คำตอบ:


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
จากคำถามที่พบบ่อยเชิงมุม : ในทางกลับกันอย่าสร้างบริการที่มีจุดประสงค์เพียงอย่างเดียวในชีวิตคือการจัดเก็บและส่งคืนบิตของข้อมูล
Jakob Stoeck

7
ฉันใช้เมล็ดด่วน + เชิงมุมโดย Btford ถ้าฉันตั้งค่าตัวแปรใน $ rootScope ใน Controller1 พูดและย้ายไปยัง URL อื่น (ขับเคลื่อนโดย Controller2 พูด) ฉันสามารถเข้าถึงตัวแปรนี้ได้ อย่างไรก็ตามถ้าฉันรีเฟรชหน้าใน Controller2 แล้วตัวแปรจะไม่สามารถเข้าถึงได้บน $ rootScope หากฉันบันทึกข้อมูลผู้ใช้ในการลงชื่อเข้าใช้ฉันจะมั่นใจได้อย่างไรว่าข้อมูลนี้สามารถเข้าถึงได้จากที่อื่นแม้ในการรีเฟรชหน้า?
Craig Myles

19
@JakobStoeck รวมสิ่งนี้กับคำตอบนี้แล้วคุณจะเหลือข้อมูลไว้ใน rootScope ฉันไม่คิดว่าถูกต้องเช่นกัน วิธีการจัดเก็บและส่งคืนบิตข้อมูลที่ใช้ทั่วโลกคืออะไร
user2483724

11
ฉันอยากรู้ว่าข้อเสียของบริการที่เฉพาะเจาะจงสำหรับการจัดเก็บค่าคืออะไร แยกฉีดเฉพาะที่คุณต้องการและทดสอบได้ง่าย ข้อเสียคืออะไร?
Brian

12
โอ้พระเจ้าทำไมทุกคนกลัว reaal ตัวแปรทั่วโลกถ้าคุณรู้ว่าสิ่งที่แอพของคุณจะประกอบด้วยเพียงแค่สร้างตัวแปร js ทั่วโลกจริงแทนสิ่งที่ overcomplicating
Max Yari

93

หากคุณต้องการเก็บค่าตามเอกสารเชิงมุมของผู้ให้บริการคุณควรใช้สูตรค่า:

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

คุณจะต้องใช้อย่างอื่นเพื่อสิ่งนั้นเช่นคุกกี้ที่เก็บข้อมูลในตัวเครื่องหรือฐานข้อมูล
คณบดีหรือ

1
ฉันชอบอันนี้ที่สุด ตอนนี้ฉันสามารถแก้ไขได้โดยสร้างกระบวนการสำหรับ dev, stage และ prod
jemiloii

1
มีบทความง่าย ๆ ที่อธิบายการใช้ค่าคงที่และค่าที่คล้ายกับตัวแปร Global: 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สำหรับชี้ออก
โทนี่ Lampada

2
ในการรีเฟรชค่า $ rootScope จะว่างเปล่า
xyonme

hardcoding $ rootScope.name เท่ากับค่าบางอย่าง .. จริง ๆ แล้วเราจำเป็นต้องอ่านและตั้งค่าไว้ที่นั่น

25

เพื่อเพิ่มความคิดอื่น ๆ ลงใน wiki pool แต่ 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 ภายในตัวควบคุมเพื่อให้การเปลี่ยนแปลงภายในตรรกะหรือมุมมองของตัวควบคุมนั้นถูกผูกไว้กับตัวแปร / ค่า / บริการทั่วโลก
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;
}]);

ใน HTML ของคุณ:

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

8
localStorage.username = 'blah'

หากคุณรับประกันว่าจะอยู่ในเบราว์เซอร์ที่ทันสมัย แม้ว่าจะรู้ว่าคุณค่าของคุณจะกลายเป็นสตริง

นอกจากนี้ยังมีประโยชน์ในการถูกแคชระหว่าง reloads


5
เฮ้ฉันจะเก็บตัวแปรทั้งหมดผ่าน localStorage เราจะมีความเพียรอยู่บ้าง! นอกจากนี้เพื่อหลีกเลี่ยงข้อ จำกัด ของสตริงลองจัดเก็บ. toString () ของฟังก์ชั่นแล้วค่อยประเมินเมื่อจำเป็น!
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

ฉันเพิ่งพบวิธีอื่นโดยไม่ได้ตั้งใจ:

สิ่งที่ฉันทำคือประกาศประกาศvar db = nullแอพด้านบนและจากนั้นแก้ไขในตอนapp.jsนั้นเมื่อฉันเข้าถึงในcontroller.js ฉันสามารถเข้าถึงได้โดยไม่มีปัญหาอาจมีปัญหาบางอย่างเกี่ยวกับวิธีการนี้ซึ่งฉันไม่ทราบ ทางออกที่ดีฉันเดา


0

ลองสิ่งนี้คุณจะไม่บังคับให้ฉีด$rootScopeในคอนโทรลเลอร์

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

คุณสามารถใช้มันในบล็อกการทำงานเท่านั้นเนื่องจากบล็อกการตั้งค่าจะไม่ให้บริการการใช้งานของ $ rootScope


0

จริงๆแล้วมันง่ายมาก (ถ้าคุณใช้ Angular 2+ อยู่ดี)

เพียงเพิ่ม

declare var myGlobalVarName;

อยู่ด้านบนสุดของไฟล์คอมโพเนนต์ของคุณ (เช่นหลังจากคำสั่ง "นำเข้า") และคุณจะสามารถเข้าถึง "myGlobalVarName" ได้ทุกที่ภายในคอมโพเนนต์ของคุณ


-2

คุณสามารถทำอะไรเช่นนี้ ..

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

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

3
$ rootScope ไม่ได้ถูกกำหนดเมื่อคุณใช้วิธีนี้
Ahmad Ahmadi
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.