When using ui-router's $state.go() in my Ionic app, the onEnter method is not called.
The states would be transitioning from tab.settings to tab.queries. The state transitions fine. How do I get the onEnter method to be called from $state.go() call? Does onEnter not get called when entering child views?
Route
$stateProvider
.state('tab', {
url: "/tab",
abstract: true,
templateUrl: "templates/tabs.html"
})
.state('tab.queries', {
url: '/queries',
views: {
'tab-queries': {
templateUrl: 'templates/tab-queries.html',
controller: 'QueryCtrl',
onEnter: function() {
activate();
}
}
}
})
.state('tab.settings', {
url: '/settings',
views: {
'tab-settings': {
templateUrl: 'templates/tab-settings.html',
controller: 'SettingsCtrl'
}
}
});
SettingsCtrl
vm.removeAll = function() {
Data.removeAll().then(function() {
$state.go('tab.queries');
});
};
QueryCtrl
function activate() {
console.log('Activating');
}
Try moving the onEnter from within the view object up a level to the state object, I've just ran across this same problem and it seemed to work OK for me.
.state('tab.queries', {
url: '/queries',
views: {
'tab-queries': {
templateUrl: 'templates/tab-queries.html',
controller: 'QueryCtrl'
}
},
onEnter: function() {
activate();
}
})
Related
I'm trying to create a nested state config. When I go with the simple non-nested it works, but when I attempt to do it in a nested fashion it fails.
Below is the working code: (Notice the last state 'new')
app.config(
function($stateProvider, $urlRouterProvider) {
$stateProvider
.state('home', {
url: '/home',
templateUrl: '/views/home.html',
controller: 'MainCtrl',
resolve: {
qstnrPromise: ['qstnrs', function(qstnrs) {
return qstnrs.getAll();
}]
}
})
.state('questionnaires', {
url: '/questionnaires/{id}',
templateUrl: '/views/questionnaire.html',
controller: 'QuestionsCtrl',
resolve: {
questionnaire: ['$stateParams', 'qstnrs', function($stateParams, qstnrs){
return qstnrs.get($stateParams.id);
}]
}
})
.state('new', {
url: '/questionnaires/{id}/new',
template: '<h3> testy </h3>'
});
//$urlRouterProvider.otherwise('home');
$urlRouterProvider.otherwise(function($injector, $location){
$injector.invoke(['$state', function($state) {
$state.go('home');
}]);
});
});
If I attempt to change it with the following, it fails. The URL on browser changes but I don't receive the template.
.state('questionnaires.new', {
url: '/new',
template: '<h3>New question</h3>'
});
Have I understood states incorrectly? Or where have I gone wrong?
Try changing your new state to:
.state('new', {
url: '/new',
parent: 'questionnaires',
template: '<h3>New question</h3>'
})
See if that works for you.
You can make the questionnaires state abstract.
$stateProvider
.state('questionnaires', {
abstract: true,
url: '/questionnaires',
})
.state('questionnaires.new', {
// url will become '/questionnaires/new'
url: '/new'
//...more
})
But in this case you will need to add one additional nested state in order to implement the functionality you need for the $stateParams.id.
I have a < ion-side-menu > with links to my pages defined here:
app.config(function($stateProvider, $urlRouterProvider, $ionicConfigProvider) {
$stateProvider.state('content', {
url: "/content",
abstract: true,
templateUrl: "templates/sidemenu.html",
controller: 'SideController'
});
$stateProvider.state('content.home', {
url: "/home",
views: {
'menuContent': {
templateUrl: "templates/home.html",
controller: "HomeController"
}
}
});
$stateProvider.state('content.nearby', {
url: "/nearby",
views: {
'menuContent': {
templateUrl: "templates/nearby.html",
controller: "NearbyController"
}
}
});
$stateProvider.state('content.map', {
url: "/map",
views: {
'menuContent': {
templateUrl: "templates/map.html",
controller: "MapController"
}
}
});
$stateProvider.state('content.radar', {
url: "/radar",
views: {
'menuContent': {
templateUrl: "templates/radar.html",
controller: "RadarController"
}
}
});
$stateProvider.state('content.location-details', {
url: "/location-details/:index",
views: {
'menuContent': {
templateUrl: "templates/location-details.html",
controller: "DetailsController"
}
},
resolve: {
currentLocation: function($stateParams, shareService, NearbyFactory)
{
return NearbyFactory.getLocations()[$stateParams.index];
}
}
});
$urlRouterProvider.otherwise("/content/home");
});
I want to execute a method in my controllers when the user navigates to this page and when the page is left (for loading AJAX data or start listening to some Cordova sensors). Like this:
app.controller("HomeController", function()
{
$scope.onEnter = function(previous_page)
{
...
};
$scope.onExit = function(next_page)
{
...
};
});
I've already tried onEnter and onExit inside the $stateProvider state but afaik I don't have my $scope there.
What is the easiest/best/nicest way to get this functionality? It would be great if I could determine the previous/next page and if the user navigated back/forward. I tried this:
$scope.$on('$routeChangeStart', function(event, next, current)
{
console.log(next);
});
but this didn't work every time and it didn't fire when loading the page. This also seems a bit dirty to me because I'd have to implement this in every single controller.
Thank you!
You can use $ionicView.beforeEnter and beforeLeave.
Simply add this to your HomeController :
app.controller("HomeController", function()
{
$scope.$on('$ionicView.beforeEnter', function() {
//do stuff before enter
});
$scope.$on('$ionicView.beforeLeave', function() {
//do your stuff after leaving
});
});
You can check the docs of the $ionicView here.
You can try this. It works every time when it's loading the page:
$scope.$on('$locationChangeStart', function(event)
{
console.log(event);
});
This is my plunker http://plnkr.co/edit/GMfMcXgHguYjFYoxWEaM
1.) click the above live demo link
2.) click the "create" button which should activate the projects.create state
3.) an alert() should pop up now but it does not.
Why are those onExit and onEnter callbacks in the projects state definition not called?
The projects state onExit should be triggered when this state is left and we activate the projects.create state.
app.js
.config(function($stateProvider, $urlRouterProvider) {
$urlRouterProvider.otherwise('/projects');
$stateProvider
.state('projects', {
url: '/projects',
views: {
'menu#""': {
template: 'Start your projects!'
},
'content#': {
templateUrl: "projects.html",
controller: 'ProjectsController',
onEnter: function(){
alert('hello onEnter');
},
onExit: function(){
alert('hello onExit');
}
}
}
})
.state('projects.create', {
url: '/create',
views: {
'outer#': {
templateUrl: 'projects.create.html',
controller: 'ProjectWizardController'
}
}
})
.state('projects.selected', {
url: '/:projectId'
})
});
Okay, I got it now. The first problem is that, if you are using nested views then you can't use onEnter & onExit callbacks there (at view level). You can only use them at state level configuration.
For reference documentation: https://github.com/angular-ui/ui-router/wiki/Multiple-Named-Views#views-override-states-template-properties
If you define a views object, your state's templateUrl, template and
templateProvider will be ignored. So in the case that you need a
parent layout of these views, you can define an abstract state that
contains a template, and a child state under the layout state that
contains the 'views' object.
Now since, you can't use them at view level, you have to add it to your state config for create page like this:
.state('projects', {
url: '/projects',
views: {
'menu#""': {
template: 'Start your projects!'
},
'content#': {
templateUrl: "projects.html",
controller: 'ProjectsController'
}
}
})
.state('projects.create', {
url: '/create',
views: {
'outer#': {
templateUrl: 'projects.create.html',
controller: 'ProjectWizardController'
}
},
onEnter: function(){
alert('hello onEnter');
},
onExit: function(){
alert('hello onExit');
}
})
Hope this helps!
My angular app is routed as following:
$stateProvider
// setup an abstract state for the tabs directive
.state('tab', {
url: "/tab",
abstract: true,
templateUrl: "templates/tabs.html"
})
// Each tab has its own nav history stack:
.state('tab.dotnet', {
url: '/dotnet',
views: {
'tab-dotnet': {
templateUrl: 'templates/tab-dotnet.html',
controller: 'QuestionsCtrl'
}
}
})
.state('tab.sql', {
url: '/sql',
views: {
'tab-sql': {
templateUrl: 'templates/tab-sql.html',
controller: 'QuestionsCtrl'
}
}
})
The above two routes use the same controller but different html pages.
Since both the pages are same, I want to have a single html page in my application instead of two different tab-sql and tab-dotnet pages.
But I will need a differentiation variable to be injected to the controller when selecting the tabs.
Basically I need something like this:
.state('tab.dotnet', {
url: '/dotnet',
views: {
'tab-dotnet': {
templateUrl: 'templates/tab.html',
controller: 'QuestionsCtrl',
type: 'dotnet' // so that i get this type in my Controller
}
}
})
.state('tab.sql', {
url: '/sql',
views: {
'tab-sql': {
templateUrl: 'templates/tab.html',
controller: 'QuestionsCtrl',
type: 'sql'
}
}
})
How to achieve this?
You can pass data to controllers in a state using resolve.
.state('tab.dotnet', {
url: '/dotnet',
views: {
'tab-dotnet': {
templateUrl: 'templates/tab.html',
controller: 'QuestionsCtrl',
resolve: {
type: 'dotnet';
}
}
}
})
.state('tab.sql', {
url: '/sql',
views: {
'tab-sql': {
templateUrl: 'templates/tab.html',
controller: 'QuestionsCtrl',
resolve: {
type: 'sql';
}
}
}
})
https://github.com/angular-ui/ui-router/wiki#resolve
Angular UI Router Question
When $state.go("main.loadbalancer.readonly"); is activated in the resolve block, the main.loadbalancer.vips state controller VipListCtrl (the controller only) still loads after the resolve.
Since the state main.loadbalancer.readonly is activated, how can I keep make the controller VipListCtrl cancel and not load?
I tried using a promise and never having the promise resolve, but then the UI Router seems to stay sit at that resolve forever.
angular.module("main.loadbalancer", ["ui.bootstrap", "ui.router"]).config(function($stateProvider) {
return $stateProvider.state("main.loadbalancer", {
url: "device/:id",
views: {
"content#": {
templateUrl: "loadbalancer/loadbalancer.html",
controller: "LoadBalancerCtrl"
}
}
}).state("main.loadbalancer.vips", {
resolve: {
isDeviceReadOnly: function($state) {
if (!$state.current.data['deviceId']) {
$state.go("main.loadbalancer.readonly"); //THIS IS RAN...NEED CONTROLLER
//VipListCtrl TO NOT RUN AFTERWARDS
}
}
},
url: "/vips",
templateUrl: "loadbalancer/vip-table.html",
controller: "VipListCtrl"
}).state("main.loadbalancer.nodes", {
url: "/nodes",
templateUrl: "loadbalancer/node-table.html",
controller: "NodeListCtrl"
}).state("main.loadbalancer.admin", {
url: "/admin",
templateUrl: "loadbalancer/admin.html",
controller: "AdminCtrl"
}).state("main.loadbalancer.readonly", {
url: "/readonly",
templateUrl: "loadbalancer/readonly.html",
controller: "ReadonlyCtrl"
});
});
resolve: {
isDeviceReadOnly: function($state, $q, $timeout) {
if (!$state.current.data['deviceId']) {
$timeout(function() { $state.go("main.loadbalancer.readonly"); });
return $q.reject("rejection message"); // <-- Gotta reject your resolve
}
}
},
plunk which demonstrates $q.reject cancelling the transition: http://plnkr.co/edit/njJtyVbKD4rDY3OckAF6?p=preview