I'm new to using Angular UI Router and I seem to be having difficulty being able to update a parent view from it's child view.
I have the following HTML structure (restructured for easier reading, obviously views are in separate html files).
<div ui-view="main">
{ main content }
<div ui-view="tab">
{ tabbed content }
</div>
</div>
Inside tab I have the following sref:
<span ui-sref="silverstone.platforms.view({_id: platform._id})">{{platform.name}}</span>
And here are my states: (I'm using webpack, hence require)
$stateProvider
.state('silverstone', {
url: '/silverstone',
views: {
'main': {
controller: 'SilverstoneCtrl',
template: require('./templates/index.html')
}
}
});
$stateProvider
.state('silverstone.platforms', {
url: '/platforms',
views: {
'tab': {
controller: 'SilverstoneCtrl',
template: require('./templates/platforms.html')
}
}
});
$stateProvider
.state('silverstone.platforms.view', {
url: '/:_id',
views: {
'main': {
controller: 'SilverstoneCtrl',
template: require('./templates/platform-view.html')
}
}
});
When the above sref is clicked, the "main" view needs to be updated. The URL is updating but the views aren't...?
I was missing # in my silverstone.platforms.view state to explicitly address the parent view.
$stateProvider
.state('silverstone.platforms.view', {
url: '/:_id',
views: {
'main#': {
controller: 'SilverstoneCtrl',
template: require('./templates/platform-view.html')
}
}
});
Use reload: true parameter in yout ui-sref links like this.
ui-sref="silverstone.platforms.view({_id: platform._id})" ui-sref-opts="{reload: true}"
Edit:
Maybe your problem are caused by nested states combined with flat template structure.
Other solution may be to properly nest your templates, as states.
For example we have states app and app.substate, then we have two templates for both states. Tempalte of app state contains ui-view directive. (that means every state contains new ui-view directive for injecting of substate template). States are nested by default, this would represent appropriately nested templates.
Related
I've been working with Angular for a year or 2 now, but this is my first project using ui-router. I'm running into a few issues with views and sub-views. The app is a standard left-side menu bar, with the views on the right changing depending on what's clicked in the menu bar.
On index.html
<body>
<div ui-view></div>
</body>
In the config.js file, which defines the routes
.state("dashboard", {
url: "/dashboard",
templateUrl: "components/dashboard/dashboard.html",
data: {
pageTitle: "Dashboard",
requiresLogin: false
}
})
.state("dashboard.welcome", {
url: "/welcome",
templateUrl: "components/welcome/welcome.html",
data: {
pageTitle: "Welcome",
requiresLogin: false
}
})
In the dashboard.html file
<div class="dashboard">
<div class="container-fluid">
<div class="row">
<div class="col-xs-12 col-md-8">
<div ui-view>
The /dashboard path loads correctly, and will load the left-side navigation bar with a blank right side. But changing the state to dashboard.welcome (/welcome) will not load the welcome.html template.
Whenever working with ui-router you need to understand that the concept of states is different from routes. When you define a sub-state, its defined relative to its parent state. In your scenario dashboard.welcome is defined as a child state of dashboard. The routes to substate is relative to the parent and is {parent url}/{child url}. Hence you should use either of the below 2 to route to that state:
Using $state.go change the state by specifying state name
$state.go('dashboard.welcome');
Using $location.path change the route by specifying url
$location.path('/dashboard/welcome');
It sounds like you want links to /welcome to be for state dashboard.welcome. Here is a plunker showing how this can be done. I show two sets of dashboard and welcome states. The first set of states (dashboard & welcome) shows that /dashboard/welcome will bring you to the dashboard.welcome state.
The second set (dashboard2 & welcome2) shows that /welcome will go to state dashboard2.welcome2. I believe this is what you were looking for.
If you hover over the links you can see where they will take you.
https://plnkr.co/edit/AVKPFa?p=info
Nested routes in ui-router get nested urls. I would however recommend using named-views for this kind of structure. You can find more info about it here:
https://github.com/angular-ui/ui-router/wiki/Multiple-Named-Views
The gist of it is: you can specify a named component (ui-view) for your left menu navigation and another one for content, which gives you much more control down the line, because named components can be overwritten in child states or they can keep the default template, depending on your needs.
Example:
.state('root', {
url: '',
abstract: true,
views: {
'header': {
templateUrl: 'templates/partials/header.html',
controller: 'headerCtrl'
},
'logo': {
templateUrl: 'templates/partials/logoView.html'
},
'footer':{
templateUrl: 'templates/partials/footer.html',
controller: 'footerCtrl'
}
}
})
.state('root.login', {
url: '/login',
views: {
'header#': {
template: ''
},
'container#': {
templateUrl: 'templates/login.html',
controller: 'loginController'
}
}
})
.state('root.report', {
url: '/report',
views: {
'container#': {
templateUrl: 'templates/eu_dashboard.html',
controller: 'reportController'
}
}
})
And in your index.html:
<div ui-view="logo"></div>
<div ui-view="header"></div>
<div id="mainView" ui-view="container"></div>
<div ui-view="footer"></div>
I have tried several examples on using the ui-router and the state manager. My nested views and routes are not working as I hoped. Here is an example of how I am configuring the states:
$stateProvider
.state("main", {
abstract: true,
url: "/main",
views: {
"layout": {
templateUrl: "main.index.html"
},
"mainNavigation#main": {
templateUrl: "main-navigation-partial.html"
}
},
onEnter: function() {
console.log("enter main");
}
})
.state("main.dashboard", {
url: "/dashboard",
views: {
"container#main": {
templateUrl:"main-dashboard.html"
}
}
});
As you can see, I have an abstract state named main. All main views will use the mainNavigation view. There is also a container view area where the content for each section will reside. There is an index.html that will be used by all states. So, I may, down the road have an abstract state name client with accompanying states and views.
I can see the individual html files being loaded, but the views are not being populated in the correct named view areas. I have created a plunk that demonstrates how I want to manage my templates and views.
Your main state is loading main.index.html into a ui-view named layout. In your plunker, your root ui-view is unnamed. So to fix this, add a name to that ui-view.
http://plnkr.co/edit/xKDcuk99OACQR73LR0hf?p=preview
<div ui-view='layout'>
Or, you could leave the ui-view unnamed and change the view to reflect that.
"": {
templateUrl: "main.index.html"
}
For more on view naming, see the ui-router wiki.
I have three states: one abstract state and two concrete states inheriting from the abstract one. I am transitioning from one state to another and I noticed that the variables that were in the $scope in one state are no longer in $scope after I have transitioned to the other state: see $scope.signupForm.member.email below.
Can someone please advise?
My UI router configuration:
$stateProvider
.state('signup', {
abstract: true,
views: {
'#': {
templateUrl: 'signup/views/signup.html'
}
}
})
.state('signup.form', {
url: '/signup',
views: {
'#signup': {
controller: 'SignupCtrl',
templateUrl: 'signup/views/signup.form.html'
}
}
})
.state('signup.success', {
url: '/signup/success',
views: {
'#signup': {
controller: 'SignupCtrl',
templateUrl: 'signup/views/signup.success.html'
}
}
})
Relevant snippet from my controller:
signupService.signup($scope.signupForm)
.success(function () {
//TODO: issue with controller no longer being in scope: signupForm.member.email is not displayed in template
$state.go('signup.success');
});
My email input (from signup.form.html):
<input type="email" name="email"
placeholder="{{'SIGNUP_FORM_EMAIL' | translate}}"
ng-model="signupForm.member.email" ng-required="true"
ng-pattern="EMAIL_PATTERN"
class="form-control"/>
Where I try to display the email (from signup.success.html):
<div class="panel-body">
success!
check your email at: {{signupForm.member.email}}
</div>
edit 1:
If I pull up the controller one level - by putting it into the abstract state i.e. 'signup', then signupFormCtrl - the angular form controller - is undefined!
<form name="signupFormCtrl" ng-submit="signup()" novalidate>
edit 2:
This is what I tried:
.state('signup', {
abstract: true,
views: {
'#': {
controller: 'SignupCtrl',
templateUrl: 'signup/views/signup.html'
}
}
})
.state('signup.form', {
url: '/signup',
views: {
'#signup': {
templateUrl: 'signup/views/signup.form.html'
}
}
})
.state('signup.success', {
url: '/signup/success',
views: {
'#signup': {
templateUrl: 'signup/views/signup.success.html'
}
}
})
Thre is a working plunker
This is feasable with UI-Router built-in features. We will need to introduce controller for our base state:
.state('signup', {
abstract: true,
templateUrl: 'signup/views/signup.html',
controller: 'SignupBaseCtrl',
})
Inside of this controller we would define a Model inside of a $scope:
.controller('SignupBaseCtrl', ['$scope', function ($scope) {
$scope.signupForm = { member : { email : null }};
}])
And now, if we would work with a model like this:
{{signupForm.member.email}}
In any of our child states, we would be accesing the same model, the same reference object singupForm.
And how it is possible? how it is working? All is clearly explained here:
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.
You can also check: Controller from Parent Layout is not access by child views
Data from one scope can not be accessed from a different scope. try using the rootScope for data that is to be used across scopes use $root in templates as in {{$root.signupForm.member.email}} and $rootScope in controllers as in $rootScope.signupForm.member.email
I'm having an issue with nested states in UI-Router. I have a two states, and upon button click it should transition to another state, and the url changes, but the template does not. Here is my code for the state logic:
$stateProvider.state('accounts', {
url: '/accounts',
views: {
'menu': {
templateUrl: 'templates/menu.html',
controller: 'MenuController'
},
'main': {
templateUrl: 'templates/accounts.html',
controller: 'AccountsController'
}
}
});
$stateProvider.state('accounts.detail', {
url: '/:accountID',
views: {
'main': {
templateUrl: 'templates/accounts.detail.html',
controller: 'AccountsDetailController'
}
}
});
And my button logic: $state.go('accounts.detail', { accountID : account.accountID});
Both of my views are wrapped up in ui-view tags. All other root states work correctly (/home, /orders) however /accounts/:accountID will not trigger the template to load and transition. Any insight will be greatly appreciated.
<ion-view /> is not the equivalent of <ui-view />, in Ionic Framework it is just a container to insert header/footer bars and content.
use <ion-nav-view /> http://ionicframework.com/docs/api/directive/ionNavView/
and reference by name this nested view in your parent view: <ion-nav-view name="main" />
My issue was that I wasn't referring to my views correctly. Because of how they are nested, I needed to use the absolute name to cause the view to show. The linked UI-router documentation describes my issue.
https://github.com/angular-ui/ui-router/wiki/Multiple-Named-Views#view-names---relative-vs-absolute-names
I would like to make a bootstrap tabset with each tab having it's own controller. Can anyone point me in which direction I should go.
Currently I have made several difference controllers, which I would like to be used in a tabset instead of having them displayed as a different route.
I know I could fake it by having the tabset in the difference controller templates displaying the given controllers tab as active, but I would like to be able to have a main TabController with several child controllers (for each tab)
If you are using angular ui router you can use nested states to do this.
Create an abstract state with a view that contains the tabs and a nested ui-view
Create a child state for each of your tabs, each inheriting from the abstract state
Each child state can set the content of the nested ui-view, and define a controller
$stateProvider.state( 'tabs', {
abstract: true,
url: 'tabs',
views: {
"tabs": {
controller: 'TabsCtrl',
templateUrl: 'tabs.html'
}
}
})
.state('tabs.tab1', {
url: '', //make this the default tab
views: {
"tabContent": {
controller: 'Tab1Ctrl',
templateUrl: 'tab1.html'
}
}
})
.state('tabs.tab2', {
url: '/tab2',
views: {
"tabContent": {
controller: 'Tab2Ctrl',
templateUrl: 'tab2.html'
}
}
});
Why don't you put a directive on each tab that has it's own controller? If you are using 1.x. Separate your code out by directive not tabs