Angular ui-router 多次实例化子控制器 [英] Angular ui-router instantiates the child controller multiple times

查看:24
本文介绍了Angular ui-router 多次实例化子控制器的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

甜蜜演示

我有一个配置文件列表,其中每个配置文件可以是摘要视图,也可以是详细视图.任何时候只有一个个人资料可以有详细视图.

profiles.html

<div ui-view="profile-summary"></div><div ui-view="profile-details"></div>

profile-summary.html

{{ profile.name }}<button ng-click="showProfileDetails(profile.id)">更多</button>

profile-summary-controller.js

$scope.showProfileDetails = function(profileId) {$state.go('profiles.profile', { profileId: profileId });};

个人资料详细信息 .html

{{ profile.name }} 的详细资料<button ng-click="hideProfileDetails()">少</button>

profile-details-controller.js

$scope.hideProfileDetails = function() {$state.go('profiles.profile', { profileId: null });};

ui-router 配置

$stateProvider.state('个人资料', {url: '/profiles?keywords',意见:{'个人资料':{templateUrl: 'profiles.html',控制器:'ProfilesCtrl'},'profile-summary@profiles':{templateUrl: 'profile-summary.html',控制器:'ProfileSummaryCtrl'}}}).state('profiles.profile', {url: '/:profileId',意见:{'个人资料详细信息':{templateUrl: 'profile-details.html',控制器:'ProfileDetailsCtrl'}}});

我的问题:

  • More按钮被点击时,ProfileDetailsCtrl被实例化3次.我怎么能只为扩展的配置文件实例化它?
  • 我是否正确利用了 ui-router 的灵活性,或者有更好的方法来实现这一点?(注意:当个人资料被展开时,它应该反映在 URL 中(以使其可收藏)

在这里玩耍

解决方案

扩展细节替换

在下面的评论中讨论后,对于基本的有一个不同的解决方案>(必需) 概念:

<块引用>

在列表中 - 如何替换一行 (点击),更多细节 - 使用 ui-router?

即让我们从这个开始:

  • 更多 1 基本信息 1
  • 更多 2 基本信息 2//点击这里更多
  • 更多 3 基本信息 2

在点击 More 2 时得到这个 (如果接下来点击 Less 2 就会返回)

  • 更多 1 基本信息 1
  • Less 2 ui-router 状态机加载的非常详细的信息...
  • 更多 3 基本信息 2

这将是解决方案:

1) 列表视图模板,将有 ng-if,检查是否有一些 detail 信息:

<div ng-if="!profile.detail"><button ui-sref=".profile({profileId:profile.id})">更多</button>{{ profile.name }}

<div ng-if="profile.detail"><button ui-sref=".">少</button>{{ profile.detail |json }}

一些有趣的部分要提到:我们只是使用内置的 ui-sref 和相对路径 def ui- 而不是一些 ng-clicksref=".profile({profileId:profile.id})" - 这将调用子状态 profile.

一旦孩子被加载,我们可以通过重新调用父ui-sref="." (wow...)

2) 我们的 detail 状态将做两件事

  1. 通过 ID 加载详细信息//GetById 在服务器上调用
  2. 休假清理//恢复列表原样

    //从父集合中查找配置文件var profile = _.find($scope.profiles, {id : $stateParams.profileId});

    $http.get("detail.json")//getById.then(函数(响应){

    //将只包含 id 的详细信息...这里我们过滤var detail = _.find(response.data, {id : $stateParams.profileId});//分配细节profile.detail = 详细信息;});

    //清理 - 删除细节var 清理 = 函数(){删除 profile.detail;}$scope.$on("$destroy", 清理);

有几点需要提及:我们钩住了 $scope 事件destroy".一旦我们回到父级,这将被解雇.这就是我们清理 ui-router detail 状态往返过程中所有足迹的地方...

3) 详细视图

