加载视图时运行AngularJS初始化代码


92

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

为此,我在视图的主要元素上使用了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.isSavinginit()函数中初始化的标志。

问题:加载视图时,isClean函数init()函数之前被调用,因此标记isSavingundefined。我该怎么做才能防止这种情况?

Answers:


136

加载视图时,其关联的控制器也会加载。无需使用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,这也解决了您的第二个问题。

小提琴


正如John David Five所提到的,你可能不希望这种重视$scope,为了使这个方法私有。

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

见jsFiddle


如果要等待预先设置某些数据,则可以将该数据请求移至解决方案,或者将监视程序添加到该集合或对象,然后在数据满足初始化条件时调用init方法。通常,一旦满足我的数据要求,我就会删除观察者,因此,如果您观察的数据发生更改并且符合运行init方法的条件,则init函数不会随机重新运行。

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

8
如果您需要某些模型的数据来运行初始化该怎么办?还是在渲染时页面上仅提供一些数据,以便可以进行初始化?
尤金

38
不需要将初始化函数附加到$ scope。将您的初始化函数设为私有。您永远不希望一个init函数运行一次以上,因此不要在$ scope上公开它。
约翰·大卫

2
我想在显示视图时随时运行init函数,但我不知道如何运行,该函数仅运行一次。有什么想法可以在每个页面/模板加载时运行它吗?
Jorre 2014年

9
我不是熟练的专家,但是这种方法很麻烦,因为init()是在简单的控制器实例化时调用的……换句话说,当您需要测试单个控制器方法时,也会调用init()。打破测试!
瓦格纳·伦纳迪

1
@WagnerLeonardi说什么。这种方法使得测试您的“私有” init()方法非常困难。
史蒂文·罗杰斯

36

从AngularJS 1.5开始,我们应该使用$onInit任何AngularJS组件都可以使用的工具。从v1.5以来的组件生命周期文档中获取以下方法:

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

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

>> 小提琴演示


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

组件生命周期使我们能够很好地处理组件。它允许我们为组件的“初始化”,“更改”或“破坏”创建事件。这样,我们就可以管理取决于组件生命周期的东西。这个小例子显示了注册和注销$rootScope事件监听器$on。知道,当控制器在视图中丢失其引用或被销毁时,$on绑定的事件$rootScope将不会被取消引用,我们需要$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();
  }
});

>> 小提琴演示


17

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

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

1
初始化代码是否在匿名闭包中存在原因?
亚当·托利

@AdamTolley没有特别的原因。他只是定义一个函数并立即调用它,而没有将其绑定到var。
Tair 2015年

7
您如何以这种方式正确地测试私有init()函数?
史蒂芬·罗杰斯

仅公共成员经过单元测试。单元测试不应该依赖于哪些类私下进行以获得预期的结果。
菲尔(Phil)

14

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

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阻止您运行在范围上定义的方法,这通常是我们需要做的,在启动时运行一次,然后在范围上也可以运行它们再次根据用户需要
chrismarx

也就是说,如果它运行在控制器的顶部,
chrismarx 2015年
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.