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))
Extend with detail Replace
After discussion in comments below, there is a different solution for the basic (required) concept:
I.e. let's start with this:
getting this when More 2 is clicked (ang getting back if Less 2 is clicked next)
This would be the solution:
1) The list view template, would have
ng-if
, checking if there is somedetail
info, or not:A few fancy parts to mention: instead of some
ng-click
we just do use the built inui-sref
with a relative path defui-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 thingscleanup 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){
// 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 theui-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!so there must be the view anchor somewhere in the parent:
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
Here we do create anchors/targets with the same
ui-view
name:And finally, we ask to inject our view into these (3) parent/child view-targets:
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
And I do iterate inside fo the summary:
So now, each
ui-view
is there only once... see that in action here