javascript 使用控制器初始化角度服务的正确方法
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/22460716/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
Proper way to initialize angular services using the controllers
提问by Shantanu
I come from a static typed object oriented background (C#) and am new to Angular and Javascript in general. I am trying to build an app using Angular and JQueryMobile and am facing a situation where the services are not behaving like singletons - i.e. even though they have been initialised once in one controller, the properties within the service are not storing any state that they were set to when passed into another controller or service. Also, I am experiencing some unexpected behaviour when I try to debug the code which is described below:
我来自静态类型的面向对象背景 (C#),并且一般来说是 Angular 和 Javascript 的新手。我正在尝试使用 Angular 和 JQueryMobile 构建一个应用程序,但面临着服务行为不像单例的情况——即,即使它们已经在一个控制器中初始化一次,服务中的属性也没有存储它们的任何状态传递到另一个控制器或服务时设置为。此外,当我尝试调试下面描述的代码时,我遇到了一些意外行为:
My setup:
我的设置:
- I am using the JQueryMobile single page template having all the pages of the app as divs on the same html page and the redirection takes place using "href="#DivName" pattern
- Every div(page) of the app has an associated controller and service
- The controllers get injected with multiple services as per requirement and the injected services are expected to retain the state that they were initialised to in other controllers.
- I am not using routing and templates - the whole html is in a single page
- 我正在使用 JQueryMobile 单页模板,该模板将应用程序的所有页面作为同一 html 页面上的 div,并且使用“href="#DivName”模式进行重定向
- 应用程序的每个 div(page) 都有一个关联的控制器和服务
- 控制器根据需要注入多个服务,并且注入的服务应保持它们在其他控制器中初始化的状态。
- 我没有使用路由和模板 - 整个 html 都在一个页面中
The relevant code outline is below:
相关代码大纲如下:
HTML:
HTML:
<div data-role="page" id="PageA" data-ng-controller="PageAController">
<!-- page content and UI elements-->
</div>
<div data-role="page" id="PageB" data-ng-controller="PageBController">
<!-- page content and UI elements-->
</div>
SERVICES:
服务:
var App = angular.module('App', []);
App.service('serviceA', function () {
var propA;
//objectX is an array of objects with each object containing a hashtable and some other properties
propA = [{},{}]; //hardcoded value for objectX gets returned properly
return {
initialize: function(objectX){
propA = objectX; // Dynamically initialized objectX value is returned as "undefined" by the getPropA function below
},
getPropA: function () {
return propA;
}
};
});
App.service('serviceB', function (serviceA) {
var propB;
return {
initialize: function(){
propB = serviceA.getPropA(); //THIS DOES NOT WORK AS THE VALUE RETURNED IS UNDEFINED
},
processPropB: function () {
//logic for processing of propB
}
};
});
CONTROLLERS:
控制器:
App.controller('ControllerA', ['$scope', 'ServiceA',
function ($scope, ServiceA) {
$scope.UITriggeredAction = function(){
ServiceA.initialize(objectX);
};
}]);
//This controller gets invoked at a later point during the user interaction where ServiceA is supposed to be initialised and injected
App.controller('ControllerB', ['$scope', 'ServiceB', 'ServiceA',
function ($scope, ServiceB, ServiceA,) {
var returnedPropA = ServiceA.getPropA(); //THIS RETURNS UNDEFINED TOO
//process logic to use serviceB depending on valu of returnedPropA
}]);
QUESTIONS:
问题:
In the above code for serviceA, if I initialise the value for propA by hard coding it before the return block, the value does get returned when getPropA() method is executed. Please explain how this works.
I would also like to know the order of invoking the the javascript code in Angular - my understanding is that angular runtime invokes the appropriate controller's function when the relevant page is loaded in the browser and the controller code in turn invokes the service methods. But if I have alerts in different controllers and/or services and load the page, the alerts get displayed right away even though the page or controller/service was not supposed to be invoked. What is even more odd is that the alerts do not get executed when the actual controller or service method in which they were placed does get executed. It seems like all the controllers are getting executed on page load.
在上面的 serviceA 代码中,如果我在返回块之前通过硬编码来初始化 propA 的值,则在执行 getPropA() 方法时确实会返回该值。请解释这是如何工作的。
我还想知道在 Angular 中调用 javascript 代码的顺序——我的理解是,当相关页面加载到浏览器中时,Angular 运行时会调用适当的控制器函数,而控制器代码又会调用服务方法。但是如果我在不同的控制器和/或服务中有警报并加载页面,即使不应该调用页面或控制器/服务,警报也会立即显示。更奇怪的是,当放置警报的实际控制器或服务方法确实被执行时,警报并没有被执行。似乎所有控制器都在页面加载时执行。
Please let me know how to setup my code so that I can pass the services in different controllers without them losing their state once initialised or updated anywhere during the execution.
请让我知道如何设置我的代码,以便我可以在不同的控制器中传递服务,而不会在执行期间的任何地方初始化或更新后丢失它们的状态。
Thanks, Shantanu
谢谢,山塔努
EDIT:
编辑:
The answer I think lies right there in my second question. For me - the control flow for the execution of the code is not clear. Below is what I think the problem is:
我认为答案就在我的第二个问题中。对我来说 - 代码执行的控制流不清楚。以下是我认为的问题:
Basically, both the angular controllers are getting executed on page load by the angular runtime rather than in the sequence that they are loaded and used in the app - though this might just be a JQMobile thing.
基本上,两个 angular 控制器都是在 angular 运行时在页面加载时执行的,而不是按照它们在应用程序中加载和使用的顺序 - 尽管这可能只是 JQMobile 的事情。
When an assignment operation takes place on a user interface action, the corresponding variable in the service is set successfully. But the angular runtime does not re-evaluate all the controllers to ensure that the new assignment is reflected across all the controllers.
当用户界面动作发生赋值操作时,服务中对应的变量设置成功。但是 angular 运行时不会重新评估所有控制器以确保新分配反映在所有控制器上。
So the other controller continues to return what it was set to at initialization - undefined.
所以另一个控制器继续返回它在初始化时设置的内容 - 未定义。
But if the variable is set at page load itself - that is the value returned as the initialization takes place with the required value and everything works.
但是,如果该变量是在页面加载本身时设置的 - 那就是在使用所需值进行初始化时返回的值,并且一切正常。
As requested I have also created below a simple plunk to show the above points with comments: http://plnkr.co/edit/yBOL0P3ycsoNzVrydaMs
根据要求,我还在下面创建了一个简单的 plunk 来显示上述要点和评论:http: //plnkr.co/edit/yBOL0P3ycsoNzVrydaMs
As a solution to the problem, below is what I think should work:
作为问题的解决方案,以下是我认为应该起作用的方法:
Somehow ensure that the controllers are initialized only when required rather than all at once on page load so that the initialization can take place as per the state set in the previous actions. I think can be achieved through templates and routing so that the controllers are invoked only when the required route and template is called foe. I'm not sure if this is the way it works.
The second option is to publish events in setting controllers and subscribe to them wherever required to keep things in sync. This seems like a better approach.
以某种方式确保控制器仅在需要时初始化,而不是在页面加载时一次性初始化,以便初始化可以按照先前操作中设置的状态进行。我认为可以通过模板和路由来实现,这样控制器仅在所需的路由和模板被称为 foe 时才被调用。我不确定这是否是它的工作方式。
第二个选项是在设置控制器中发布事件并在需要保持同步的任何地方订阅它们。这似乎是一种更好的方法。
I will confirm this and post an answer if it works.
如果可行,我会确认并发布答案。
Please let me know your thoughts.
请让我知道你的想法。
Sorry for the long post :)
抱歉,帖子太长了:)
采纳答案by Sten Muchow
This is an example of the asynchronous nature of javascript. Basically everything is instantiated on page load so the value may or may not be populated. A awesome solution that Angular provides for us is to use a promise - $q - or we could use $timeout to poll but thats just sloppy.
这是 javascript 异步特性的一个例子。基本上所有内容都在页面加载时实例化,因此可能会或可能不会填充该值。Angular 为我们提供的一个很棒的解决方案是使用 promise - $q - 或者我们可以使用 $timeout 进行轮询,但这只是草率。
Here is your code with the awesome promise to the rescue!
这是您的代码,其中包含令人敬畏的救援承诺!
First the link to the plunker: http://plnkr.co/edit/OCgL8jATTdScRbYubt9W
首先是plunker的链接:http://plnkr.co/edit/OCgL8jATTdScRbYubt9W
Some code to look at:
要查看的一些代码:
Controllers:
控制器:
var app = angular.module('plunker', []);
app.controller('ControllerA', ['$scope','serviceA',function($scope, serviceA) {
$scope.initializeA = function(){
serviceA.initialize("Chris");
};
}]);
app.controller('ControllerB', ['$scope', 'serviceB', 'serviceA', function($scope, serviceB, serviceA) {
$scope.initializeB = function(){
serviceA.getPropA().then(function(data) {
alert("ControllerB : " + data);
});
serviceB.initialize();
serviceB.processPropB();
};
/////////////////////////////
//Controller Initialization//
/////////////////////////////
serviceA.getPropA().then(function(data) {
alert('now i have the data: ' + data)
});
serviceB.initialize();
serviceB.processPropB();
}]);
They are now set up to process their code when promises are resolved.
他们现在设置为在解决承诺时处理他们的代码。
Now where we set the promises up:
现在我们设置承诺的地方:
Services:
服务:
ServiceA:
服务A:
app.service('serviceA', function ($q) {
var
propA,
deferred = $q.defer()
;
return {
initialize: function(name){
propA = name;
deferred.resolve(propA);
},
getPropA: function () {
return deferred.promise;
}
};
});
ServiceB:
服务B:
app.service('serviceB', function (serviceA, $q) {
var
propB,
deferred = $q.defer()
;
return {
initialize: function(){
serviceA.getPropA().then(function(data) {
propB = data;
deferred.resolve();
});
},
processPropB: function () {
deferred.promise.then(function() {
alert("ServiceB: " + propB);
})
}
};
});
This can be a really confusing issue to people starting out with JS and or Angular, but as we have this awesome weapon against such cases - "THE $q!" - once we learn to wield it properly our asynchronous nightmares once again become fluffy clouds and butterflies.
对于刚开始使用 JS 和/或 Angular 的人来说,这可能是一个非常令人困惑的问题,但是由于我们拥有针对此类情况的强大武器 - “THE $q!” - 一旦我们学会正确使用它,我们的异步噩梦将再次变成蓬松的云彩和蝴蝶。
回答by timh
I dont know if this solves the problem, but I think you are instantiating the service, as if it is a factory.
我不知道这是否解决了问题,但我认为您正在实例化服务,就好像它是一个工厂。
Factories return and object, like you are doing...
工厂返回并反对,就像您正在做的那样......
But services should utilize "this" type syntax:
但是服务应该使用“this”类型的语法:
App.service('serviceA', function () {
var somePrivateProperty;
this.propA = [{},{}]; // public property
this.initalize = function(objectX) {
this.propA = objectX
}
this.getPropA = function() { return this.propA }
// although, your controller which Injects this service,
// could access propA directly without the getter
var somePrivateMethod = function() {}
});
Example of services vs factories: http://jsfiddle.net/thomporter/zjFp4/1/
服务与工厂的示例:http: //jsfiddle.net/thomporter/zjFp4/1/
回答by Stefan Bradstreet
So I know this is a year old but here is an easier implementation.
所以我知道这是一岁了,但这里有一个更简单的实现。
Angular Service implementation for singleton pattern:
单例模式的 Angular 服务实现:
app.service('serviceA', function () {
var privateProperty;
this.publicObject = {};
this.publicObject.sharedProperty = "Hardcoded value";
this.initialize = function(value){
this.publicObject.sharedProperty = value;
}
});
First by using the this.Service syntax you assign the variables/functions directly on the prototype and the service behaves as a 'service'. Plus it feels more angular!
首先,通过使用 this.Service 语法,您可以直接在原型上分配变量/函数,并且服务表现为“服务”。而且感觉更有棱角!
Second by moving the property onto an object it maintains the scope of the object so that it can be altered by multiple controllers since i believe the Angular controllers store objects by reference and specific variable instances by value.
其次,通过将属性移动到一个对象上,它维护了对象的范围,以便它可以被多个控制器更改,因为我相信 Angular 控制器通过引用存储对象,并通过值存储特定变量实例。
So if your controllerA contained:
因此,如果您的控制器 A 包含:
$scope.variableByValue = serviceA.publicObject.sharedProperty;
$scope.variableByValue = serviceA.publicObject.sharedProperty;
$scope.variableByValue = "New value from A"
$scope.variableByValue = "来自 A 的新值"
than in controllerB:
比控制器B:
alert("serviceA.publicObject.sharedProperty") //this would NOT be "New value from A"
alert("serviceA.publicObject.sharedProperty") //这不会是“来自A的新值”
Plunker of the restructured service:
重组服务的Plunker: