https://plnkr.co/edit/ByatrCzdUJfAV3oc8XPq?p=preview
^ On line 10, if you put back in the abstract:true key you will see the tags view appear in this plnkr app.
However my problem is that in my real app it won't let me use the abstract key because you first start at a login state and then transition to the dashboard state.
And the abstract key allows me to add the additional tags state as a child of dashboard.
When I have that key in there and I login in my real app this is the error I get:
Error: Cannot transition to abstract state 'dashboard'
Plnkr code:
var dash = {
name: 'dash',
url: '/dash?ticker',
// abstract: true,
views: {
'': { templateUrl: 'dashboard.html' },
'tickersList#dash': {
templateUrl: 'tickers-list.html',
controller: 'tickersController'
},
'alertsList#dash': {
templateUrl: 'alerts-list.html',
controller: 'alertsController'
}
}
};
var tags = {
name: 'dash.tags',
url: '?ticker',
params: {
ticker: 'AAA'
},
views: {
'tagsList#dash': {
templateUrl: 'tags-list.html',
controller: 'tagsController'
}
}
}
$stateProvider
.state(dash)
.state(tags);
Real app
LoginController:
$state.go('dashboard')
STATE_CONSTANTS:
dashboard state object:
.constant('STATE_CONSTANTS', {
dash: {
name: 'dashboard',
// abstract: true,
url: `/dashboard?ticker?start_epoch?end_epoch?timespan?group?sort?term_id_1?term_id_2?term_id_3?social?stream?links?retweets?tags_open?feed_open?chart_alerts?chart_max`,
views: {
'': {
templateUrl: 'dash/dashboard_container.html',
controller: function(UserFactory, container, user) {
this.container = container;
UserFactory.storeUser(user);
},
controllerAs: 'dc',
bindToController: true,
resolve: {
user: (AuthFactory) => AuthFactory.check_login(),
settings: (user, UserFactory) => UserFactory.settings(user),
container: ($stateParams, TagsFactory) => TagsFactory.createTerms($stateParams)
}
},
'platformHeader#dashboard': {
templateUrl: 'headers/platform/platform_header.html',
controller: 'PlatformCtrl',
controllerAs: 'ph'
},
'timespanHeader#dashboard': {
templateUrl: 'headers/timespan/timespan_header.html',
controller: 'TimeHeaderCtrl',
controllerAs: 'thc'
},
'tickersPanel#dashboard': {
templateUrl: 'tickers/panel/tickers_panel.html',
controller: 'TickersPanelCtrl',
controllerAs: 'tikp'
},
},
params: {
ticker: '',
},
data: { authorizedRoles: ['All'] }
},
login state object:
login: {
name: 'login',
url: '/login',
templateUrl: 'auth/login.html',
controller: 'LoginCtrl',
data: { authorizedRoles: ['All'] }
}
dashboard.html template
<div>
<header>
<div ui-view="platformHeader"></div>
<div ui-view="timespanHeader"></div>
</header>
<aside>
<!-- the headers and tickersPanel are all child states of
dashboard state -->
<div ui-view="tickersPanel"></div>
<!-- tags is a seperate state from dashboard -->
<div ui-view="tagsPanel"></div>
</aside>
//...
app.js
$stateProvider
.state(STATE_CONSTANTS.login)
.state(STATE_CONSTANTS.password)
.state(STATE_CONSTANTS.passwordreset)
.state(STATE_CONSTANTS.settings)
.state(STATE_CONSTANTS.settingsDefault)
.state(STATE_CONSTANTS.settingsAlerts)
.state(STATE_CONSTANTS.dash)
The behavior is right. You cannot transit to abstract state. Look at your example from plunker.
var dash = {
name: 'dash',
url: '/dash?ticker'
var tags = {
name: 'dash.tags',
url: '?ticker',
You have an abstract state "dash" and you have a child state "dash.tags" which is not abstract. So you can transit only to child state.
In your app, you try transiting to an abstract state which is not possible.
Abstract states are used if you want to have some basic state with common behavior (parent state). You cannot transit to such states but they can have some basic template, resolve functions... So, you have to remove abstract flag or create a child state.
Related
https://plnkr.co/edit/VV13ty8XaQ20tdqibmFy?p=preview
Expected
After login the dashboard state renders dashboard.html, and all components and ui-views should render: tickers, tags, social(named ui-view) and feed.
Results
After login the dashboard state renders dashboard.html however only the components tickers,tags and feed show up, but not the social (named-ui-view)
I feel that my problem lies somewhere around where I transition from the login state to the dashboard state. Once you hit the dashboard state, it serves up the default template which is the component element tag: <dash-module></dash-module>. This will then render the dash.component template: dashboard.html and controller. However I've lost access to the social view in the dashboard state object.
dashboard.html
<div class="jumbotron text-center">
<h1>The Dashboard</h1>
</div>
<div class="row">
<tickers-module></tickers-module>
<tags-module></tags-module>
// Expecting the social-module-template.html to show below:
<div ui-view="social"></div>
<feed-module></feed-module>
</div>
The routerApp module with the dashboard component full code in Plnkr
// RouterApp module
////////////////////////////////////////////////////////////////////////////////
var routerApp = angular.module('routerApp', ['ui.router', 'tickers', 'tags', 'feed']);
routerApp.config(function($stateProvider, $urlRouterProvider) {
$urlRouterProvider.otherwise('/login');
const login = {
name: 'login',
url: '/login',
templateUrl: 'login.html',
bindToController: true,
controllerAs: 'l',
controller: function($state) {
this.login = function() {
$state.go('dashboard', {});
}
}
}
const dashboard = {
name: 'dashboard',
url: '/dashboard',
params: {
ticker: {},
tags: {}
},
template: '<dash-module></dash-module>',
views: {
'' : {
templateUrl: 'dashboard.html',
},
'social' : {
templateUrl: 'social-module-template.html',
controller: function($state) {
console.log('Social init', $state.params);
}
}
}
}
$stateProvider
.state(login)
.state(dashboard);
})
tags.component('dashModule', {
templateUrl: 'dashboard.html',
controller: function($scope, $state) {
console.log('dashModule loaded!');
}
})
This is the part that should render the social html content in the <div ui-view="social"></div>
views: {
'' : {
templateUrl: 'dashboard.html',
},
'social' : {
templateUrl: 'social-module-template.html',
controller: function($state) {
console.log('Social init', $state.params);
}
}
}
I made changes to your plunker here You were missing # here.
const dashboard = {
name: 'dashboard',
url: '/dashboard',
params: {
ticker: {},
tags: {}
},
template: '<dash-module></dash-module>',
views: {
'' : {
templateUrl: 'dashboard.html',
},
'social#dashboard' : {
templateUrl: 'social-module-template.html',
controller: function($state) {
console.log('Social init', $state.params);
}
}
}
}
In order for these components to appear under the home state, we must define them using absolute naming. Specifically, we must use the # syntax to tell AngularJS that these components of our application should be mapped to a specific state. This follows the viewName#stateName syntax and tells our application to utilize named views from an absolute, or specific state. You can read more about relative vs. absolute names here.
See this for more information.
The problem you have is named view has to render in same state i.e Dashboard.
Change the following and it should work.
social#dashboard
Check this Plunkr
Named Views UI router
How do I setup ui-router to allow views inheritance? Currently, I have my ui-router setup similar to as follows,
$stateProvider
.state('public', {
url: '/public',
templateUrl: 'partials/public.html',
controller: 'PublicController'
})
// abstract parent
.state('private', {
abstract: true,
views: {
'': {
template: '<ui-view></ui-view>'
},
'nav': {
templateUrl: 'partials/nav.html'
}
}
})
// concrete private child state
.state('private.pageone', {
url: '/pageone',
views: {
'': {
templateUrl: 'partials/pageone.html',
controller: 'PageOneCtrl'
}
}
})
Everything is running fine, except that the parent nav view is not being inherited, and so I can't see the nav in the child state display. On the other hand, if I get rid of the parent state and then move the nav view directly to the child state... I can display the nav. Can anybody explain to me why I can't seem to be able to inherit the nav, even though the child is specified as private.pageone (same as parent: 'private').
The docs for ui-router mention that relative views actually target the template in the parent state. So to be able to use relative views in this particular situation we would have to do the following.
$stateProvider
.state('public', {
url: '/public',
templateUrl: 'partials/public.html',
controller: 'PublicController'
})
// abstract parent
.state('private', {
abstract: true,
views: {
'': {
template: '<ui-view></ui-view>'
}
}
})
// concrete private child state
.state('private.pageone', {
url: '/pageone',
views: {
'': {
templateUrl: 'partials/pageone.html',
controller: 'PageOneCtrl'
},
// target the <div ui-view="nav"></div> in the parent template
'nav': {
templateUrl: 'partials/nav.html'
}
}
})
Alternatively, it would be possible to use absolute views instead of relative ones, as in the following.
$stateProvider
.state('public', {
url: '/public',
templateUrl: 'partials/public.html',
controller: 'PublicController'
})
// abstract parent
.state('private', {
abstract: true,
views: {
'': {
template: '<ui-view></ui-view>'
},
// note how nav has been specified absolutely
'nav#private': {
templateUrl: 'partials/nav.html'
}
}
})
// concrete private child state
.state('private.pageone', {
url: '/pageone',
views: {
'': {
templateUrl: 'partials/pageone.html',
controller: 'PageOneCtrl'
}
}
})
My index.html page has 3 views:
<header ui-view="header"></header>
<main ui-view="content"></main>
<footer ui-view="footer"></footer>
I just changed the site to use these 3 views instead of the initial single view.
All the routes in my app work fine, for example the "home" view:
$stateProvider.state('home', {
url: '/home',
data: {
pageTitle: 'Home',
access: 'private',
bodyClass: 'home'
},
views: {
'header': {
templateUrl: 'modules/header/header.html'
},
'content': {
controller: 'HomeController as home',
templateUrl: 'modules/home/templates/home.html'
},
'footer': {
templateUrl: 'modules/footer/footer.html'
}
}
});
My issue is the "otherwise" state in the app does not correctly load the "home" state as it should. The page is blank, no console errors. Here's the state in my app.module:
angular.module('app').config(function ($stateProvider) {
$stateProvider.state('otherwise', {
url: '*path',
template: '',
data: {
pageTitle: '',
access: 'public',
bodyClass: ''
},
controller: function ($state) {
$state.go('home');
}
});
});
What am I missing here?
I am not sure if your wild char works or not.
Ideally I use the following for default routing:
$urlRouterProvider.otherwise('/app/home');
Is it possible to define multiple views in child state with parent child state relationship using UI-Router?
I have the following code in my config
$urlRouterProvider.otherwise("/child");
$stateProvider
.state('parent', {
abstract: true,
views: {
'parent': {
templateUrl: "parent.html",
controller: "parentCtrl as parentCtrl"
},
}
})
.state('parent.child', {
url: '/child',
views: {
'state1#parent.child': {
templateUrl: "child.html",
controller: "childCtrl as childCtrl"
},
}
});
I verify that my parent.html is showing up, but my child.html is not
If I move my child.html to the parent views object like
$stateProvider
.state('parent', {
abstract: true,
views: {
'parent': {
templateUrl: "parent.html",
controller: "parentCtrl as parentCtrl"
},
'state1#parent.child': {
templateUrl: "child.html",
controller: "childCtrl as childCtrl"
},
}
})
Than child.html works.
I verify using console.log($state.$current.name); in my parentCtrl that my current state is parent.child.
Can someone give me some hint?
Thanks
There is a working plunker
I adjusted your states and mostly child views : {} like this
$stateProvider
.state('parent', {
abstract: true,
views: {
'parent': {
templateUrl: "parent.html",
controller: "parentCtrl as parentCtrl"
},
}
})
.state('parent.child', {
url: '/child',
views: {
//'state1#parent.child': {
'view1#parent': {
templateUrl: "child.html",
controller: "childCtrl as childCtrl"
},
// the same as view2#parent
'view2': {
templateUrl: "child.html",
controller: "childCtrl as childCtrl"
},
'view3#': {
templateUrl: "child.html",
controller: "childCtrl as childCtrl"
},
}
the construct '...#parent.child' is wrong, because it is a absolute naming... trying to say, that we search a ui-view="..." inside of the 'parent.child' state. And that is not the case.
So, let's place this into index:
// place for parent
<div ui-view="parent"></div>
// place for child view number 3
place in index for view 3
<div ui-view="view3"></div>
And parent template could be like this
<div ui-view="view1"></div>
<div ui-view="view2"></div>
Then the above state def will target view1 with absolute naming, the same for view3 - being in index.html; view2 will go to parent with relative name
check it here
I'm using UI-Router module for routing. I have 2 states that router should match the urls with them :
// Dashboard
.state('dashboard', {
url: "/dashboard",
templateUrl: "dashboard/views/index.html",
controller: "DashboardController",
...
})
// Users
.state('users', {
url: "/users",
templateUrl: "users/views/index.html",
controller: "UsersController",
...
})
// Single User
.state('users.id', {
url: "/{id:^[a-z0-9_-]{3,16}$}",
templateUrl: "users/views/show.html",
controller: "UserController",
...
})
also I have set a default route :
$urlRouterProvider.otherwise("/dashboard");
I want the router to match users state when I go to http://127.0.0.1:8000/app/#/users
and to match user state when I go to http://127.0.0.1:8000/app/#/users/testuser
Problem :
the users state works good, but the user state url doesn't get matched and redirects to the default state. What's the problem?
There is a working example
Try to use this regex def:
.state('users.id', {
url: "/{id:(?:[a-z0-9_-]{3,16})}",
These links will work
<a href="#/users">
<a href="#/users/testuser">
This will go to otherwise
<a href="#/users/xx">
Some inspiration could be found here
In case, we want to go to state 'users.id' directly, we just have to use proper call. In this extended plunker, we can see that it could be done like this:
// working
<a ui-sref="users">
<a ui-sref="users.id({id:'testword'})">
// not working - id does not fit - otherwise is used
<a ui-sref="users.id({id:'not-working simply too long'})">
The same would be with $state.go('users.id', {id:'testword'})
Check it here
Here is an example of how I've done it in the past. Maybe it will help you.
app.config(['$stateProvider', '$urlRouterProvider',
function ($stateProvider, $urlRouterProvider,$rootScope,$cookieStore) {
$urlRouterProvider.otherwise("/login");
$stateProvider
.state('login', {
url: '/login',
title: 'Login',
templateUrl:'views/loginView.html',
controller: 'loginCtrl',
resolve: resolver($rootScope),
})
.state('account', {
url: '/account',
title: 'My Account',
accessLevel: 2,
resolve: resolver($rootScope,$cookieStore),
views: {
'navigation': {
templateUrl: 'views/navigationView.html',
controller: 'navigationCtrl'
},
'content': {
templateUrl: 'views/contentView.html',
controller: 'navigationCtrl'
}
}
})
.state('account.dashboard', {
url:'/dashboard',
title: 'Dashboard',
views : {
'pageContent': {
templateUrl:'views/dashboardView.html',
controller: 'dashboardCtrl'
}
}
})
.state('account.foo', {
url:'/foo',
title: 'foo',
views : {
'pageContent': {
templateUrl:'views/foo.html',
controller: 'fooCtrl'
}
}
})
.state('account.maps', {
url:'/maps',
title: 'Maps and stuff',
views : {
'pageContent': {
templateUrl:'views/mapsView.html',
controller: 'mapsCtrl'
}
}
})
}])