Angular UI-Router - javascript

I am using angular pre-defined shell to build a basic working poc for my new project. Basic template is loaded by using ng-include in the index.html. But other views are linked to menu components using ui-router. Everything in the code side is perfect still I am having problem in loading the views when clicking on the menu items after running the shell.
I added the href tags to check if that works, but still no use.Could someone help me with this.
<li ng-class="{active: $state.includes('app')}">
<a href="">
<i class="fa fa-desktop"></i>
<span class="nav-label">{{ 'APPVIEWS' | translate }}</span>
<span class="pull-right label label-primary">SPECIAL</span>
</a>
<ul class="nav nav-second-level" ng-class="{in: $state.includes('app')}">
<li ui-sref-active="active">
<a ui-sref="app.contacts">Contacts</a>
</li>
</ul>
</li>
My states in config file are defined as :
function config($stateProvider, $urlRouterProvider, $ocLazyLoadProvider, IdleProvider, KeepaliveProvider) {
IdleProvider.idle(5);
IdleProvider.timeout(120);
$urlRouterProvider.otherwise("/dashboards/dashboard_1");
$ocLazyLoadProvider.config({
debug: false
});
$stateProvider
.state('app', {
abstract: true,
url: "/app",
templateUrl: "views/common/content.html",
})
.state('app.contacts', {
url: "/contacts",
templateUrl: "views/contacts.html",
data: { pageTitle: 'Contacts' }
})
}

If the "app" route is going to be abstract you can take the url off of it
the app.contacts could have a parent of the app route
$stateProvider
.state('app', {
"abstract": true,
templateUrl: "views/common/content.html",
})
So when i set up my "app" route as abstract and wanted all my other views to be nested inside of that route i defined a named view that they would go into
so my "app" template would be like
// html stuff here for app
<div ui-view="content"></div>
// more html stuff here
then my app.contacts route would look like
.state('app.contacts', {
parent: "app"
url: "/contacts",
views: {
"content": {
templateUrl: "views/contacts.html"
}
},
data: { pageTitle: 'Contacts' }
})

Related

ionic view is not cached, controller reloads

I have an ionic app with some views, when I start the app I will go to my main view, when the controller of the view is initialized I will load some data.
The problem is, when I navigate from that view, using tabs, that controller is sometimes destroyed, so when I navigate back the data will have to load again.
I have done some testing with '$ionicView.loaded' and '$ionicView.unloaded' and it seems to be pretty random when the view is unloaded.
This is my state configuration
.config(function($stateProvider, $urlRouterProvider) {
$stateProvider
.state('login', {
name: "login",
url: "/login",
templateUrl: "templates/login.html",
controller: 'loginCtrl'
})
// This is a sidemenu
.state('app', {
url: '/app',
abstract: true,
templateUrl: 'templates/menu.html'
})
.state('app.hms', {
url: '/hms',
views: {
'menuContent': {
templateUrl: 'templates/hms-app.html',
controller: 'HmsCtrl'
}
}
})
.state('app.hms-item', {
url: '/hms/:hmsId',
views: {
'menuContent': {
templateUrl: 'templates/hms-item.html',
controller: 'HmsItemCtrl'
}
},
params: {
item: null
}
})
.state('app.history', {
url: '/history',
views: {
'menuContent': {
templateUrl: 'templates/history-list.html',
controller: 'HistoryCtrl'
}
}
})
.state('app.history-item', {
url: '/history/:itemId',
views: {
'menuContent': {
templateUrl: 'templates/history-item.html',
controller: 'HistoryItemCtrl'
}
},
params: {
itemId: null
}
})
.state('app.event-new', {
url: '/event/new',
views: {
'menuContent': {
templateUrl: 'templates/event-new.html',
controller: 'EventCtrl as ctrl'
}
}
})
.state('app.risk-new', {
url: '/risk/new',
views: {
'menuContent': {
templateUrl: 'templates/risk-new.html',
controller: 'RiskCtrl as ctrl'
}
}
});
// if none of the above states are matched, use this as the fallback
// this is also the first page, 'front page'
$urlRouterProvider.otherwise('login');
});
My tabs are defined in each .html template I need them in (so not the login page, for instance), they are defined after the closing of ion-content:
<div class="tabs tabs-icon-top">
<a class="tab-item" href="#/app/hms">
<i class="icon ion-home"></i>
Home
</a>
<a class="tab-item" href="#/app/event/new">
<i class="icon ion-document-text"></i>
Hendelse
</a>
<a class="tab-item" href="#/app/risk/new">
<i class="icon ion-document-text"></i>
Risikorapport
</a>
<a class="tab-item" href="#/app/history">
<i class="icon ion-ios-list"></i>
Historikk
</a>
What could cause the controller of the view to be destroyed when I navigate from it?
Turns out, Ionic removes caching for views that are "forward", so when navigating back from a forward view that $scope will be destroyed.
This is explained under caching here
By default, when navigating back in the history, the “forward” views are removed from the cache. If you navigate forward to the same view again, it’ll create a new DOM element and controller instance. Basically, any forward views are reset each time. This can be configured using the $ionicConfigProvider:
So, configuring the $ionicConfigProvider with $ionicConfigProvider.views.forwardCache(true); fixed my issue
I also saw I could fix my issue another way, and that was to not make my views "forward", when navigating to a new view using href, we can specify what sort of view that should be, by doing nav-direction="swap", available directions are forward, back, enter, exit, and swap
Example of tab, with direction
<a class="tab-item" nav-direction="swap" nav-transition="android" href="#/app/home">
<i class="icon ion-home"></i>
Home
</a>

