मैं इस सवाल पर कुछ इसी तरह की तलाश में ठोकर खाई, लेकिन मुझे लगता है कि यह क्या हो रहा है, साथ ही कुछ अतिरिक्त समाधानों के बारे में पूरी तरह से स्पष्टीकरण के योग्य है।
जब एक कोणीय अभिव्यक्ति आपके द्वारा उपयोग किए एचटीएमएल में मौजूद है, जैसे कोणीय स्वचालित रूप से एक सेट $watch
के लिए $scope.foo
, और जब भी एचटीएमएल अद्यतन करेगा $scope.foo
बदल जाता है।
<div ng-controller="FooCtrl">
<div ng-repeat="item in foo">{{ item }}</div>
</div>
यहां मुद्दा यह है कि दो चीजों में से एक को प्रभावित कर रहा है aService.foo
जैसे कि परिवर्तन पूर्ववत हैं। ये दो संभावनाएँ हैं:
aService.foo
हर बार एक नए सरणी में सेट हो रहा है, जिससे इसका संदर्भ पुराना हो गया है।
aService.foo
इस तरह से अद्यतन किया जा रहा है कि अद्यतन पर एक $digest
चक्र ट्रिगर नहीं होता है।
समस्या 1: उल्लिखित संदर्भ
पहली संभावना को देखते हुए, यह मानते हुए कि $digest
लागू किया जा रहा है, अगर aService.foo
हमेशा एक ही सरणी होती थी, तो स्वचालित रूप से सेट$watch
परिवर्तनों का पता लगाएगा, जैसा कि नीचे दिए गए कोड स्निपेट में दिखाया गया है।
समाधान 1-ए: सुनिश्चित करें कि प्रत्येक अद्यतन पर सरणी या वस्तु एक ही वस्तु है
angular.module('myApp', [])
.factory('aService', [
'$interval',
function($interval) {
var service = {
foo: []
};
// Create a new array on each update, appending the previous items and
// adding one new item each time
$interval(function() {
if (service.foo.length < 10) {
var newArray = []
Array.prototype.push.apply(newArray, service.foo);
newArray.push(Math.random());
service.foo = newArray;
}
}, 1000);
return service;
}
])
.factory('aService2', [
'$interval',
function($interval) {
var service = {
foo: []
};
// Keep the same array, just add new items on each update
$interval(function() {
if (service.foo.length < 10) {
service.foo.push(Math.random());
}
}, 1000);
return service;
}
])
.controller('FooCtrl', [
'$scope',
'aService',
'aService2',
function FooCtrl($scope, aService, aService2) {
$scope.foo = aService.foo;
$scope.foo2 = aService2.foo;
}
]);
<!DOCTYPE html>
<html>
<head>
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.23/angular.min.js"></script>
<link rel="stylesheet" href="style.css" />
<script src="script.js"></script>
</head>
<body ng-app="myApp">
<div ng-controller="FooCtrl">
<h1>Array changes on each update</h1>
<div ng-repeat="item in foo">{{ item }}</div>
<h1>Array is the same on each udpate</h1>
<div ng-repeat="item in foo2">{{ item }}</div>
</div>
</body>
</html>
जैसा कि आप देख सकते हैं, एनजी-रिपीट माना जाता है कि बदलाव aService.foo
होने पर अपडेट नहीं होता aService.foo
है, लेकिन एनजी-रिपीट इससे जुड़ा होता aService2.foo
है । इसका कारण यह है कि हमारा संदर्भ aService.foo
पुराना है, लेकिन हमारा संदर्भ पुराना aService2.foo
नहीं है। हमने प्रारंभिक सरणी के साथ एक संदर्भ बनाया $scope.foo = aService.foo;
था, जिसे तब सेवा द्वारा खारिज कर दिया गया था, यह अगले अद्यतन पर है, जिसका अर्थ है$scope.foo
अब उस सरणी को संदर्भित नहीं अब हम चाहते थे।
हालांकि, हालांकि यह सुनिश्चित करने के लिए कई तरीके हैं कि प्रारंभिक संदर्भ को चातुर्य में रखा जाए, कभी-कभी वस्तु या सरणी को बदलना आवश्यक हो सकता है। या फिर क्या हुआ अगर सेवा संपत्ति संदर्भ एक तरह एक आदिम String
या Number
? उन मामलों में, हम केवल एक संदर्भ पर भरोसा नहीं कर सकते। तो हम क्या कर सकते हैं?
पहले दिए गए जवाबों में से कई उस समस्या के लिए कुछ समाधान देते हैं। हालांकि, मैं व्यक्तिगत रूप से जिन और thetallweeks द्वारा सुझाई गई सरल पद्धति का उपयोग करने के लिए टिप्पणी में हूं :
html मार्कअप में बस aService.foo का संदर्भ लें
समाधान 1-बी: सेवा को गुंजाइश में संलग्न करें, और {service}.{property}
HTML में संदर्भ ।
मतलब, बस यह करें:
HTML:
<div ng-controller="FooCtrl">
<div ng-repeat="item in aService.foo">{{ item }}</div>
</div>
जे एस:
function FooCtrl($scope, aService) {
$scope.aService = aService;
}
angular.module('myApp', [])
.factory('aService', [
'$interval',
function($interval) {
var service = {
foo: []
};
// Create a new array on each update, appending the previous items and
// adding one new item each time
$interval(function() {
if (service.foo.length < 10) {
var newArray = []
Array.prototype.push.apply(newArray, service.foo);
newArray.push(Math.random());
service.foo = newArray;
}
}, 1000);
return service;
}
])
.controller('FooCtrl', [
'$scope',
'aService',
function FooCtrl($scope, aService) {
$scope.aService = aService;
}
]);
<!DOCTYPE html>
<html>
<head>
<script data-require="angular.js@1.4.7" data-semver="1.4.7" src="https://ajax.googleapis.com/ajax/libs/angularjs/1.4.7/angular.js"></script>
<link rel="stylesheet" href="style.css" />
<script src="script.js"></script>
</head>
<body ng-app="myApp">
<div ng-controller="FooCtrl">
<h1>Array changes on each update</h1>
<div ng-repeat="item in aService.foo">{{ item }}</div>
</div>
</body>
</html>
इस तरह, प्रत्येक पर $watch
हल होगाaService.foo
$digest
, जिसे सही ढंग से अद्यतन मूल्य मिलेगा।
यह उस तरह का है जो आप अपने वर्कअराउंड के साथ करने की कोशिश कर रहे थे, लेकिन रास्ते के बारे में बहुत कम दौर में। आपने $watch
नियंत्रक में एक अनावश्यक जोड़ दिया जो स्पष्ट रूप foo
से $scope
जब भी बदलता है , डालता है । आपको लगता है कि अतिरिक्त की जरूरत नहीं है $watch
जब आप संलग्न aService
के बजाय aService.foo
करने के लिए $scope
बाँध स्पष्ट रूप से करने के लिए, और aService.foo
मार्कअप में।
अब यह सब ठीक है और अच्छा मानकर एक $digest
चक्र लगाया जा रहा है। उपरोक्त मेरे उदाहरणों में, मैंने $interval
सरणियों को अद्यतन करने के लिए कोणीय सेवा का उपयोग किया , जो $digest
प्रत्येक अद्यतन के बाद स्वचालित रूप से एक लूप को बंद कर देता है । लेकिन अगर सेवा चर (जो भी कारण से) "कोणीय दुनिया" के अंदर अद्यतन नहीं हो रहे हैं, तो क्या होगा। दूसरे शब्दों में, हम न एक है $digest
चक्र स्वचालित रूप से सक्रिय किया जा रहा है जब भी सेवा संपत्ति बदलता है?
यहाँ समाधान में से कई इस मुद्दे को हल करेंगे, लेकिन मैं कोड व्हिस्परर से सहमत हूं :
यही कारण है कि हम एंगुलर जैसे ढांचे का उपयोग कर रहे हैं, अपने स्वयं के पर्यवेक्षक पैटर्न को नहीं पकाना है
इसलिए, मैं उपयोग करना जारी रखना पसंद करूंगा aService.foo
HTML मार्कअप में संदर्भ जैसा कि ऊपर दिए गए दूसरे उदाहरण में दिखाया गया है, और नियंत्रक के भीतर अतिरिक्त कॉलबैक दर्ज नहीं करना है।
समाधान 2: सेटर और गेट्टर का उपयोग करें $rootScope.$apply()
मैं हैरान था कि किसी ने अभी तक एक सेटर और गेट्टर के उपयोग का सुझाव नहीं दिया है । इस क्षमता को ECMAScript5 में पेश किया गया था, और इस तरह से यह अब सालों से चली आ रही है। बेशक, इसका मतलब है कि, यदि किसी भी कारण से, आपको वास्तव में पुराने ब्राउज़रों का समर्थन करने की आवश्यकता है, तो यह विधि काम नहीं करेगी, लेकिन मुझे ऐसा लगता है कि गेटर्स और सेटरर्स का जावास्क्रिप्ट में बहुत अधिक उपयोग किया जाता है। इस विशेष मामले में, वे काफी उपयोगी हो सकते हैं:
factory('aService', [
'$rootScope',
function($rootScope) {
var realFoo = [];
var service = {
set foo(a) {
realFoo = a;
$rootScope.$apply();
},
get foo() {
return realFoo;
}
};
// ...
}
angular.module('myApp', [])
.factory('aService', [
'$rootScope',
function($rootScope) {
var realFoo = [];
var service = {
set foo(a) {
realFoo = a;
$rootScope.$apply();
},
get foo() {
return realFoo;
}
};
// Create a new array on each update, appending the previous items and
// adding one new item each time
setInterval(function() {
if (service.foo.length < 10) {
var newArray = [];
Array.prototype.push.apply(newArray, service.foo);
newArray.push(Math.random());
service.foo = newArray;
}
}, 1000);
return service;
}
])
.controller('FooCtrl', [
'$scope',
'aService',
function FooCtrl($scope, aService) {
$scope.aService = aService;
}
]);
<!DOCTYPE html>
<html>
<head>
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.23/angular.min.js"></script>
<link rel="stylesheet" href="style.css" />
<script src="script.js"></script>
</head>
<body ng-app="myApp">
<div ng-controller="FooCtrl">
<h1>Using a Getter/Setter</h1>
<div ng-repeat="item in aService.foo">{{ item }}</div>
</div>
</body>
</html>
यहाँ मैंने सेवा फ़ंक्शन में एक 'निजी' चर जोड़ा realFoo
:। इस पर क्रमशः कार्यों get foo()
और set foo()
कार्यों का उपयोग करके अद्यतन और पुनर्प्राप्त किया जाता हैservice
ऑब्जेक्ट जाता है।
$rootScope.$apply()
सेट फ़ंक्शन के उपयोग पर ध्यान दें । यह सुनिश्चित करता है कि कोणीय किसी भी परिवर्तन से अवगत होगा service.foo
। यदि आपको 'इनप्रोग' त्रुटियां मिलती हैं, तो यह उपयोगी संदर्भ पृष्ठ देखें , या यदि आप कोणीय> = 1.3 का उपयोग करते हैं, तो आप इसका उपयोग कर सकते हैं $rootScope.$applyAsync()
।
इसके अलावा अगर aService.foo
यह बहुत बार अद्यतन किया जा रहा है, तो इससे सावधान रहें , क्योंकि यह प्रदर्शन को काफी प्रभावित कर सकता है। यदि प्रदर्शन एक समस्या होगी, तो आप सेटर का उपयोग करके अन्य उत्तरों के समान एक पर्यवेक्षक पैटर्न सेट कर सकते हैं।