I have this classic ionic view refresh problem:
.state('app', {
url: '/app',
abstract: true,
templateUrl: 'templates/menu.html',
controller: 'AppCtrl'
})
.state('app.menu', {
url: "/menu",
views: {
'menuContent': {
templateUrl: "templates/headmenu.html"
}
}
})
.state('app.menu.media', {
url: "/media/:MediaType",
views: {
'media-tab': {
templateUrl: "templates/tabs/media.html",
controller: "MediaCtrl"
}
}
}).state('app.settings', {
url: "/settings",
views: {
'settingsContent': {
templateUrl: "templates/settings.html",
controller: "SettingsCtrl"
}
}
})
The app start from 'app.menu.media' state, I click on 'settings', the view is loaded, but the problem occurs when I try going back to 'app.menu.media' state, the view is not refreshed (I still have the 'settings' view)
One of the solutions suggested by other devs is using ui-sref-opts="{reload: true, notify: true}" inside a <a ui-sref="app.menu.media" ...> ..</a> tag, but it's not smooth, the users feel like if the app freezes for a short time, and the same goes for the javascript solution: $state.go('app.menu.media', {}, {reload: true});
I know that the controller is not loaded when it is in the same state, but in this example we have different controllers: "MediaCtrl" and "SettingsCtrl", so why we have this problem? Is there any clean solution to fix this problem?
Edit:
This problem occurs even if I'm disabling the cache: $ionicConfigProvider.views.maxCache(0);
After Debuging the app, I found that the problem was not related to cache, but it doesn't mean that enabling the views cache will not affect the app, disabling the cache was important for my case.
So the problem was related to the fact that I was using the same state "app" for both 'media' and 'settings' views, with different view names: "settingsContent" for "app.settings" and "menuContent" for "app.menu". So I had to add these two Ion-nav-views to the template Url linked to the "app" state, which is "menu.html", juste like this:
<ion-nav-view name="menuContent"></ion-nav-view>
<ion-nav-view name="settingsContent"></ion-nav-view>
So when I go back to "app.menu.media" state, the view is indeed loaded, but the "settings" view is still there, and it is hiding my new requested view "media"!
So what I did to fix this issue is to use the same Ion View name:
.state('app.menu', {
url: "/menu",
views: {
'THESAMENAME': {
templateUrl: "templates/headmenu.html"
}
}...
....
.state('app.settings', {
url: "/settings",
views: {
'THESAMENAME': {
templateUrl: "templates/settings.html",
controller: "SettingsCtrl"
}
}
And inside menu.html, I deleted the two previous lines, and replaced theme with:
<ion-nav-view name="THESAMENAME"></ion-nav-view>
I hope this could help someone
Related
I want to be able to click on the anchor in the #leftDiv and make the UI router template open in the #rightDiv. So, when I click on Hello Plunker 1 in the #leftDiv, I want peopleOne.html to open in the #rightDiv. When I click on Hello Plunker 2, I want peopleTwo.html to replace peopleOne.html in the #rightDiv.
Here is a Plunker - https://plnkr.co/edit/T8RTgea8VccA9mdBABGC?p=preview
Can someone provide insight into why this is not working.
Script.js
var Delivery = angular.module('Delivery', ['ui.router']);
angular
.module('Delivery')
.config(function($stateProvider, $locationProvider) {
$locationProvider.hashPrefix('');
$stateProvider
.state('home', {
url: '/Delivery',
views: {
'view': {
templateUrl: 'Delivery.html',
},
},
})
.state('peopleOne', {
url: '/peopleOne',
parent: 'home',
views: {
'view#': {
templateUrl: 'peopleOne.html'
}
},
})
.state('peopleTwo', {
url: '/peopleTwo',
parent: 'home',
views: {
'view#': {
templateUrl: 'peopleTwo.html'
}
},
})
})
A few problems I've noticed:
First, put a console.log after the $stateProvider configuration calls to setup your routes. You will see that this code is never even called. Your angular app is not quite set up properly. You are using dat-ng-app in the index template, when you should be using ng-app. Otherwise angular is never actually used.
The next issue is in your $stateProvider configuration. I am not sure which documentation you are following, but your configurations for the states should look something like this:
# Set the default state
$urlRouterProvider.otherwise('/home')
# Configures home, peopleOne, and peopleTwo states
$stateProvider
.state('home', {
url: '/home',
templateUrl: 'home.html'
})
.state('peopleOne', {
url: '/peopleOne',
templateUrl: 'peopleOne.html',
parent: 'home'
})
.state('peopleTwo', {
url: '/peopleTwo',
templateUrl: 'peopleTwo.html',
parent: 'home'
})
Finally, when actually creating the links in your template, I find it easier to use the ui-sref tag, which allows you to create a link based on a state name. So a link to the peopleOne state would look like this: <a ui-sref="peopleOne"></a>.
I've attached a plunker based off your original code
https://plnkr.co/edit/NazuoFoDOa3VGR6smoyH
I have a route definition as follows:
$stateProvider
.state('vehicles', {
url: '/vehicles',
templateUrl: 'foo/bar1.html'
}).state('vehicles.id', {
url: '/{id}',
templateUrl: 'foo/bar3.html'
}).state('vehicles.create', {
url: '/create',
templateUrl: 'foo/bar2.html',
controller: 'VehicleCreateController'
});
I have a button that does
$state.go("vehicles.create");
The problem is, that while the URL changes correctly, the page remains the same. Only after the second click, the correct template appears.
After a hint from my colleague I realized, that it was the state definitions that caused the problem. Reordering the states from "more specific" (URL-wise - i.e. /create) to less specific (/{id}) did the trick. So the thing that was wrong was having the more generic URL /vehicles/{id} before the very similar, but less generic /vehicles/create.
So here's the improved version:
$stateProvider
.state('vehicles', {
url: '/vehicles',
templateUrl: 'foo/bar1.html'
}).state('vehicles.create', {
url: '/create',
templateUrl: 'foo/bar2.html',
controller: 'VehicleCreateController'
}).state('vehicles.id', {
url: '/{id}',
templateUrl: 'foo/bar3.html'
});
use : for your params and ? to make those params optional if you need.
check the below code snippet, for routing with params.
$stateProvider
.state('contacts.detail', {
url: "/contacts/:contactId",
templateUrl: 'contacts.detail.html',
controller: function ($stateParams) {
// If we got here from a url of /contacts/42
expect($stateParams).toBe({contactId: "42"});
}
})
check this for more clear view on routing.
I'm working in a project with ionic and when testing the app in the browser I go to the child state of one view and later i refresh the page and the back button disappears. I print the "$ionicHistory" object and the "backView" property is null.
app.config(function($stateProvider, $urlRouterProvider) {
$stateProvider
.state('app', {
url: '/app',
abstract: true,
templateUrl: 'templates/tabs/tabs.html'
})
.state('app.parent', {
url: '/parent',
views: {
'tab-parent': {
templateUrl: 'templates/parent/parent.html'
}
}
})
.state('app.parent.child', {
url: '/child',
views: {
'tab-parent#app': {
templateUrl: 'templates/parent/child.html'
}
}
})
$urlRouterProvider.otherwise('/app/parent');
});
How i can solved it?
Thank you.
as #gianlucatursi says, refreshing the page means that the object holding the application browsing history is reinitialized. the back button is only displayed if there are entries in the history object
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
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'