UI-Router nested state view not rendering (rendering parent view)

Using ui-router in angular, I have setup a 'users' state where all the users in my application are returned in an ng-repeat. I am trying to create the show state for the users profile page however the view of the child state is inheriting the parent view 'templates/users/index.html' (Here is my app.js:
.state('users', {
abstract: true,
name: 'users',
url: '/users',
views: {
nav: {
templateUrl: 'templates/navbar.html',
controller: 'MainCtrl'
},
content: {
templateUrl: 'templates/users/index.html',
controller: 'UsersCtrl'
}
}
})
.state('users.show', {
url: '/show/:id',
views: {
nav: {
templateUrl: 'templates/navbar.html',
controller: 'MainCtrl'
},
content: {
templateUrl: 'templates/users/show.html',
controller: 'UsersCtrl'
}
}
});
Here is my index.html:
<div class="container">
<label>Search: <input ng-model="searchText"></label>
<a ui-sref="users.show({id: user.id})" ng-click="showUser(user.id)" ng-repeat="user in users | filter:searchText ">
<h2> {{ user.name }} </h2>
<h3> {{ user.email }} </h3>
<button ng-click="createConversation({sender_id: current_user.id, recipient_id: user.id})" >Message</button>
</a>
</div>
The url in the browser is http://localhost:8080/#/users/show/2 however the html file being inherited in the users.show state is the parents users (index.html) file.
Any help appreciated!
The nested view users.show will actually render within the user view. So you just need to place another <ui-view> within templates/users/index.html where the show view will render.

Angular ui-router Nested states open from url

I'm try to use ui-router to manage nested views on a single page app.
Let's say I want to create a dashboard application with a common area and multiple views.
The main and the nested states are handled like this:
$stateProvider.state('home', {
url: '/',
template: 'MY HOME PAGE'
})
.state('login', {
url: '/login',
templateUrl: '/pages/login.html'
})
.state('registration', {
url: '/registration',
templateUrl: '/pages/registration.html'
})
.state('dashboard', {
url: '/dashboard',
templateUrl: '/pages/dashboard/dashboard.html'
})
.state('dashboard.me', {
url: '/me',
templateUrl: '/pages/dashboard/me.html'
})
.state('dashboard.messages', {
url: '/messages',
templateUrl: '/pages/dashboard/messages.html'
})
.state('dashboard.friends', {
url: '/friends',
templateUrl: '/pages/dashboard/friends.html'
});
The dashboard HTML page is the following:
<div class="container" ng-controller="dashboardCtrl" ng-init="init()">
<h2>DASHBOARD</h2>
<ul>
<li><a ui-sref="dashboard.me">Me</a></li>
<li><a ui-sref="dashboard.messages">My Messages</a></li>
<li><a ui-sref="dashboard.friends">My Friends</a></li>
</ul>
<div ui-view></div>
The above HTML is also included inside an ui-view.
Everything works fine if I navigate my application using the anchors.
By the way if I try to go directly to myhost/dashboard/me or myhost/dashboard/friends (every path with two levels of nested views) the app doesn't work. I get an angular (unexpected token <) but I don't think it's relevant...
It seems like it's not able to resolve the first level of nested view.
The following images show the HTML obtained when the navigation is done using anchors:
and the HTML obtained when the page is called directly from the browser address link:
Any ideas? Thanks.
The problem is that angular is not loading, hence the error. The code looks correct to me though, unless you missed the closing tag for
<div class="container" ng-controller="dashboardCtrl" ng-init="init()">
?
JavaScript State Configurations :
$stateProvider.state('home', {
url: '/',
template: 'MY HOME PAGE'
})
.state('login', {
url: '/login',
templateUrl: '/pages/login.html'
})
.state('registration', {
url: '/registration',
templateUrl: '/pages/registration.html'
})
.state('dashboard', {
url: '/dashboard',
abstract: true,
parent: 'home',
templateUrl: '/pages/dashboard/dashboard.html'
})
.state('dashboard.me', {
url: '/me',
views: {
'#dashboard_view': {
templateUrl: '/pages/dashboard/me.html'
}
}
})
.state('dashboard.messages', {
url: '/messages',
views: {
'#dashboard_view': {
templateUrl: '/pages/dashboard/messages.html'
}
}
})
.state('dashboard.friends', {
url: '/friends',
views: {
'#dashboard_view': {
templateUrl: '/pages/dashboard/friends.html'
}
}
});
Dashboard HTML
<div class="container" ng-controller="dashboardCtrl" ng-init="init()">
<h2>DASHBOARD</h2>
<ul>
<li><a ui-sref="dashboard.me">Me</a></li>
<li><a ui-sref="dashboard.messages">My Messages</a></li>
<li><a ui-sref="dashboard.friends">My Friends</a></li>
</ul>
<div ui-view="dashboard_view"></div>
You can get more details:
Multiple Named Views
Nested States and Nested Views

State.go() shows the url in address page but doesn't refresh the html view

I have a problem when whorking on Ionic with angularJs, the problem is in routing system when I try to develop a login page .
In the controller part of code i'l trying to load a welcome page calle 'dash' with state.go(psc.dash)
here is my controller.js :
angular.module('starter.controllers', [])
.controller('LoginCtrl', function($location, $state) {
var user = this;
user.login = function() {
console.log("LOGIN user: " + user.email + " - PW: " + user.password);
setTimeout(function() {
state.go('psc.dash');
}, 20);
};
})
.controller('DashCtrl', function($scope, $location) {});
here is my App.js:
.config(function($stateProvider, $urlRouterProvider, $locationProvider) {
$stateProvider
.state('login', {
url: "/login",
views: {
'login': {
templateUrl: "templates/login.html",
controller: "LoginCtrl"
}
}
})
.state('psc', {
url: "/psc",
abstract: true,
templateUrl: "templates/psc.html"
})
.state('psc.dash', {
url: "/dash",
views: {
'psc-dash': {
templateUrl: "templates/dash.html",
controller: "DashCtrl"
}
}
})
;
// if none of the above states are matched, use this as the fallback
$urlRouterProvider.otherwise('/login');
});
and here is my login.html
<div class="list list col span_1_of_2 " ng-controller="LoginCtrl as loginCtrl">
<label class="item item-input">
<span class="input-label">E-mail</span>
<input type="text" ng-model="loginCtrl.email">
</label>
<label class="item item-input">
<span class="input-label">password</span>
<input type="password" ng-model="loginCtrl.password">
</label>
<div>
<div class="col span_2_of_3">forgot password ? </div>
<button class="button button-positive col span_1_of_3" ng-click="loginCtrl.login()">
connect
</button>
</div>
</div>
The problem is when I click on connect button the url '/psc/dash' appears in address bar but the login view stay displayed and the page is not reloaded with the new html view.
EDIT
This is wrong. There is a discrepancy in the ui-router documentation: states that inherit from an abstract state are prefixed with their parents URL.
The reason is that although your 'psc.dash' nested state is defined as a child of the 'psc' state, the URL you have assigned to it is not automatically prefixed with its parent's URL.
You want to change the 'psc.dash' state definition to this:
.state('psc.dash', {
url: "/psc/dash",
views: {
'psc-dash': {
templateUrl: "templates/dash.html",
controller: "DashCtrl"
}
}
})
Take a look at the ui-router documentation for why this is:
What Do Child States Inherit From Parent States?
Child states DO inherit the following from parent states:
Resolved dependencies via resolve Custom data properties
Custom data properties
Nothing else is inherited (no controllers, templates, url, etc).
The service is $state so the code should be:
$state.go('psc.dash');
You can get rid of the controller definition in your HTML:
<div class="list list col span_1_of_2" ng-controller="LoginCtrl as loginCtrl">
</div>
use this instead:
<div class="list list col span_1_of_2">
</div>
and change the state this way:
.state('login', {
url: "/login",
views: {
'login': {
templateUrl: "templates/login.html",
controller: "LoginCtrl as loginCtrl"
}
}
})
You don't need to define the controller twice.
Your login.html template does not use <ion-view>. It would be interesting to see a plunker of your project.
Another things I am not sure about is the state definition. If the view login is not wrapped in another container you should write it like this:
.state('login', {
url: "/login",
templateUrl: "templates/login.html",
controller: "LoginCtrl as loginCtrl"
})
SOLUTION:
my psc route should be named 'login' to be identified
.state('psc', {
url: "/psc",
views: {
'login': {
abstract: true,
templateUrl: "templates/psc.html"
}
}
})

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'

Categories