How to update only the named view using UI-Router - javascript

I am creating a web app to help students in science, history and math. When you first land on the site I have a home/landing page. When you click get started I route to /exam/instructions. Each of my steps instructions, math and science our templates that I load into the ui-view="exam-detail". Currently the whole ui-view loads when I navigate to and from instructions through sciences. Ideally I simply want an area for pagination and an area for the subject matter and only want the ui-view="exam-detail" to update with the correct template.
I have not used UI-Router at all and any assistance would be greatly appreciated.
index.html
<div ui-view></div>
state-exam>exam.html
<div class="state-exam">
<nav ui-view="exam-pagination"></nav>
<section ui-view="exam-detail"></section>
</div>
route.js
(function() {
'use strict';
angular
.module('studentPortal')
.config(routeConfig);
function routeConfig($stateProvider, $urlRouterProvider) {
$stateProvider
.state('home', {
url: '/',
templateUrl: 'app/main/main.html',
controller: 'MainController',
controllerAs: 'main'
})
.state('exam', {
url: '/exam/:step',
abstract: true,
templateUrl: 'app/state-exam/exam.html',
controller: 'ExamController',
controllerAs: 'examController',
})
.state('exam.instructions', {
url: '/instructions',
views: {
'exam-pagination':{
templateUrl: 'app/state-exam/exam-pagination.html'
},
'exam-detail' : {
templateUrl: 'app/state-exam/exam-instructions.html'
}
}
})
.state('exam.math', {
url: '/math',
views: {
'exam-pagination':{
templateUrl: 'app/state-exam/exam-pagination.html'
},
'exam-detail' : {
templateUrl: 'app/state-exam/exam-math.html'
}
}
});
$urlRouterProvider.otherwise('/');
}
})();

There is a working plunker
There is a similar Q & A in fact, with working plunker:
Angular UI Router - Nested States with multiple layouts
Solution here, is to move the static view from child to parent. It won't be reloaded for each child (view is reloaded only if parent state is changed). We will use absolute naming (see included links for more details)
So this is the code adjustment
.state('exam', {
url: '/exam/:step',
abstract: true,
// the root view and the static pagination view
// will be defined here, so we need views : {}
views: {
'':{
templateUrl: 'app/state-exam/exam.html',
controller: 'ExamController',
controllerAs: 'examController',
},
// absolute naming targets the view defined above
'exam-pagination#exam':{
templateUrl: 'app/state-exam/exam-pagination.html'
},
}
})
.state('exam.instructions', {
url: '/instructions',
views: {
// 'exam-pagination':{}, // defined in parent
'exam-detail' : {
templateUrl: 'app/state-exam/exam-instructions.html'
}
}
})
.state('exam.math', {
url: '/math',
views: {
// 'exam-pagination':{}, // defined in parent
'exam-detail' : {
templateUrl: 'app/state-exam/exam-math.html'
}
}
});
Also check this to get more details about absolute view naming
Angular UI router nested views
Angular-UI Router: Nested Views Not Working
The working example is here

Related

Error loading child state with ui-router

