如何在页面加载时执行AngularJS控制器函数?

370

目前我有一个Angular.js页面,该页面允许搜索并显示结果。用户点击搜索结果,然后点击返回按钮。我希望搜索结果再次显示,但我无法找出如何触发搜索执行的方法。以下是详细信息:

  • 我的Angular.js页面是一个搜索页面,带有搜索字段和搜索按钮。用户可以手动键入查询并按下按钮,ajax查询将被触发并显示结果。我使用搜索词更新URL。这一切都很好。
  • 用户单击搜索结果并转到另一页-这也很好。
  • 用户单击返回按钮,返回到我的Angular搜索页面,并显示正确的URL,包括搜索词。所有操作正常。
  • 我将搜索字段值绑定到URL中的搜索词,因此它包含预期的搜索词。所有操作正常。

如何在不让用户按下“搜索”按钮的情况下重新执行搜索功能?如果使用jQuery,则可以在documentready函数中执行函数。我看不到Angular.js的等效方法。


你是否在使用 $routeProvider?使用它连接到应用程序中提供搜索结果数据的服务。不要像使用 jQuery 时那样考虑 document.ready 的术语。如果没有看到你目前如何连接搜索,很难提供帮助。 - charlietfl
14个回答

438

一方面,如@Mark-Rajcok所说,您可以只使用私有内部函数:

// at the bottom of your controller
var init = function () {
   // check if there is query in url
   // and fire search in case its value is not empty
};
// and fire it after definition
init();

您还可以查看ng-init指令。实现方式将类似于:

// register controller in html
<div data-ng-controller="myCtrl" data-ng-init="init()"></div>

// in controller
$scope.init = function () {
    // check if there is query in url
    // and fire search in case its value is not empty
};

但是要注意不要像angular文档(自1.2版本以来)所暗示的那样使用ng-init,但是在我看来,这取决于您应用程序的架构。

当我想将后端的值传递到angular应用程序中时,我使用了ng-init

<div data-ng-controller="myCtrl" data-ng-init="init('%some_backend_value%')"></div>

6
@Duke,你能否将init()函数的内容放在控制器底部?也就是说,为什么需要使用ng-init并有一个init()函数呢?当用户点击返回按钮时,我假设你正在切换视图,因此会创建并执行一个新的myCtrl控制器。 - Mark Rajcok
25
尽管这个解决方案是可行的,但我投了反对票,因为 ng-init 的文档建议不要这样做。具体来说,“ngInit 的唯一适当用法是为了给 ngRepeat 的特殊属性起别名,就像下面的演示那样。除了这种情况,你应该使用控制器而不是 ngInit 在作用域中初始化值。” - Jason Capriotti
1
控制器在 HTML 就位之后实例化。 - Dmitry Evseev
3
如果控制器被重用在其他页面上,但你只想在特定页面上触发它,该怎么办? - Roberto Linares
3
我个人认为指令是重复使用功能的更好位置。你可以使用带有初始化参数的指令:<div smth on-init="doWhatever()">。当然,这取决于特定的用例。 - Dmitry Evseev
显示剩余4条评论

135

试试这个?

$scope.$on('$viewContentLoaded', function() {
    //call it here
});

5
谢谢回复,但是Dmitry的答案完全符合我的要求。进一步的研究似乎不建议使用$viewContentLoaded。 - Duke Dougal
7
用例只是不同而已。我也一直使用马克的方法。 - holographic-principle
对于我的使用情况来说,这是一个完美的解决方案。我们需要自动选择文本以鼓励用户复制它。页面可能会被重新加载/访问多次,因此$viewContentLoaded是一个完美的匹配。谢谢! - Olga Gnatenko
这个是否可能被执行多次?我得到了一种似乎在模仿那样的行为... - MadPhysicist
1
这个对我没用,但是 $scope.$watch 起作用了。主要区别是什么? - Burak Tokak

60

我一直无法让$viewContentLoaded起作用,而ng-init应该只在ng-repeat中使用(根据文档),并且在控制器中直接调用函数可能会导致错误,如果代码依赖于尚未定义的元素。

这是我做的,它对我有效:

$scope.$on('$routeChangeSuccess', function () {
  // do something
});

除非你正在使用ui-router,否则它应该是这样的:

$scope.$on('$stateChangeSuccess', function () {
  // do something
});

