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:
ProfileDetailsCtrl
is instantiated 3 times. How could I instantiate it only for profile that is extended?PLAYGROUND HERE
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:
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 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
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)
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
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With