I have a working app though I am changing the architecture, cutting views down into smaller, more manageable documents. I currently have a state with a child state.
.state('patents', {
url: '/patents',
templateUrl: 'p3sweb/app/components/patents/views/list-patents.htm',
controller: 'listPatentsCtrl',
controllerAs: '$ctrl',
})
.state('patents.patent', {
url: '/{patentId}/:patentHref',
templateUrl: 'p3sweb/app/components/patents/views/patent-item.htm'
})
When I click on a table row in state patents, up pops patents.patent view below it.
I have now changed it so the child patents.patent state has multiple views like so:
.state('patents.patent', {
url: '/{patentId}/:patentHref',
templateUrl: 'p3sweb/app/components/patents/views/patent-item.htm',
views: {
'#': {
templateUrl: 'p3sweb/app/components/patents/views/patent-item.htm'
},
'patentinfo#patents.patent': {
templateUrl: 'p3sweb/app/components/patents/views/ui-views/patent-info.htm',
controller: 'patentInfoCtrl',
controllerAs: '$ctrl'
},
'patentcostanalysis#patents.patent': {
templateUrl: 'p3sweb/app/components/patents/views/ui-views/patent-costanalysis.htm',
controller: 'patentCostAnalysisCtrl',
controllerAs: '$ctrl'
},
'patentrenewals#patents.patent': {
templateUrl: 'p3sweb/app/components/patents/views/ui-views/patent-renewals.htm',
controller: 'patentRenewalsCtrl',
controllerAs: '$ctrl'
}
})
When I now click on the row in state patents, the patents.patent view replaces the parent view. So instead of displaying below it, it's the only view displayed.
Question
How do I resolve the issue that has occurred now I have included multiple views in patents.patent state?
So it turns out, after reading ui-router wiki, using # creates an absolute path, and I wasn't targeting the view in the parent state patents.
So instead of
views: {
'#': {
templateUrl: 'p3sweb/app/components/patents/views/patent-item.htm'
}
It should of been
views: {
'#patents': {
templateUrl: 'p3sweb/app/components/patents/views/patent-item.htm'
}

Two states with views views defined for one

I'm trying to modulise my app using angular-ui-router to define a website with 2 states: main and checkout. The main state should haves multiple "section" tags which im trying to define as ui-view items. I can't tell what's wrong with my routes setup but I get a feeling that the main.html is not being loaded. Any advise on whats wrong with my definition... I could avoid using views for the secions and just use ng-include...
routes.js
app.config(function($stateProvider, $urlRouterProvider) {
$urlRouterProvider.otherwise('/main');
$stateProvider
.state('main', {
url: '/main',
controller: 'MainCtrl',
templateUrl: 'templates/main.html',
views:{
'home': {
templateUrl: 'templates/main.home.html'
},
'about': {
templateUrl: 'templates/main.about.html'
},
'services': {
templateUrl: 'templates/main.services.html'
},
'shop': {
templateUrl: 'templates/main.shop.html',
controller: 'ShopCtrl'
}
}
})
.state('checkout', {
url: '/checkout',
templateUrl: 'templates/checkout.html',
controller: 'CheckoutCtrl'
});
index.html
<div ui-view id="app-ui-view"></div>
templates/main.html
<section ui-view="home" id="home"></section>
<section ui-view="about" id="about"></section>
<section ui-view="services" id="services"></section>
<section ui-view="shop" id="shop"></section>
Basically the page loads but main or checkout states don't load. How am i nesting things wrong?
By not specifying a parent you map both states to the default ui-view in the index.html. So when accessing main state there won't be any templates linked to the default ui-view, the only one present in the existing html.
so the main state should have this definition:
.state('main', {
url: '/main',
views:{
'': {
controller: 'MainCtrl',
templateUrl: 'templates/main.html',
},
'home#main': {
templateUrl: 'templates/main.home.html'
},
'about#main': {
templateUrl: 'templates/main.about.html'
},
'services#main': {
templateUrl: 'templates/main.services.html'
},
'shop#main': {
templateUrl: 'templates/main.shop.html',
controller: 'ShopCtrl'
}
}
})

Use same ui-view template for multiple states

I am using angularjs ui-router for a cordova app. I am trying to reuse a ui-view template (left-panel) for multiple states. This ui-view is for almost all the states except one state. I tried to refer many tutorials but still not able to implement what I want. Here is my code in app.js:
var angularApp = angular.module('angularApp', [
'ui.router',
]);
angularApp.config(function ($stateProvider, $urlRouterProvider) {
$urlRouterProvider.otherwise('/');
$stateProvider
.state('root', {
abstract: true,
views: {
'left-panel': {
templateUrl: 'templates/common-left-panel.html',
},
}
})
.state('root.home', {
url: '/',
views: {
'container#': {
templateUrl: 'templates/home.html',
}
}
})
.state('root.settings', {
url: 'settings',
views: {
'container#': {
templateUrl: 'templates/settings.html',
}
}
})
.state('root.category', {
url: 'category/:catId',
views: {
'container#': {
templateUrl: 'templates/category-nodes.html',
controller: 'ListCatNodesCtrl'
}
}
})
});
This is in index.html
<div ui-view="left-panel"></div>
<a ui-sref="root.settings">Settings</a>
<div ui-view="container"></div>
With this code, the home page is rendered properly. But when I click on the settings link, there isn't any change in screen or url. In rendered DOM, I get <a ui-sref="root.settings" href="#settings">Settings</a>. The same holds for category page as well. Basically I am developing an android app using cordova and angularjs. Loads of thanks in advance.

Angular UI Router - Views in an Inherited State

edit: Based on the answer by #actor2019 I want to update my question to better explain the problem:
Using Angular UI-Router(v0.0.2), I've setup the app to properly navigate between main "pages"/state, while inheriting the base state.
Index.html:
<div ui-view></div>
base.html:
<!-- Header -->
<div>
<!-- Header markup -->
<!-- Search View -->
<div ui-view="search"></div>
</div>
<!-- Page Content view -->
<div ui-view></div>
The issue is here in the app.js file. When I add the views parameter to the base state, everything stops working(100% blank page). Without that parameter, the page renders correctly, but I have no search view.
app.js:
$urlRouterProvider.otherwise('/');
//
// Now set up the states
$stateProvider
.state('base', {
abstract: true,
templateUrl: 'views/base.html',
views: {
"search": {
templateUrl: "views/search.html"
}
}
})
.state('base.home', {
url: "/",
templateUrl: "views/home.html"
})
.state('base.page2', {
url: "/page2",
templateUrl: "views/page2.html"
});
How do I add views to this parent 'base' state?
UPDATE:
The problem with #actor2019's answer here is that the search view gets reinitialized when the state changes. I'd like the views off the base level to persist through state changes.
The first obvious mistake:
You can't specify controller and template on the state while your using views. They are mutually exclusive...
This is because when there is no "views" but a controller and template on the state, UI-Router automatically creates the "views" property and pulls those properties to an "empty" view...
.state('base', {
abstract: true,
templateUrl: 'views/base.html', //Can't do this
views: { // when this is there.
"search": {
templateUrl: "views/search.html"
}
}
})
Instead do:
.state('base', {
abstract: true,
views: {
"": {
templateUrl: 'views/base.html'
},
"search": {
templateUrl: "views/search.html"
}
}
})
Second problem:
How views targeting works with nested views etc. is not very logical, it may work well if you restrict your self to one view in one view all the way down, but ones you start working with multiple named views it all gets confusing... Add unnamed views on top and many people gets lost...
The way views work in UI-Router is the worst part of UI-Router...
Given you example I am not even entirely sure of the way to target the search view from your abstract parent state... Might be:
.state('base', {
abstract: true,
views: {
"": {
templateUrl: 'views/base.html'
},
"search#base": {
templateUrl: "views/search.html"
}
}
})
If it can even be made to work... Alternatively you can move the search view out of base.html, but I guess you added it in there for a reason.
The whole view concept is the biggest reason why I ended up writing https://github.com/dotJEM/angular-routing instead.
The Child state should be home.search instead of header.search. In your case, you may want to write some abstract state to hold the layout,
base.html
<div class="row-fluid">
<div class="header">
<div class="span3" ui-view="logo"></div>
<div class="span9" ui-view="menu"></div>
</div>
</div>
<div class="row-fluid">
<div class="content">
<div class="span2" ui-view="sidebar"></div>
<div class="span10" ui-view="entry"></div>
</div>
</div>
in app.js
$stateProvider
.state('base',{
abstract:true,
url:'/',
templateUrl: viewBase+'base.html'
})
.state('base.main',{
url:'',
views:{
"logo":{
templateUrl:viewBase+'main/logo.html'
},
"menu":{
templateUrl:viewBase+'main/menu.html'
},
"sidebar":{
templateUrl:viewBase+'main/sidebar.html'
},
"entry":{
templateUrl: viewBase+'main/entry.html'
}
}})
According to the ui-router documentation, when the application is in a particular state—when a state is "active"—all of its ancestor states are implicitly active as well. So, for example, when the "contacts.list" state is active, the "contacts" state is implicitly active as well, because it's the parent state to "contacts.list". Child states will load their templates into their parent's ui-view. I'd reccomend looking over the section of their documentation entitled Nested States & Views to gain a fuller understanding of how to do this.
In the code you have provided us here, the parent state of the search template is home, while
.state('header.search', {
templateUrl: "views/search.html",
controller: "SearchCtrl"
})
implies that the parent state of the search template should be header in order for the view to get loaded correctly. So, I believe the following changes to your app.js will fix your issue.
app.js
$stateProvider
.state('home', {
url: "/",
views: {
'': {
templateUrl: "views/mainContent.html",
controller: "MainCtrl"
},
'header': {
templateUrl: "views/header.html"
},
'footer': {
templateUrl: "views/footer.html"
},
}
})
.state('home.search', {
views: {
'search': {
templateUrl: "views/search.html",
controller: "SearchCtrl"
}
})
.state('anotherPage', {
url: "/anotherPage",
templateUrl: "views/anotherPage.html"
});
This works for me.
$stateProvider
.state('base', {
abstract: true,
url:'/',
templateUrl: 'views/base.html'
})
.state('base.home', {
url: "",
views: {
"search#base": {
templateUrl: "views/searchOfHome.html"
}
//content#base, contentOfHome.html
}
})
.state('base.page2', {
url: "page2",
views: {
"search#base": {
templateUrl: "views/searchOfPage2.html"
}
//content#base, contentOfPage2.html
});
If 'base' is the root state, you don't need the '#base'

With angular.js using ui-router, how to only reload one view?

I have a fairly simple todo app using angular.js for which I am using the ui-router library. I looked through the ui-router example on github (https://github.com/angular-ui/ui-router/tree/master/sample) but was unable to figure out what I am doing wrong. In my app I have a sidebar navigation view (with the list of things todo) and a content view (which displays the todo item's details when clicked). The problem I have is that when I navigate to /todo/exampleItem the content view updates and the navigation panel is reloaded as well. This doesn't effect the functionality of the app but I would like to avoid the navigation panel flickering each time you click on an item.
Here is my code to handle the state changes:
app.config(function ($stateProvider) {
$stateProvider
.state('todo', {
url: "/todo",
views: {
"navPanel": {
templateUrl: "./navPanel.html",
controller: 'PanelController'
}
}
})
.state('todo/:item', {
url: "/todo/:item",
views: {
"PanelView": {
templateUrl: "./navPanel.html",
controller: 'PanelController'
},
"ContentView": {
templateUrl: "./content.html",
controller: 'ContentController'
}
}
})
});
In my index.html my views are set up as follows:
<div class="column" data-ui-view="PanelView"></div>
<div class="column" data-ui-view="ContentView"></div>
Is there some way I can stop the navPanel view from being reloaded each time a new item is clicked?
Based on the voted answer of that question angularjs ui-router - how to build master state which is global across app
app.config(['$stateProvider', function ($stateProvider) {
$stateProvider
.state('todo', {
abstract: true,
views: {
"navPanel": {
templateUrl: "./navPanel.html",
controller: 'PanelController'
}
}
})
.state('todo/:item', {
url: "/todo/:item",
views: {
"ContentView#": {
templateUrl: "./content.html",
controller: 'ContentController'
}
}
})
}]);

Categories