1
你很棒,我的朋友。 - taco
正好是我需要的。我花了两天时间找这个! - hellojebus
3
每当URL参数发生变化时(例如通过$location),$scope.$on('$routeChangeSuccess'会被触发,因此你可以只使用$scope.$on('$locationChangeSuccess'。如果你需要在页面加载/重新加载时触发某些操作,可以像这里建议的那样使用$scope.$watch('$viewContentLoaded'。它不会在URL参数更改期间触发。 - Neurotransmitter
使用AngularJS 1.17.2,在控制器内部*$routeChangeSuccess*表现出色... adam0101万岁... - ArifMustafa

48
angular.element(document).ready(function () {

    // your code here

});

4
必须将其选择为采纳答案,这是更安全的做法。 - Marwen Trabelsi
如果我们不使用$scope,而是使用this,那么这很好。 - Aakash
这需要放在哪里?我已经把它放在我的模板中,但它没有触发。 - Dave
在这种情况下注入$document不是最好的选择吗? angular.element($document).ready... - jamie
5
解释是必要的 - Hidayt Rahman

32

Dimitri/Mark的解决方案对我无效,但是使用$timeout函数似乎可以很好地确保你的代码只在标记渲染后运行。

# Your controller, including $timeout

var $scope.init = function(){
 //your code
}

$timeout($scope.init)

希望它有所帮助。


3
最终,这是唯一对我有效的。谢谢。 - zmirc
1
这个可以工作。另外,在初始化完成后,不要忘记使用$timeout.cancel(timer)清除超时,其中timer = $timeout($scope.init, milliseconds)。此外,我认为在您上面的代码中,$scope不应该有'var'定义。 - Emmanuel N K
这应该是被接受的答案。这是唯一一个对我有效的答案(尝试了上面的所有答案)。它甚至在你等待 iframe 加载时也可以工作。 - ph_0

28

如果你想要观察viewContentLoaded DOM对象的变化并且在变化后执行某些操作,可以这样做。使用$scope.$on也可以,但是当你的路由启用了单页模式时,会有所不同。

 $scope.$watch('$viewContentLoaded', function(){
    // do something
 });

7
具体而言,如果您使用 $rootScope.$watch 而不是 $rootScope.$on,它不会在路由更改时触发,因此您可以使用特定于初始页面加载的逻辑。 - csahlman

20
您可以使用Angular的$window对象:
$window.onload = function(e) {
  //your magic here
}

4

3
使用$routeProvider时,您可以在.state上进行resolve并引导您的服务。也就是说,在解析您的服务后,您将加载控制器和视图: ui-routes
 .state('nn', {
        url: "/nn",
        templateUrl: "views/home/n.html",
        controller: 'nnCtrl',
        resolve: {
          initialised: function (ourBootstrapService, $q) {

            var deferred = $q.defer();

            ourBootstrapService.init().then(function(initialised) {
              deferred.resolve(initialised);
            });
            return deferred.promise;
          }
        }
      })

服务

function ourBootstrapService() {

 function init(){ 
    // this is what we need
 }
}

3

我发现Dmitry Evseev的回答非常有用。

情况1:仅使用angularJs:
要在页面加载时执行方法,您可以在视图中使用ng-init并在控制器中声明init方法。但是,根据Angular Docs on ng-init的说法,不建议使用较重的功能:

这个指令可能被滥用,向您的模板添加不必要的逻辑。只有少数几种适当的使用ngInit,例如为ngRepeat的特殊属性设置别名,如下面的演示所示;以及通过服务器端脚本注入数据。除了这些少数情况之外,您应该使用控制器而不是ngInit在作用域上初始化值。

HTML:

<div ng-controller="searchController()">
    <!-- renaming view code here, including the search box and the buttons -->
</div>

控制器:

app.controller('SearchCtrl', function(){

    var doSearch = function(keyword){
        //Search code here
    }

    doSearch($routeParams.searchKeyword);
})

警告: 不要将此控制器用于其他意图的视图,因为这会导致搜索方法在那里执行。

案例2:使用Ionic:
上述代码将起作用,只需确保在route.js中禁用视图缓存:

route.js

.state('app', {
    url           : '/search',
    cache         : false, //disable caching of the view here
    templateUrl   : 'templates/search.html'   ,
    controller    : 'SearchCtrl'
  })

希望这可以帮到你。

1
这个只有一个赞,而且在最底下,很高兴我一直往下看,cache: false 对我很有帮助 - 谢谢! - Rani Kheir

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接