【问题标题】:Running AngularJS initialization code when view is loaded加载视图时运行 AngularJS 初始化代码
【发布时间】:2013-04-22 14:59:13
【问题描述】:

当我加载视图时,我想在其关联的控制器中运行一些初始化代码。

为此,我在视图的主要元素上使用了 ng-init 指令:

<div ng-init="init()">
  blah
</div>

在控制器中:

$scope.init = function () {
    if ($routeParams.Id) {
        //get an existing object
        });
    } else {
       //create a new object
    }

    $scope.isSaving = false;
}

第一个问题:这是正确的做法吗?

接下来,我对发生的事件顺序有疑问。在视图中,我有一个“保存”按钮,它使用 ng-disabled 指令:

<button ng-click="save()" ng-disabled="isClean()">Save</button>

isClean() 函数在控制器中定义:

$scope.isClean = function () {
    return $scope.hasChanges() && !$scope.isSaving;
}

如您所见,它使用$scope.isSaving 标志,该标志在init() 函数中初始化。

问题:当视图加载时,isClean 函数在init() 函数之前 被调用,因此标志isSavingundefined。我能做些什么来防止这种情况发生?

【问题讨论】:

    标签: angularjs


    【解决方案1】:

    当您的视图加载时,其关联的控制器也会加载。无需使用ng-init,只需在控制器中调用init() 方法即可:

    $scope.init = function () {
        if ($routeParams.Id) {
            //get an existing object
        } else {
            //create a new object
        }
        $scope.isSaving = false;
    }
    ...
    $scope.init();
    

    由于您的控制器在ng-init 之前运行,这也解决了您的第二个问题。

    Fiddle


    正如John David Five 提到的,您可能不想将此附加到$scope 以使此方法私有。

    var init = function () {
        // do something
    }
    ...
    init();
    

    See jsFiddle


    如果您想等待某些数据被预设,要么将该数据请求移至解析,要么向该集合或对象添加一个观察者,并在您的数据满足您的初始化条件时调用您的 init 方法。一旦满足我的数据要求,我通常会删除观察者,因此如果您观察的数据发生变化并符合运行 init 方法的条件,init 函数不会随机重新运行。

    var init = function () {
        // do something
    }
    ...
    var unwatch = scope.$watch('myCollecitonOrObject', function(newVal, oldVal){
                        if( newVal && newVal.length > 0) {
                            unwatch();
                            init();
                        }
                    });
    

    【讨论】:

    • 如果您需要来自某些模型的数据来运行初始化怎么办?或者只是渲染时页面上可用的一些数据,以便初始化可以工作?
    • 初始化函数不需要附加到 $scope。将您的 init 函数设为私有。你永远不希望一个 init 函数运行不止一次,所以不要在 $scope 上暴露它。
    • 我想在我的视图显示时运行 init 函数,但我不知道如何,该函数只运行一次。有什么想法可以在每个页面/模板加载时运行它吗?
    • 我不是角度专家,但这种方法很不适合测试,因为 init() 只是在控制器实例化时调用......换句话说,当您需要测试控制器的单个方法时, init() 也被调用..打破测试!
    • @WagnerLeonardi 说了什么。这种方法使测试您的“私有”init() 方法变得非常困难。
    【解决方案2】:

    从 AngularJS 1.5 开始,我们应该使用$onInit,它在任何 AngularJS 组件上都可用。取自 component lifecycle documentation 自 v1.5 以来它的首选方式:

    $onInit() - 在一个控制器上的所有控制器之后在每个控制器上调用 元素已经被构造并初始化了它们的绑定(和 在此指令的前后链接功能之前 元素)。这是放置初始化代码的好地方 控制器。

    var myApp = angular.module('myApp',[]);
    myApp.controller('MyCtrl', function ($scope) {
    
        //default state
        $scope.name = '';
    
        //all your init controller goodness in here
        this.$onInit = function () {
          $scope.name = 'Superhero';
        }
    });
    

    >> Fiddle Demo


    使用组件生命周期的高级示例:

    组件生命周期使我们能够以良好的方式处理组件的内容。它允许我们为例如创建事件。组件的“初始化”、“更改”或“销毁”。通过这种方式,我们能够管理取决于组件生命周期的东西。这个小例子展示了注册和注销$rootScope 事件监听器$on。通过知道,绑定在$rootScope 上的事件$on 当控制器在视图中丢失其引用或被销毁时不会被取消,我们需要手动销毁$rootScope.$on 侦听器。放置这些东西的好地方是 $onDestroy 组件的生命周期函数:

    var myApp = angular.module('myApp',[]);
    
    myApp.controller('MyCtrl', function ($scope, $rootScope) {
    
      var registerScope = null;
    
      this.$onInit = function () {
        //register rootScope event
        registerScope = $rootScope.$on('someEvent', function(event) {
            console.log("fired");
        });
      }
    
      this.$onDestroy = function () {
        //unregister rootScope event by calling the return function
        registerScope();
      }
    });
    

    >> Fiddle demo

    【讨论】:

      【解决方案3】:

      或者你可以在控制器中初始化内联。如果您使用控制器内部的 init 函数,则不需要在作用域中定义它。事实上,它可以自我执行:

      function MyCtrl($scope) {
          $scope.isSaving = false;
      
          (function() {  // init
              if (true) { // $routeParams.Id) {
                  //get an existing object
              } else {
                  //create a new object
              }
          })()
      
          $scope.isClean = function () {
             return $scope.hasChanges() && !$scope.isSaving;
          }
      
          $scope.hasChanges = function() { return false }
      }
      

      【讨论】:

      • 初始化代码处于匿名闭包中是否有原因?
      • @AdamTolley 没有特别的原因。他只是定义了一个函数并立即调用它,而没有将它绑定到一个 var。
      • 你怎样才能以这种方式正确地对私有 init() 函数进行单元测试?
      • 只有公共成员才能进行单元测试。单元测试不应该依赖于私下做什么来获得预期的结果。
      【解决方案4】:

      我在我的项目中使用以下模板:

      angular.module("AppName.moduleName", [])
      
      /**
       * @ngdoc controller
       * @name  AppName.moduleName:ControllerNameController
       * @description Describe what the controller is responsible for.
       **/
          .controller("ControllerNameController", function (dependencies) {
      
              /* type */ $scope.modelName = null;
              /* type */ $scope.modelName.modelProperty1 = null;
              /* type */ $scope.modelName.modelPropertyX = null;
      
              /* type */ var privateVariable1 = null;
              /* type */ var privateVariableX = null;
      
              (function init() {
                  // load data, init scope, etc.
              })();
      
              $scope.modelName.publicFunction1 = function () /* -> type  */ {
                  // ...
              };
      
              $scope.modelName.publicFunctionX = function () /* -> type  */ {
                  // ...
              };
      
              function privateFunction1() /* -> type  */ {
                  // ...
              }
      
              function privateFunctionX() /* -> type  */ {
                  // ...
              }
      
          });
      

      【讨论】:

      • 这看起来很干净,但是 iffe 阻止您运行您在范围上定义的方法,这通常是我们需要做的,在启动时运行它们一次,然后让它们也打开能够根据用户需要再次运行它们的范围
      • 也就是说,如果它在控制器的顶部运行-
      猜你喜欢
      • 2016-12-17
      • 1970-01-01
      • 2012-12-27
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2012-10-10
      相关资源
      最近更新 更多