没有.没有,因为我们不需要模板.好吧,实际上我们仍然需要视图锚点,详细信息状态放在哪里...并且DetailController 被调用!

 .state('profiles.profile', {url: '/:profileId',意见:{'profile-details': {//没有模板控制器:'ProfileDetailsCtrl'}}});

所以父级中的某处必须有视图锚点:

<div ui-view="profile-details"></div>

工作代码示例:

看看那个解决方案这里...应该是清除

(以下是为什么多次触发控制器的答案的原始部分)

答案的原始部分

控制器被实例化的次数与其视图被注入页面的次数一样多.你确实注入了视图3次.

这里是个人资料的来源

$scope.profiles = [{ id: '100', name: 'Misko Hevery' },{ id: '101', name: 'Igor Minar' },{ id: '102', name: 'Vojta Jina' },];

这里我们创建了具有相同 ui-view 名称的锚点/目标:

//对于我们数组中的每个配置文件<div ui-view="profile-summary"></div><div ui-view="profile-details"></div>//我们注入这个名为 profile-details 的视图

最后,我们要求将我们的视图注入这(3)个父/子视图目标:

.state('profiles.profile', {url: '/:profileId',意见:{'profile-details': {//父视图目标出现 3 次templateUrl: 'profile-details.html',控制器:'ProfileDetailsCtrl'}}});

解决方案:这不应该发生.我们不应多次使用一个 ui-view="viewName".这是工作.但这不是我们可以正确管理的...只需从中继器中移动目标...

在这里扩展我更新了plunker,我把profiles.html做成这样

//这里没有中继器<div ui-view="profile-summary"></div><div ui-view="profile-details"></div>

我在摘要中进行迭代:

{{ profile.name }}<button ng-click="showProfileDetails(profile.id)">更多</button>

所以现在,每个 ui-view只有一次...看到它在行动 这里

SWEET DEMO

I have a list of profiles, where each profile can be either in a summary or in a detailed view. Only one profile can have a detailed view at any time.

profiles.html

<div ng-repeat="profile in profiles">
  <div ui-view="profile-summary"></div>
  <div ui-view="profile-details"></div>
</div>

profile-summary.html

<div ng-if="$state.params.profileId !== profile.id">
  {{ profile.name }}
  <button ng-click="showProfileDetails(profile.id)">More</button>
</div>

profile-summary-controller.js

$scope.showProfileDetails = function(profileId) {
  $state.go('profiles.profile', { profileId: profileId });
};

profile-details .html

<div ng-if="$state.params.profileId === profile.id">
  Detailed profile of {{ profile.name }}
  <button ng-click="hideProfileDetails()">Less</button>
</div>

profile-details-controller.js

$scope.hideProfileDetails = function() {
  $state.go('profiles.profile', { profileId: null });
};

ui-router configuration

$stateProvider
  .state('profiles', {
    url: '/profiles?keywords',
    views: {
      'profiles': {
        templateUrl: 'profiles.html',
        controller: 'ProfilesCtrl'
      },
      'profile-summary@profiles': {
        templateUrl: 'profile-summary.html',
        controller: 'ProfileSummaryCtrl'
      }
    }
  })
  .state('profiles.profile', {
    url: '/:profileId',
    views: {
      'profile-details': {
        templateUrl: 'profile-details.html',
        controller: 'ProfileDetailsCtrl'
      }
    }
  });

Questions I have:

  • When the More button is clicked, ProfileDetailsCtrl is instantiated 3 times. How could I instantiate it only for profile that is extended?
  • Am I utilizing the ui-router flexibility properly, or there is a better way to implement this? (Note: When profile is expanded, it should be reflected in the URL (to make it bookmarkable))

PLAYGROUND HERE

解决方案

Extend with detail Replace

After discussion in comments below, there is a different solution for the basic (required) concept:

Inside a list - How to replace a row (on-click), with more details - using ui-router?

I.e. let's start with this:

  • More 1 basic info1
  • More 2 basic info2 // click more here
  • More 3 basic info2

getting this when More 2 is clicked (ang getting back if Less 2 is clicked next)

  • More 1 basic info1
  • Less 2 very detailed information loaded with ui-router state machine...
  • More 3 basic info2

This would be the solution:

1) The list view template, would have ng-if, checking if there is some detail info, or not:

<div ng-repeat="profile in profiles">

  <div ng-if="!profile.detail">
    <button ui-sref=".profile({profileId:profile.id})">More</button>
    {{ profile.name }}
  </div>

  <div ng-if="profile.detail">        
    <button ui-sref=".">Less</button>
    {{ profile.detail | json }}
  </div>

</div>

A few fancy parts to mention: instead of some ng-click we just do use the built in ui-sref with a relative path def ui-sref=".profile({profileId:profile.id})" - That will call the child state profile.

Once child is loaded, we can just get back by re-calling the parent ui-sref="." (wow...)

2) Our detail state will be doing two things

  1. load the detail by ID // GetById called on a Server
  2. cleanup on leave // Restoring the list as it was

    // find a profile from parent collection var profile = _.find($scope.profiles, {id : $stateParams.profileId});

    $http .get("detail.json") // getById .then(function(response){

      // would contain just a detail for id... here we filter
      var detail = _.find(response.data, {id : $stateParams.profileId});
    
      // assign the detail
      profile.detail = detail;
    
    });
    

    // cleanup - remove the details var cleanup = function(){ delete profile.detail; } $scope.$on("$destroy", cleanup);

A few things to mention: we hook on the $scope event "destroy". This will be fired, once we go back to the parent. And that's the place where we clean all the foot prints made during the ui-router detail state round-trip...

3) the detail view

There is NONE. None, becuase we do not need a template. Well in fact we still need the view anchor, where is the detail state placed... and the DetailController is called!

  .state('profiles.profile', {
    url: '/:profileId',
    views: {
      'profile-details': {   // NO template
        controller: 'ProfileDetailsCtrl'
      }
    }
  });

so there must be the view anchor somewhere in the parent:

<div ui-view="profile-details"></div>

Working code example:

Take a look a that solution here... it should be clear

(below is the original part of the answer why multiple times fired controller)

Original part of the answer

The controller is instantiated as many times, as many times is its view injected into the page. And you do inject the view 3 times.

Here is the source of the profiles

$scope.profiles = [
    { id: '100', name: 'Misko Hevery' },
    { id: '101', name: 'Igor Minar' },
    { id: '102', name: 'Vojta Jina' },
];

Here we do create anchors/targets with the same ui-view name:

<div ng-repeat="profile in profiles">   // for each profile in our array
  <div ui-view="profile-summary"></div>
  <div ui-view="profile-details"></div> // we inject this view named profile-details
</div>

And finally, we ask to inject our view into these (3) parent/child view-targets:

.state('profiles.profile', {
    url: '/:profileId',
    views: {
      'profile-details': {                 // the parent view-target is there 3 times
        templateUrl: 'profile-details.html',
        controller: 'ProfileDetailsCtrl'
      }
    }
  });

Solution: this should not happen. We should not use one ui-view="viewName" moret than once. It is working. but it is not what we can correctly manage... simply move the targets from repeater...

EXTEND here I updated the plunker, I made the profiles.html like this

// NO repeater here
<div ui-view="profile-summary"></div>
<div ui-view="profile-details"></div>

And I do iterate inside fo the summary:

<div ng-repeat="profile in profiles">
  {{ profile.name }}
  <button ng-click="showProfileDetails(profile.id)">More</button>
</div>

So now, each ui-view is there only once... see that in action here

这篇关于Angular ui-router 多次实例化子控制器的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
相关文章
其他开发最新文章
热门教程
热门工具
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