使用控制器初始化角度服务的正确方法

时间:2014-03-17 17:05:54

标签: javascript angularjs jquery-mobile

我来自一个静态类型的面向对象背景(C#),一般来说是Angular和Javascript的新手。我正在尝试使用Angular和JQueryMobile构建一个应用程序,并且面临的情况是服务不像单例一样 - 即使它们已经在一个控制器中初始化一次,服务中的属性也不存储它们所属的任何状态设置为传递到另一个控制器或服务时。此外,当我尝试调试下面描述的代码时,我遇到了一些意外的行为:

我的设置:

  • 我正在使用JQueryMobile单页模板,该应用程序将所有应用程序页面作为div放在同一个html页面上,并使用“href =”#DivName“pattern
  • 进行重定向
  • 应用的每个div(页面)都有一个关联的控制器和服务
  • 控制器根据需要注入多个服务,并且注入的服务应保留在其他控制器中初始化的状态。
  • 我没有使用路由和模板 - 整个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
        }
    };
});

控制器:

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

}]);

问题:

  • 在上面的serviceA代码中,如果我通过在返回块之前对其进行硬编码来初始化propA的值,则在执行getPropA()方法时会返回该值。请解释一下这是如何运作的。

  • 我还想知道在Angular中调用javascript代码的顺序 - 我的理解是角度运行时在浏览器中加载相关页面并且控制器代码依次调用时调用适当的控制器函数服务方式。但是,如果我在不同的控制器和/或服务中发出警报并加载页面,即使不应该调用页面或控制器/服务,也会立即显示警报。更奇怪的是,当执行它们的实际控制器或服务方法执行时,警报不会被执行。似乎所有控制器都在页面加载时执行。

请让我知道如何设置我的代码,这样我就可以在不同的控制器中传递服务,而不会在执行过程中的任何地方初始化或更新后丢失状态。

谢谢, 山塔努

修改

我认为答案就在于我的第二个问题。对我来说 - 执行代码的控制流程并不清楚。以下是我认为问题所在:

基本上,两个角度控制器都是在角度运行时在页面加载时执行,而不是在应用程序中加载和使用它们的顺序 - 尽管这可能只是一个JQMobile事件。

在用户界面操作上执行赋值操作时,将成功设置服务中的相应变量。但角度运行时不会重新评估所有控制器,以确保新分配反映在所有控制器上。

所以另一个控制器继续返回初始化时设置的内容 - undefined。

但是如果变量是在页面加载时自己设置的 - 那就是在初始化时使用所需的值进行返回的值,一切正常。

根据要求,我还在下面创建了一个简单的插件,以显示上述评论点: http://plnkr.co/edit/yBOL0P3ycsoNzVrydaMs

作为问题的解决方案,以下是我认为应该有效的方法:

  • 以某种方式确保控制器仅在需要时初始化,而不是在页面加载时立即初始化,以便初始化可以按照先前操作中设置的状态进行。我认为可以通过模板和路由实现,这样只有在所需的路由和模板被称为foe时才会调用控制器。我不确定这是否有效。

  • 第二个选项是在设置控制器中发布事件,并在需要的地方订阅它们以保持同步。这似乎是一种更好的方法。

如果有效,我会确认并发布答案。

请让我知道你的想法。

很抱歉这篇长篇文章:)

3 个答案:

答案 0 :(得分:4)

我不知道这是否能解决问题,但我认为您正在实例化该服务,就像它是一家工厂一样。

工厂回归和反对,就像你在做......

但服务应该利用&#34;这个&#34;类型语法:

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() {}


  });

服务与工厂的示例:http://jsfiddle.net/thomporter/zjFp4/1/

答案 1 :(得分:4)

这是javascript异步性质的一个例子。基本上所有内容都在页面加载时实例化,因此可能会填充或不填充值。 Angular为我们提供的一个很棒的解决方案是使用一个承诺 - $ q - 或者我们可以使用$ timeout来进行民意调查,但这只是草率的。

这是你的代码,有很棒的救援承诺!

首先指向plunker的链接:http://plnkr.co/edit/OCgL8jATTdScRbYubt9W

要查看的一些代码:

控制器:

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();

}]);

它们现在设置为在解决promises时处理其代码。

现在我们设定承诺:

服务:

ServiceA:

 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:

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);
            })

        }
    };
});

对于刚开始使用JS和/或Angular的人来说,这可能是一个非常令人困惑的问题,但是因为我们有这种令人敬畏的武器来对抗这种情况 - “THE $ q!” - 一旦我们学会正确使用它,我们的异步梦魇再次成为蓬松的云朵和蝴蝶。

答案 2 :(得分:1)

所以我知道这已经有一年了,但这是一个更容易实现的方法。

单例模式的角度服务实现:

app.service('serviceA', function () {

var privateProperty;

this.publicObject = {};

this.publicObject.sharedProperty = "Hardcoded value";

this.initialize = function(value){
  this.publicObject.sharedProperty = value; 
}

});

首先使用this.Service语法直接在原型上分配变量/函数,服务表现为“服务”。再加上感觉更有棱角!

其次,通过将属性移动到对象上,它可以维护对象的范围,以便多个控制器可以更改它,因为我相信Angular控制器按引用存储对象,按值存储特定的变量实例。

因此,如果您的控制器A包含:

$ scope.variableByValue = serviceA.publicObject.sharedProperty;

$ scope.variableByValue =“A的新值”

而不是在controllerB中:

alert(“serviceA.publicObject.sharedProperty”)//这不是“A的新值”

重组服务的人物:

http://plnkr.co/edit/3Ux4y37OtXjHsFLv6MSY?p=preview