每当打开/显示视图时运行控制器函数。

81

我正在使用angular+ionic构建一个应用程序,它在底部使用传统的三个按钮菜单,其中包含三个ion-tabs。当用户点击选项卡时,该模板会通过ui-router打开。

我的状态如下:

$stateProvider
  .state('other', {
    url: "/other",
    abstract: true,
    templateUrl: "templates/other/other.html"
})
在模板中,我做了类似这样的事情:
<ion-nav-view name="other" ng-init="doSomething()"></ion-nav-view>

我知道我可以在控制器中编写doSomething()函数并在那里手动调用它。但这会导致同样的问题。我似乎无法想出如何在每次某人打开该视图时多次调用doSomething()函数。

目前,doSomething()函数能够正常调用,但仅在用户第一次打开该视图/标签时。我想在每次用户打开该视图或标签时调用一个函数(以更新地理位置)。

有没有正确的方法来实现这一点?


尝试执行 {{doSomething()}} ? - Asik
1
问题不在于doSomething(),因为它启动得很好,只是它不会触发两次。第一次打开视图时它可以正常运行,第二次似乎只是加载了缓存的视图或其他什么东西? - Jorre
ng-init 只调用一次函数。你可以在你的部分视图中调用 ng-controller="doSomething()" 或 {{doSomething()}},这样当路由器/部分被调用时,函数就会被触发。 - Asik
3
在你的配置状态中,将缓存设置为false。查看我的答案。 - Yakob Ubaidi
9个回答

87

默认情况下,您的控制器被缓存,这就是为什么您的控制器只会执行一次的原因。要关闭特定控制器的缓存,您需要修改.config(..).state并将cache选项设置为false。例如:

  .state('myApp', {
    cache: false,
    url: "/form",
    views: {
      'menuContent': {
        templateUrl: "templates/form.html",
        controller: 'formCtrl'
      }
    }
  })

欲了解更多信息,请访问http://ionicframework.com/docs/api/directive/ionNavView/


2
非常适用于注销控制器。 - mrwaim
3
默认情况下启用缓存功能是 Ionic 中的一个不幸之处。似乎应该将其作为选择性功能,而不是默认添加。 - oalbrecht
我只需要重新加载一个状态,所以我使用了:$state.go('app.statename', {}, {reload:true});更多信息来源)。 - Sjoerd Pottuit
1
虽然这样做可以工作,但并不是正确的方法。这会减慢您的应用程序速度 - 假设您不想每次用户切换到该视图时都要经过整个控制器初始化过程,我认为jczaplew的答案是更好的方法。 - Chris Rae
太棒了。我已经在整个应用程序中禁用了缓存。很高兴我找到了这个答案。 - Robert Ngetich
我正在尝试禁用聊天应用程序中的 cache: false,以便它不会加载聊天历史记录。是否需要更改代码以使其正常工作,并且每次都从 userList 和 chatting controller 初始化控制器。 - Pritish

50

在跟进AlexMart的回答和链接后,类似这样的东西是可行的:

.controller('MyCtrl', function($scope) {
  $scope.$on('$ionicView.enter', function() {
     // Code you want executed every time view is opened
     console.log('Opened!')
  })
})

3
$ionicView.beforeEnter 可能是您想要的。$ionicView.enter 会在视图完全进入后触发。如果您正在使用动画视图转换,则使用 $ionicView.beforeEnter 会在转换开始之前执行。这意味着您可以在转换期间运行代码,这可能会使您的应用程序感觉更加"顺畅"。 - rinogo
我认为这只是最简单和干净的答案。关于@rinogo的评论,我认为每个程序员可以根据视图/控制器中发生的任何事情决定使用“enter”还是“beforeEnter”。在这里需要理解的重要事情是,您可以处理生命周期事件以在每次进入视图时调用函数。 - jcarballo

46
如果您已将某个控制器分配给视图,那么每次加载视图时都会调用该控制器。在这种情况下,您可以在控制器被调用时立即执行一些代码,例如这样做:
如果您为视图指定了特定的控制器,则每次加载视图时都会调用该控制器。在这种情况下,您可以在控制器被调用时执行一些代码,例如以下方式:
<ion-nav-view ng-controller="indexController" name="other" ng-init="doSomething()"></ion-nav-view>

并且在您的控制器中:

app.controller('indexController', function($scope) {
    /*
        Write some code directly over here without any function,
        and it will be executed every time your view loads.
        Something like this:
    */
    $scope.xyz = 1;
});

编辑:您可以尝试跟踪状态变化,然后在路由更改并访问特定路由时执行某些代码,例如:

