As per the documentation for multiple named views (https://github.com/angular-ui/ui-router/wiki/Multiple-Named-Views) child states can set named views in ancestor states by using the # notation.
This doesn't seem to be working in my example (http://jsfiddle.net/sarvesht/rx8jLep2/)
This is how I have defined my parent and child states, I am setting the viewB view in the child state but its not reflected on the page.
.state('index', {
url: "",
views: {
"viewA": {
template: "index.viewA"
}
}
})
.state('index.sub',{
views: {
"viewB#index": {
template: "index.viewB"
}
}
})
Is it possible to achieve what I am trying to do? Or am I doing something wrong?
Regards
Sarvesh
If I do understand your aim properly, you are almost there. The only adjustment should be:
.state('index.sub',{
views: {
// instead of this
// "viewB#index": {
// we need thsi
"viewB#": {
template: "index.viewB"
}
}
})
The reason is, that I expect that your ViewA and ViewB are both defined on the root level (index.html). And that's why the first state 'index' is correctly working, because it definition is:
.state('index', {
url: "",
views: {
// this is working
"viewA": {
// and this would be as well
"viewA#": {
template: "index.viewA"
}
}
})
Other words, I expect both views are inside of root view, which name is empty string. That's why we can target both of them like:
"viewA#": {
"viewB#": {
See: View Names - Relative vs. Absolute Names small cite:
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.
For example, the previous example could also be written as:
.state('report',{
views: {
'filters#': { },
'tabledata#': { },
'graph#': { }
}
})
Notice that the view names are now specified as absolute names, as opposed to the relative name. It is targeting the 'filters', 'tabledata', and 'graph' views located in the root unnamed template. Since it's unnamed, there is nothing following the '#'. The root unnamed template is your index.html.
There is updated plunker, with this state def:
$urlRouterProvider.otherwise("/index/sub")
$stateProvider
.state('index', {
url: "/index",
views: {
"viewA": {
template: "index.viewA"
}
}
})
.state('index.sub', {
url : "/sub",
views: {
/*
"viewB#index": {
template: "index.viewB"
},
*/
"viewB#": {
template: "index.viewB"
}
}
})
The key is to define url or at least properly navigate to sub state index.sub
Related
I'm having some difficulties to understand how UI-Router abstract state work.
Here's the thing :
I have an index.html with only a <ui-view> tag inside its <body>.
There is two main states : Landing and App ( /landing and /app ).
No problem with the landing state for now, because its only a static file ( no child view etc ).
But for my /app state, I need an abstract parent state that cover the whole application ( need to resolve User Profile for each child states ).
The thing is that child of this abstract state also have sub-view. And I can't make those render.
My $stateProvider config (simplified) :
//landing state, no problem here
.state('landing', {
url: '/landing',
templateUrl: 'landing.html'
})
// my abstract parent state, with the resolve
.state('root', {
url: '',
abstract: true,
template: "<div ui-view></div>",
resolve: {
current_user: function (UserFactory) {
return UserFactory.initCurrentUserProfile();
}
}
})
// the child which cannot render the view headerAndSearchbar
.state('root.app', {
url: '/app',
templateUrl: 'app.html',
views: {
'headerAndSearchbar': {
templateUrl: './partials/header/headerAndSearchbar.html'
}
}
})
app.html :
<div ui-view="headerAndSearchbar">
</div>
<div>
This is app.html
</div>
Note that if i remove views declaration in the state root.app, i can see the text "This is app.html".
headerAndSearchbar.html only contains simple html & css
Any ideas ? I'm bashing my head on this- What am I missing?
There are two issues.
Firstly, There are in fact two views, inside of the child 'root.app'.
Unnamed (the one, which is related to templateUrl 'app.html')
named view 'headerAndSearchbar'
And that means, that both most be declared inside of the views : {} setting:
.state('root.app', {
url: '/app',
//templateUrl: 'app.html',
views: {
'' : { templateUrl: 'app.html' },
'headerAndSearchbar': { ... n
}...
But that is not enough.
Secondly, we need absolute naming for the second (named) because it is not related to its parent, but to itself:
.state('root.app', {
url: '/app',
//templateUrl: 'app.html',
views: {
'' : { templateUrl: 'app.html' },
// this is the same as a path to parent view 'headerAndSearchbar#root'
// 'headerAndSearchbar': { ...
// this means ... search in this state, inside of app.html
'headerAndSearchbar#root.app': { ...
}...
Check these for more details:
Angularjs ui-router not reaching child controller
Angular UI Router - Nested States with multiple layouts
I need child state be able to use parent state's resolve functions. But I also need to keep same ui-view for both states. Here's a fiddle. And there's a code
$stateProvider
.state('parent', {
url: "/",
template: '<p>Hello {{parent.one}}</p><br>'
+ '<button ng-click="goToChild()">child</button><br>',
// this one below work but I don't need it
// template: '<p>Hello {{parent.one}}</p><br>'
// + '<button ng-click="goToChild()">child</button><br>'
// + '<div ui-view></div>',
resolve: {
test: function() {
return 1;
}
},
controller: function($scope, $state, test) {
$scope.parent = { one: test };
$scope.goToChild = function() {
$state.go('parent.child');
}
}
})
.state('parent.child', {
url: "/child",
template: '<p>Hello {{child.one}}</p>',
controller: function($scope, test) {
$scope.child = { one: test };
}
})
$urlRouterProvider.otherwise('/');
There is a working plunker.
The answer should be hidden/revealed in this two states definition:
parent with multi views
.state('parent', {
url: "/",
views: {
'#': {
template: '<div ui-view=""></div>',
controller: function($scope, $state, test) {
$scope.parent = { one: test };
$scope.goToChild = function() {
$state.go('parent.child');
}
}
},
'#parent': {
template: '<p>Parent says: hello <pre>{{parent | json}}</pre></p>'
+ '<br><button ng-click="goToChild()">child</button><br>',
}
},
resolve: {
test: function() { return 1; },
},
})
Child consuming parent resolve, and having its own
.state('parent.child', {
url: "^/child/:id",
template: '<p>Child says: hello <pre>{{child | json }}</pre></p>',
resolve: {
testChild: function() { return 2; },
},
controller: function($scope, test, testChild) {
$scope.child = {
one: test,
two : testChild,
parent: $scope.parent,
};
},
})
Check it here
And how it works? Well, it all is based on the:
Scope Inheritance by View Hierarchy Only
Keep in mind that scope properties only inherit down the state chain if the views of your states are nested. Inheritance of scope properties has nothing to do with the nesting of your states and everything to do with the nesting of your views (templates).
It is entirely possible that you have nested states whose templates populate ui-views at various non-nested locations within your site. In this scenario you cannot expect to access the scope variables of parent state views within the views of children states.
and also:
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.
For example, the previous example could also be written as:
.state('report',{
views: {
'filters#': { },
'tabledata#': { },
'graph#': { }
}
})
So, the above documentation cites are the core of the plunker. The parent uses multi views, one of them is unnamed - and will be used for inheritance. Parent also injects into that view its own "parent" view. The Resolve of a parent is in place...
Child now injects into anchor of its parent, which does have all the stuff needed. That means, that child does inherit scope and also resolve stuff. It shows its own resolve as well...
I have a single-page AngularJS application with four regions, each with its own content:
I need each region to communicate via services, but otherwise they need to have their own independent routing for view purposes i.e. they should each have their own view state.
I have tried to do this (plunkr) with angular-ui-router but I can't figure out how to create angular-ui states that affect only a particular module or region, without modifying the rest of the regions on the page.
The page contains the regions:
<body>
<a ui-sref="initial1">Initial Region 1</a><br/>
<a ui-sref="initial2">Initial Region 2</a>
<div ui-view="region1" class="region1"></div>
<div ui-view="region2" class="region2"></div>
</body>
And the app attempts to define each region in an independent module:
var app = angular.module('Main', ['ui.router', 'Region1', 'Region2']);
var region1App = angular.module('Region1', []);
region1App.config(function($urlRouterProvider, $stateProvider) {
$urlRouterProvider.otherwise("/");
$stateProvider
.state('initial1', {
url: '/',
views: {
'region1#': {
template: 'Initial Region 1 State, go to <a ui-sref="second1">Second State</a>'
}
}
})
.state('second1', {
url: '/',
views: {
'region1#': {
template: 'Second Region 1 State, go to <a ui-sref="initial1">Initial State</a>'
}
}
});
});
var region2App = angular.module('Region2', []);
region2App.config(function($urlRouterProvider, $stateProvider) {
$urlRouterProvider.otherwise("/");
$stateProvider
.state('initial2', {
url: '/',
views: {
'region2#': {
template: 'Initial Region 2 State, go to <a ui-sref="second2">Second State</a>'
}
}
})
.state('second2', {
url: '/',
views: {
'region2#': {
template: 'Second Region 2 State, go to <a ui-sref="initial2">Initial State</a>'
}
}
});
});
Each module should have its own "initial" state and "second" state, and both should show on the screen at the same time, and changing the state of one should not affect the other. If this cannot be done with angular-ui-router, what is the best way to do this with Angular?
You can use UI-Router Extras - sticky states to achieve your goal.
You'll want one named <div ui-view='name'></div> for each region. Then, add sticky: true to the state definition which targets that region's named view.
<div ui-view="region1"></div>
<div ui-view="region2"></div>
<div ui-view="region3"></div>
<div ui-view="region4"></div>
.state('state1', {
sticky: true,
views: { region1: { templateUrl: 'foo.html', controller: barCtrl } }
}
.state('state2', {
sticky: true,
views: { region2: { templateUrl: 'foo2.html', controller: bar2Ctrl } }
}
.state('state3', {
sticky: true,
views: { region3: { templateUrl: 'foo3.html', controller: bar3Ctrl } }
}
.state('state4', {
sticky: true,
views: { region4: { templateUrl: 'foo4.html', controller: bar4Ctrl } }
}
There is a demo you can view which shows how this works. Note: the demo uses tabs and shows/hides the ui-views accordingly. Your use case does not need to show/hide each named view.
Check out the demo source code for more.
I created a separate angular app for each region. Communication across applications is done via obtaining a reference to the relevant scope via the app element in the DOM, and sending an event via angular.element(document.getElementById('RegionX_App')).scope().$emit as shown here.
UPDATE: I ended up using Sticky States in UI-Router Extras as described in the answer by Chris T, and it worked perfectly.
I have a abstract root state called 'site' with empty url i.e. '' , which has two child state 'profile' and 'home' where the url for home is '/' and the url for profile is '/{username:[a-zA-Z0-9]{3,20}}' inorder to test I tried many times to go to the profile state but it always reaches the home state.
But when i change the home state url with say '/home' then i'm able to go to the profile state. After searching the web i couldn't find this issue. Is it a bug? or i'm doing something wrong?
Here is the code for $stateProvider
$stateProvider.
state('site', {
url: '',
abstract: true,
views: {
'': {
templateUrl: 'views/site.html' //this is the base template
},
'navbar#site': {
templateUrl:'views/navbar.html'
}
},
data:{
access: access.public
}
}).
state('site.home', {
url: '/',
templateProvider :['$rootScope','$http','$state',function($rootScope,$http,$state){
//console.log($state);
var template = 'views/main_new.html';
if($rootScope.User.isLoggedIn()){
template = 'views/new_room_me.html'
}
return $http.get(template).then(function (resp) {
return resp.data;
});
}]
}).
state('site.profile',{
url:'/{username:[a-z0-9A-Z]{3,20},strict:true}',
templateUrl:'views/new_room_friend.html'
});
$urlRouterProvider.otherwise('/ankit');
There is a working example, where I mostly switched the order of state declaration:
.state('site.profile',{
url:'/{username:[a-z0-9A-Z]{3,20}}',
...
})
.state('site.home', {
url: '/',
...
})
The reason is that ui-router url engine does iterate states in order they were declared, and tries to find a url match. this way, we will get the working definition.
But I would suggest:
use some keyword to distinguish url, e.g. url:'/profile/{username:[a-z0-9A-Z]{3,20}}',
Not only this will help the engine to find the proper state by url, but even from perspective of the user/reader of the url - this seems to me more explicit, self-descriptive.
Anyhow, switching state def should solve the issue here...
Is it possible for a named view in ui-router to make a transition to some other named view?
For example, in the snippet below, is it possible to move from blue#bar state to orange#foo state?
.state('foo', {
url: '/',
views: {
'': { templateUrl: 'partials/a.html' },
'black#foo': {
templateUrl: 'partials/b.html',
controller: 'SuperController'
},
'green#foo': {
templateUrl: 'partials/c.html',
controller: 'SuperController'
},
'orange#foo': {
templateUrl: 'partials/d.html',
controller: 'SuperController'
}
}
})
.state('bar', {
url: '/ping/:x/:y',
views: {
'': {
templateUrl: 'partials/e.html',
controller: 'DuperController'
},
'blue#bar': {
templateUrl: 'partials/f.html',
controller: 'DuperController'
}
}
})
The ui-router transitions are there for state transitions. Other words, there are no transitions among views.
What we can do, is to move some named view anchors (ui-view="myName") into root or some parent state. Then each child (grand child) state can target any named view in its parent(s) hierarchy.
The best way how to understand that is to read this chapter from doc:
View Names - Relative vs. Absolute Names
And check this snippet (cite from the resource above):
$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
"#" : { }
});
Also, check this sample app (by ui-router team)
http://angular-ui.github.io/ui-router/sample/#/contacts
and its state defintion
state.js
small cite from the child state definition, targeting many different views:
///////////////////////
// Contacts > Detail //
///////////////////////
// You can have unlimited children within a state. Here is a second child
// state within the 'contacts' parent state.
.state('contacts.detail', {
// Urls can have parameters. They can be specified like :param or {param}.
// If {} is used, then you can also specify a regex pattern that the param
// must match. The regex is written after a colon (:). Note: Don't use capture
// groups in your regex patterns, because the whole regex is wrapped again
// behind the scenes. Our pattern below will only match numbers with a length
// between 1 and 4.
// Since this state is also a child of 'contacts' its url is appended as well.
// So its url will end up being '/contacts/{contactId:[0-9]{1,8}}'. When the
// url becomes something like '/contacts/42' then this state becomes active
// and the $stateParams object becomes { contactId: 42 }.
url: '/{contactId:[0-9]{1,4}}',
// If there is more than a single ui-view in the parent template, or you would
// like to target a ui-view from even higher up the state tree, you can use the
// views object to configure multiple views. Each view can get its own template,
// controller, and resolve data.
// View names can be relative or absolute. Relative view names do not use an '#'
// symbol. They always refer to views within this state's parent template.
// Absolute view names use a '#' symbol to distinguish the view and the state.
// So 'foo#bar' means the ui-view named 'foo' within the 'bar' state's template.
views: {
// So this one is targeting the unnamed view within the parent state's template.
'': {
templateUrl: 'app/contacts/contacts.detail.html',
controller: ['$scope', '$stateParams', 'utils',
function ( $scope, $stateParams, utils) {
$scope.contact = utils.findById($scope.contacts, $stateParams.contactId);
}]
},
// This one is targeting the ui-view="hint" within the unnamed root, aka index.html.
// This shows off how you could populate *any* view within *any* ancestor state.
'hint#': {
template: 'This is contacts.detail populating the "hint" ui-view'
},
// This one is targeting the ui-view="menu" within the parent state's template.
'menuTip': {
// templateProvider is the final method for supplying a template.
// There is: template, templateUrl, and templateProvider.
templateProvider: ['$stateParams',
function ( $stateParams) {
// This is just to demonstrate that $stateParams injection works for templateProvider.
// $stateParams are the parameters for the new state we're transitioning to, even
// though the global '$stateParams' has not been updated yet.
return '<hr><small class="muted">Contact ID: ' + $stateParams.contactId + '</small>';
}]
}
}
})
some other reading with more links