Angular ui-router instantiates the child controller multiple times

后端 未结 1 1418
天命终不由人
天命终不由人 2020-12-10 05:49

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 an

相关标签:
1条回答
  • 2020-12-10 06:24

    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

    0 讨论(0)
提交回复
热议问题