Issue with the Angular UI router when inheriting from a parent state - angularjs

I have an issue with my Angular UI router configuration.
I have an abstract authenticated state that all of my authenticated pages inherit from. Furthermore, I have a dashboard state that tries to inherit from this authenticated state using the parent: 'authenticated' notation.
Here is my configuration:
.state('authenticated', {
abstract: true,
resolve: {
currentMember: ['$rootScope', '$q', function ($rootScope, $q) {
return $rootScope.authenticated || $q.reject({unAuthorized: true});
}]
}
})
.state('dashboard', {
url: '/dashboard',
controller: 'DashboardCtrl',
templateUrl: 'dashboard/views/dashboard.view.html',
parent: 'authenticated'
})
However, using the above configuration, my dashboard page is always empty...
If I comment out the parent property as follows:
//parent: 'authenticated'
..then the dashboard view is populated with its contents properly...
Can anyone please help?

What we need here is a target for our child view, we need a setting: template: "<ui-view />" in the parent state definition:
.state('authenticated', {
abstract: true,
template: "<ui-view />", // here
resolve: {
currentMember: ['$rootScope', '$q', function ($rootScope, $q) {
return $rootScope.authenticated || $q.reject({unAuthorized: true});
}]
}
})
This template: "<ui-view />", piece of code is now doing essential part of our state machine: it creates target for our child .state('dashboard'.... That state will now be placed (its unnamed view) into that parent target.
The reason why it was working, when we commented out the parent: setting was:
our view of the state 'dashboard' was injected into index.html target <div ui-view=""> - (index.html is so called root state).
The templatce could also be like '<div ui-view=""></div>'. I just used simplified expression resulting in the same behaviour...
To get more ideas about that all, please, check:
View Names - Relative vs. Absolute Names
Behind the scenes, every view gets assigned an absolute name that follows a scheme of viewname#statename, where viewname is the name used in the view directive and state name is the state's absolute name, e.g. contact.item. You can also choose to write your view names in the absolute syntax.
...
code snippet cite:
.state('contacts.detail', {
views: {
////////////////////////////////////
// Relative Targeting //
// Targets parent state ui-view's //
////////////////////////////////////
// Relatively targets the 'detail' view in this state's parent state, 'contacts'.
// <div ui-view='detail'/> within contacts.html
"detail" : { },
// Relatively targets the unnamed view in this state's parent state, 'contacts'.
// <div ui-view/> within contacts.html
"" : { },
///////////////////////////////////////////////////////
// Absolute Targeting using '#' //
// Targets any view within this state or an ancestor //
///////////////////////////////////////////////////////
// Absolutely targets the 'info' view in this state, 'contacts.detail'.
// <div ui-view='info'/> within contacts.detail.html
"info#contacts.detail" : { }
// Absolutely targets the 'detail' view in the 'contacts' state.
// <div ui-view='detail'/> within contacts.html
"detail#contacts" : { }
// Absolutely targets the unnamed view in parent 'contacts' state.
// <div ui-view/> within contacts.html
"#contacts" : { }

Related

How to define template or templateUrl (HTML) for a state with named views?

I was referring to multiple named views doc and found this statement in one of their examples:
Since the state "contacts.detail" has named views, its template or templateUrl will be ignored (as per the docs). So from where will contacts.detail.html be loaded/defined and how the 'info' view be targeted in that?
They are using the following example in the documentation link you provided :
$stateProvider
.state('contacts', {
// This will get automatically plugged into the unnamed ui-view
// of the parent state template. Since this is a top level state,
// its parent state template is index.html.
templateUrl: 'contacts.html'
})
.state('contacts.detail', {
views: {
////////////////////////////////////
// Relative Targeting //
// Targets parent state ui-view's //
////////////////////////////////////
// Relatively targets the 'detail' view in this state's parent state, 'contacts'.
// <div ui-view='detail'/> within contacts.html
"detail" : { },
// Relatively targets the unnamed view in this state's parent state, 'contacts'.
// <div ui-view/> within contacts.html
"" : { },
///////////////////////////////////////////////////////
// Absolute Targeting using '#' //
// Targets any view within this state or an ancestor //
///////////////////////////////////////////////////////
// Absolutely targets the 'info' view in this state, 'contacts.detail'.
// <div ui-view='info'/> within contacts.detail.html
"info#contacts.detail" : { }
// Absolutely targets the 'detail' view in the 'contacts' state.
// <div ui-view='detail'/> within contacts.html
"detail#contacts" : { }
// Absolutely targets the unnamed view in parent 'contacts' state.
// <div ui-view/> within contacts.html
"#contacts" : { }
// absolutely targets the 'status' view in root unnamed state.
// <div ui-view='status'/> within index.html
"status#" : { }
// absolutely targets the unnamed view in root unnamed state.
// <div ui-view/> within index.html
"#" : { }
});
Well detail is a child state of contact.
When you define that state, you can define it normally with tempalte url, or you can use nested views. If you use nested views, you shall have a parent state (here contact) and you have the choice to define the the detail child state with two ways :
// Relatively targets the 'detail' view in this state's parent state, 'contacts'.
// <div ui-view='detail'/> within contacts.html
"detail" : {templateUrl: 'contacts.detail.html' ...}
Which is the relative way (relative to its parent state, here 'contacts')
// Absolutely targets the 'detail' view in the 'contacts' state.
// <div ui-view='detail'/> within contacts.html
"detail#contacts" : {templateUrl: 'contacts.details.html' }
Which is the absolute way, defining the detail child state.
This fiddle is an example of all of this.

Hiding parent state view from child state view in angularjs, best practices?

'update-approved-product-campaign-ad' this is the child state of 'product-campaigns.product-campaign-detail' when i call child state through ui-sref parent state view gets called along with child state view which i don't want. I want only child state view.
i solved this issue by wrapping the parent view with <div ui-view="">
This will override the ui-view with the child when navigating to a child state, otherwise it will contain the patent state's view.
This can be done using view targeting.
Say you have an unnamed ui-view for the parent at the root. When the child is activated, it can take over the parent's unnamed view using targeted views. The child view can put its own content directly into the parent's view:
var app = angular.module("plunker", ['ui.router'])
app.config(function($stateProvider) {
$stateProvider.state('parent', {
url: "",
template: "<a ui-sref='.child'>Go to child</a><h1>Parent</h1>"
}).state('parent.child', {
url: '/child',
views: {
"#": {
template: "<a ui-sref='^'>Back to parent</a><h1>child</h1>"
}
}
})
})
or it could replace the parent view with a template that just has a nested ui-view, which it in turn targets
var app = angular.module("plunker", ['ui.router'])
app.config(function($stateProvider) {
$stateProvider.state('parent', {
url: "",
template: "<a ui-sref='.child'>Go to child</a><h1>Parent</h1>"
}).state('parent.child', {
url: '/child',
views: {
"#": {
template: "<small>parent ui-view overridden by parent.child</small> <div ui-view='child'/>"
},
"child#parent.child": {
template: "<a ui-sref='^'>Back to parent</a><h1>child</h1>"
}
}
})
})
Targeted named views are "viewname#statename". The named view "#" means target the view named empty string ("") at (#) the root state ("").
http://plnkr.co/edit/iff63xbNWCbK9MEPMb4f?p=preview
As far as i know its not possible to load only the child view. Whenever a childs view is loaded, its parents' view is loaded too alongwith it.

$stateParams field is undefined if not located within its corresponding ui-view

I am trying for a parent state/view to access a child state/view. I am looking for a workaround.
My state provider configuration is as follows:
.state('advertisement', {
url: '/advertisement', abstract: true, parent: 'authenticated'
})
.state('advertisement.new', {
url: '/new',
abstract: true,
views: {
'#': {
controller: 'AdvertisementSaveCtrl',
templateUrl: 'advertisement/views/advertisement.form.html'
}
}
})
.state('advertisement.new.field', {
url: '/:fieldId',
views: {
'#advertisement.new': {
templateUrl: function ($stateParams){
return 'advertisement/views/fields/advertisement.' + $stateParams.fieldId + '.html';
}
}
}
})
In my markup:
<li ui-sref-active="active" ng-class="{'active': isActive()}"><!-- not in scope! -->
<div ui-view></div><!-- this is the view targeted by advertisement.new.field -->
The advertisement.new.field state changes according to the current field (:fieldId). I have set up a number of links (located outside of my nested ui-view) that change the state by changing the :fieldId state param but obviously, the $stateParam.fieldId is undefined if it is not within the corresponding ui-view div.
To put it differently, it seems the isActive method has no access to $stateParam.fieldId...
Can anyone please provide a workaround?
In your code samples I don't see in which controller the isActive() method is defined. But it must be defined in a controller.
I assume the isActive() function is defined on a controller named MainController.
You can then inject the $state service in this controller and via the $state.params you can access the parameters of the active state even outside of the <ui-view> tag:
app.controller('MainController', function ($scope, $state) {
console.log($stateParams);
this.isActive = function() {
// access params of active state via $state.params
console.log($state.params.fieldId);
};
});

Angular route act like another route without url change

I'm using Angular-UI router and in my project I have this structure of pages:
- Main (/main)
-- Table (/main/table/:userid)
-- Info (/main/info)
-- About (/main/about)
In case the user going to the /main I want it to act like the user hit the /main/table/1 without causing url change.
How can I achieve that ?
Here are my states:
$stateProvider
.state('main', {
'url': '/main',
'templateUrl': '/pages/main.html',
'controller': 'MainController',
'resolve': { ... }
})
.state('main.table', {
'url': '/main/table/:userid',
'templateUrl': '/pages/table.html',
})
.state('main.info', {
'url': '/main/info',
'templateUrl': '/pages/info.html',
})
.state('main.about', {
'url': '/main/about',
'templateUrl': '/pages/about.html',
})
I created working plunker here. The trick is to reuse the "main.table" stuff directly in the main state.
We can have main state define like this:
$stateProvider
.state('main', {
'url': '/main',
views: {
'': {
'templateUrl': '/pages/main.html',
'controller': 'MainController',
},
'#main': {
'templateUrl': '/pages/table.html',
'controller': 'TableController',
}
}
})
And these are almost unchanged, just the /main is replaced from url, it will be passed by parent.
.state('main.table', {
'url': '/table/:userid',
'templateUrl': '/pages/table.html',
'controller': 'TableController',
})
.state('main.info', {
'url': '/info',
'templateUrl': '/pages/info.html',
})
.state('main.about', {
'url': '/about',
'templateUrl': '/pages/about.html',
})
And this would be the controller for table view
.controller('TableController', function($scope, $stateParams) {
$scope.userid = $stateParams.userid || 1;
})
Check it all here
The technique used here is: The main state does have two views. One of them is the main - layout template. The second is immediately injecting other view into that layout. via absolute naming '#main' (unnamed view in the state main)
That view (for displaying table) is the same which we use for main.table state. We just check, that if there is no param userid - 1 is used
Read more about this multi views here
View Names - Relative vs. Absolute Names
small extract from example snippet:
$stateProvider
.state('contacts', {
// This will get automatically plugged into the unnamed ui-view
// of the parent state template. Since this is a top level state,
// its parent state template is index.html.
templateUrl: 'contacts.html'
})
.state('contacts.detail', {
views: {
////////////////////////////////////
// Relative Targeting //
// Targets parent state ui-view's //
////////////////////////////////////
// Relatively targets the 'detail' view in this state's parent state, 'contacts'.
// <div ui-view='detail'/> within contacts.html
"detail" : { },
// Relatively targets the unnamed view in this state's parent state, 'contacts'.
// <div ui-view/> within contacts.html
"" : { },
///////////////////////////////////////////////////////
// Absolute Targeting using '#' //
// Targets any view within this state or an ancestor //
///////////////////////////////////////////////////////
// Absolutely targets the 'info' view in this state, 'contacts.detail'.
// <div ui-view='info'/> within contacts.detail.html
"info#contacts.detail" : { }
// Absolutely targets the 'detail' view in the 'contacts' state.
// <div ui-view='detail'/> within contacts.html
"detail#contacts" : { }
// Absolutely targets the unnamed view in parent 'contacts' state.
// <div ui-view/> within contacts.html
"#contacts" : { }
// absolutely targets the 'status' view in root unnamed state.
// <div ui-view='status'/> within index.html
"status#" : { }
// absolutely targets the unnamed view in root unnamed state.
// <div ui-view/> within index.html
"#" : { }
});
stateProvider url property is responsible for browser URL routing mechanism.
stateProvider templateUrl property is a html partial view template which is going to render against a particular state, in our case it is main.
$stateProvider
.state('main', { // **main** is a state.
'url': '/main', // **/main** is a preferred url you want to set in the browser.
'templateUrl': '/main/table/1', // **/main/table/1** is a template to be rendered.
'controller': 'MainController',
'resolve': { ... }
})

How do I prevent reload on named view, when state changes? AngularJS UI-Router

I'm using the excellent ui-router module in my application. As part of this, I'm using named views to manage the 'dynamic sub-navigation' I have in the app.
Consider the following:
$urlRouterProvider.otherwise('/person/list');
$stateProvider
.state('person', {
url: '/person',
abstract: true,
})
.state('person.list', {
url: '/list',
views: {
"main#": {
templateUrl: "person.list.html",
controller: 'PersonListController'
}
}
})
.state('person.details', {
url: '/{id}',
views: {
'main#': {
templateUrl: "person.details.html",
controller: 'PersonController'
},
'nav#': {
templateUrl: "person.nav.html",
controller: 'PersonNavController'
}
}
});
When users first visit the app, they are presented with a list of people. When they click on a person, they are taken to the details page. Pretty basic stuff. Here's the markup if it helps...
<div>
<aside ui-view="nav"></aside>
<div ui-view="main"></div>
</div>
However, the PersonNavController calls a REST service to get a list of people, so when viewing a person, the user is able to navigate sibling elements. Using the method above causes the template and controller to re-render, thus causing a delay after every click, despite the content never changing.
Is there a way to keep the 'nav#' view loaded, and only refresh the 'main#' view?
The way I am using ui-router in this scenarios is: move the views to the least common denominator.
Other words: In case that ui-view="nav" is shared among all the details and is the same for all of them (because it should be loaded only once) - it should be part of the list state (parent of the detail state)
the parent state defintion would be adjusted like this:
.state('person.list', {
url: '/list',
views: {
"main#": {
templateUrl: "person.list.html",
controller: 'PersonListController'
}
// here we target the person.list.html
// and its ui-view="nav"
'nav#person.list': {
templateUrl: "person.nav.html",
controller: 'PersonNavController'
}
}
So where is the trick? In the power of the angular ui-router. We can, during each state defintion, target the current view. Now, the nav view is part of the list state definition - i.e. it will not be reloaded during the detail switching (also check here for more explanation)
We just have to use the defined naming conventions, see:
View Names - Relative vs. Absolute Names
Few cited lines from the mentioned documentation:
views: {
////////////////////////////////////
// Relative Targeting //
// Targets parent state ui-view's //
////////////////////////////////////
// Relatively targets the 'detail' view in this state's parent state, 'contacts'.
// <div ui-view='detail'/> within contacts.html
"detail" : { },
// Relatively targets the unnamed view in this state's parent state, 'contacts'.
// <div ui-view/> within contacts.html
"" : { },
///////////////////////////////////////////////////////
// Absolute Targeting using '#' //
// Targets any view within this state or an ancestor //
///////////////////////////////////////////////////////
// Absolutely targets the 'info' view in this state, 'contacts.detail'.
// <div ui-view='info'/> within contacts.detail.html
"info#contacts.detail" : { }
// Absolutely targets the 'detail' view in the 'contacts' state.
// <div ui-view='detail'/> within contacts.html
"detail#contacts" : { }

Resources