I am trying to extend a parent view to a child view.
My route.js
let view = {
'': {
templateUrl: '/app/content.html'
},
'sidebar': {
templateUrl: '/app/sidebar.html'
}
};
.state('profile', {
abstract: true,
url: '/profile',
views: view,
templateUrl: '/app/profile/profile.html'
})
.state('profile.about', {
parent: 'profile',
url: '/about',
views: {
'': {
templateUrl: '/app/profile/about.html'
}
}
})
My index.html:
<div ui-view></div>
My profile/profile.html:
//all other stuff (header, sidebar, etc)
<div>
<h1>Profile</h1>
<div ui-view=""></div>
</div>
My profile/about.html:
<div>
<h1>About</h1>
</div>
Everything works perfectly including the sidebar.
The problem is that about.html is showing the page but it is not extending the profile/profile.html page.
Any solutions?
Here is the plunker.
It's a little bit different but it is the same, considering how the route1 is not shown but the test.html is show.
Try this way:
let view = {
'': {
templateUrl: '/app/profile/index.html'
},
'content': {
templateUrl: '/app/content.html'
},
'sidebar': {
templateUrl: '/app/sidebar.html'
}
};
.state('profile', {
abstract: true,
url: '/profile',
views: view
})
.state('profile.about', {
parent: 'profile',
url: '/about',
views: {
'': {
templateUrl: '/app/profile/about.html'
}
}
})
Related
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.
My app is very simple. i have a home page that contains header and body. in body section I want to show login page and if URL changed to 'password/forget' I show password reset form. my templates:
index.html :
<header ui-view="header">
</header>
<div class="container-fluid">
<div class="row">
<div ui-view="main">
</div>
</div>
</div>
home.html:
<div ui-view>
</div>
And ui-router config is this:
$locationProvider.html5Mode({enabled: true, requireBase: false});
$stateProvider.state('home', {
url: '/',
views: {
'header': {
templateUrl: '/header.html'
},
'main': {
templateUrl: '/home.html'
}
}
}).state('home.forgetPassword', {
url: '/password/forget',
templateUrl: '/forgetPassword.html',
});
Now when I go "/password/forget" anything happen and index.html is showing.
I want to show forgetPassword.html when route changes to "/password/forget" .
Following this code in documentation of ui-router:
myApp.config(function($stateProvider, $urlRouterProvider) {
$stateProvider
.state('state1', {
url: "/state1",
templateUrl: "partials/state1.html"
})
.state('state1.list', {
url: "/list",
templateUrl: "partials/state1.list.html",
controller: function($scope) {
$scope.items = ["A", "List", "Of", "Items"];
}
})
.state('state2', {
url: "/state2",
templateUrl: "partials/state2.html"
})
.state('state2.list', {
url: "/list",
templateUrl: "partials/state2.list.html",
controller: function($scope) {
$scope.things = ["A", "Set", "Of", "Things"];
}
});
});
What i suggest is to create the 'password' state first.
Try the following solution:
$stateProvider.state('home', {
url: '/',
views: {
'header': {
templateUrl: '/header.html'
},
'main': {
templateUrl: '/home.html'
}
}
}).state('password', {
url: '/password',
templateUrl: '/password.html',
}).state('password.forgetPassword', {
url: '/forget',
templateUrl: '/forgetPassword.html',
});
Create the file 'password.html' for now, later on it might be useful for '/password' view.
Problem solved. it was for / in parent state and repeat it on child states.
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
This is my index.html:
<body>
<div ui-view></div>
</body>
This is my app.js:
angular.module('sample', [
'auth0',
'ngRoute',
'sample.home',
'sample.header',
'sample.login',
'ui.router',
'angular-storage',
'angular-jwt'
])
.config(function myAppConfig($stateProvider, $urlRouterProvider, $routeProvider, authProvider, $httpProvider, $locationProvider,
jwtInterceptorProvider) {
$stateProvider
.state('login', {
url: '/login',
templateUrl: 'login/login.html',
controller: 'LoginCtrl'
}).state('root', {
url: '/',
abstract: true,
views: {
'header': {
templateUrl: 'home/header.html',
controller: 'HeaderCtrl'
},
'footer': {
templateUrl: 'home/footer.html'
}
},
data: {
requiresLogin: true
}
}).state('root.home', {
url: '/',
views: {
'#': {
templateUrl: 'home/home.html'
}
},
data: {
requiresLogin: true
}
})
$urlRouterProvider.otherwise('/');
authProvider.init({
domain: AUTH0_DOMAIN,
clientID: AUTH0_CLIENT_ID,
loginUrl: '/login'
});
jwtInterceptorProvider.tokenGetter = function(store) {
return store.get('token');
}
$httpProvider.interceptors.push('jwtInterceptor');
})
.run(function($rootScope, auth, store, jwtHelper, $location, $state, $stateParams) {
$rootScope.$on('$locationChangeStart', function() {
if (!auth.isAuthenticated) {
var token = store.get('token');
if (token) {
if (!jwtHelper.isTokenExpired(token)) {
auth.authenticate(store.get('profile'), token);
} else {
$location.path('/login');
}
}
}
});
})
.controller('AppCtrl', function AppCtrl($scope, $location) {
$scope.$on('$routeChangeSuccess', function(e, nextRoute) {
if (nextRoute.$$route && angular.isDefined(nextRoute.$$route.pageTitle)) {
$scope.pageTitle = nextRoute.$$route.pageTitle + ' | Auth0 Sample';
}
});
})
If I do login & the root that's commented out, everything works fine. But I need to put in a header and footer (the files are correct) and when I try the root + root.home, I get a blank screen with no errors on the browser's console either.
I'm trying to go off of a few examples from online (such as this one) but none are working out so I don't know what I'm doing wrong.
Right now my header/footer.html just say header/footer.html while home has a button on it.
Added the full app.js in case that helps. Each html (footer/header/home) just has
<h1>Home</h1>
<div ui-view></div>
Edit: index.html
<body>
<div ui-view="header"></div>
<div ui-view></div>
<div ui-view="footer"></div>
</body>
app.js
.state('root.home', {
url: '/',
views: {
'#': {
templateUrl: 'views/home.html',
controller: 'HomeCtrl'
}
},
data: {
requiresLogin: true
}
})
I believe your issue here is your route is looking for a view named container not a class.
<div ui-view="container"></div>
Since it can't find a view named that it does not insert anything in the view.
Or you can just change your view route to be:
views: {
'#': {
templateUrl: 'home/home.html'
}
Which will tell it to insert that HTML in the first unnamed view it finds.
You can find a break down of how nested views work with UI-Router here
Assuming your home.html looks like this:
<div ui-view="header"></div>
<div ui-view="footer"></div>
your route should be something like
views: {
'header#home': {
templateUrl: 'home/header.html',
controller: 'HeaderCtrl'
},
'footer#home': {
templateUrl: 'home/footer.html'
}
},
data: {
requiresLogin: true
}
I have an app with 3 views (A,B,C) and 2 states(1,2)
html
<div ui-view="A"></div>
<div ui-view="B"></div>
<div ui-view="C"></div>
The two states are called list and create. In both states the template and controller of view A + B stay the same but view c should change templates and controllers. I can get view c's content to change but it refreshes view A and view B as it does ie things that are in their controllers run again.
What is the correct way to organise the router to prevent this?
js so far
$urlRouterProvider.otherwise("/basestate/list");
$stateProvider
.state('baseState', function() {
url:"/basestate",
templateUrl: "basestate.html",
controller: 'BaseStateCtrl'
})
.state('baseState.list', function() {
url: "/list",
views: {
"viewA#baseState": {
templateUrl: "viewA.html"
controller: "ViewACtrl"
},
"viewB#baseState": {
templateUrl: "viewB.html"
controller: "ViewBCtrl"
},
"viewC#baseState": {
templateUrl: "list.html"
controller: "listCtrl"
}
}
})
.state('baseState.create', function() {
url: "/create",
views: {
"viewA#baseState": {
templateUrl: "viewA.html"
controller: "ViewACtrl"
},
"viewB#baseState": {
templateUrl: "viewB.html"
controller: "ViewBCtrl"
},
"viewC#baseState": {
templateUrl: "create.html"
controller: "createCtrl"
}
}
})
To achieve that you basically need to freeze your viewA and viewC at the level of baseState and make that state abstract:
.state('basestate', {
url: '/basestate',
abstract: true,
views: {
"viewA": {
templateUrl: "viewA.html",
controller: "ViewACtrl"
},
"viewB": {
templateUrl: "viewB.html",
controller: "ViewBCtrl"
},
"viewC": {
template: '<div ui-view="viewC_child"></div>'
}
}
})
Note that for viewC we are making a placeholder that will contain our nested view (either list or create):
.state('basestate.list',{
url: "/list",
views: {
"viewC_child": {
templateUrl: "list.html",
controller: "ListCtrl"
}
}
})
.state('basestate.create', {
url: "/create",
views: {
"viewC_child": {
templateUrl: "create.html",
controller: "CreateCtrl"
}
}
})
Check this plunkr and be careful with commas in your code :)