$rootScope.$on('$stateChangeSuccess', 
function(event, toState, toParams, fromState, fromParams){ ... })

你可以在这里找到更多细节:状态变更事件


6
谢谢,但这正是我正在做的事情。这段代码只会在视图第一次打开时运行一次。我尝试记录一些内容到我的控制台,它只会在第一次触发。 - Jorre
2
使用stateChange可以解决问题,只是点击选项卡仍然只运行一次。感谢编辑! - Jorre
这可能是最近ionic发布的问题。但是,您也可以在路由级别上禁用缓存。只需在路由配置中的每个路由中添加cache:false即可。 - Manish Kr. Shukla
doSomething()是什么?它在控制器代码中没有出现。 - Carcamano
@ManishKr.Shukla,如果我想测试您建议在控制器开头添加的代码,该怎么办? - Alok Mishra
显示剩余2条评论

12

我曾经面对过同样的问题,现在我将这种行为的原因留给所有遇到相同问题的人。

查看生命周期

为了提高性能,我们改进了Ionic缓存视图元素和作用域数据的能力。一旦控制器被初始化,它可能会在整个应用程序的生命周期内持续存在;它只是被隐藏并从监视周期中删除。由于我们不重新构建作用域,所以我们已经添加了事件,需要在重新进入监视周期时进行监听。

要查看完整说明和$ionicView事件,请转到: http://ionicframework.com/blog/navigating-the-changes/


9

为什么不使用cache-view="false"禁用视图缓存?

在您的视图中添加以下内容到ion-nav-view中:

<ion-nav-view name="other" cache-view="false"></ion-nav-view>

或者在您的stateProvider中:

$stateProvider.state('other', {
   cache: false,
   url : '/other',
   templateUrl : 'templates/other/other.html'
})

两种方法都会使您的控制器始终被调用。


这样做会不会影响性能?我想视图会被缓存,所以整个视图不会每次重新加载。禁用此功能不会导致整个页面重新加载,而我们只想再次执行一段代码? - Sindarus
很可能是的,@Sindarus。但有一些情况是必要的。对我来说,我有一个在视图上绘制的插件,我无法弄清楚如何清除它。 - Diogo Medeiros
这对我的情况来说是一个完美的解决方案,我注销了用户然后再次访问控制器,我不想缓存,一切都应该重新初始化。谢谢! - Firas Abd Alrahman

5
例如,对于@Michael Trouw,
在您的控制器中放置以下代码。 每次进入或激活该状态时都会运行此代码,您不需要担心禁用缓存,这是更好的方法。
.controller('exampleCtrl',function($scope){
$scope.$on('$ionicView.enter', function(){
        // Any thing you can think of
        alert("This function just ran away");   
    });
})

您可以看到更多的灵活性示例,例如 $ionicView.beforeEnter -> 它在视图显示之前运行。还有一些其他的灵活性。

2

考虑到Ionic在缓存视图元素和作用域数据方面的能力,如果您想每次加载视图时都运行控制器,这可能是另一种方法。您可以通过执行以下操作全局禁用Ionic使用的缓存机制:

$ionicConfigProvider.views.maxCache(0);

否则,我让它对我起作用的方法是这样做:

$scope.$on("$ionicView.afterLeave", function () {
     $ionicHistory.clearCache();
}); 

这是为了在您再次进入视图时重新运行控制器而清除缓存的方法。

1

1
可以通过展示示例代码而不仅仅是分享链接来改进。 - Lawrence Weru
AngularJS 中是否也有类似的东西? - Wang'l Pakhrin
默认情况下,您在主控制器函数中声明和运行的任何代码、IIFE或函数都将在每次加载视图(因此也是控制器)时执行。如果您需要像应用程序生命周期内只执行一次函数这样的事件,请查看angular-ui-router粘性状态以及ViewContentLoadingViewContentLoaded事件 :) - Michahell

0
我曾经在ionic中遇到类似的问题,当我尝试在选择相机选项卡后立即加载本地相机时。我通过将控制器设置为相机选项卡(在tabs.html中)的ion-view组件,然后调用加载我的相机(addImage)的$scope方法来解决了这个问题。
在www/templates/tabs.html中。
  <ion-tab title="Camera" icon-off="ion-camera" icon-on="ion-camera" href="#/tab/chats" ng-controller="AddMediaCtrl" ng-click="addImage()">
    <ion-nav-view  name="tab-chats"></ion-nav-view>
  </ion-tab>

addImage方法定义在AddMediaCtrl中,每次用户点击“相机”选项卡时都会加载本地相机。我并没有改变Angular缓存中的任何内容就可以让它正常运行。希望这能帮到你。


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