Angular router, calling controller many times - angularjs

This code is functional, but it could work better. My question:
How i can insert navDetalle or other views in many states without call controller again.
(function() {
'use strict';
angular
.module('miApp')
.config(routerConfig);
var nav = {
templateUrl: 'app/nav/nav.html',
controller: 'NavController',
controllerAs: 'nav'
};
var navInter = {
templateUrl: 'app/nav/navInter.html',
controller: 'NavController',
controllerAs: 'nav'
};
var navDetalle = {
templateUrl: 'app/navDetalle/navDetalle.html',
controller: 'NavDetalleController',
controllerAs: 'navDetalle'
};
/** #ngInject */
function routerConfig($stateProvider, $urlRouterProvider) {
$stateProvider
.state('home', {
url: '/',
views: {
// the main template will be placed here (relatively named)
'nav': nav,
'carousel': {
templateUrl: 'app/carousel/carousel.html',
controller: 'CarouselController',
controllerAs: 'carousel'
},
'footer':{
templateUrl: 'app/footer/footer.html'
}
}
})
.state('detalle', {
url: '/detalle/:idDetalle',
views:{
'nav': navInter,
'detalle': {
templateUrl: 'app/detalle/detalle.html',
controller: 'DetalleController',
controllerAs: 'detalle'
},
'navdetalle': navDetalle,
'footer':{
templateUrl: 'app/footer/footer.html'
}
}
})
.state('resumen', {
url: '/resumen',
views:{
'nav': navInter,
'navdetalle': navDetalle,
'footer':{
templateUrl: 'app/footer/footer.html'
}
}
})
.state('success', {
url: '/success',
views:{
'nav': navInter,
'success': {
templateUrl: 'app/success/success.html',
controller: 'SuccessController',
controllerAs: 'success'
},
'navdetalle': navDetalle,
'footer':{
templateUrl: 'app/footer/footer.html'
}
}
})
.state('cancel', {
url: '/cancel',
views:{
'nav': navInter,
'navdetalle': navDetalle,
'cancel': {
templateUrl: 'app/cancel/cancel.html',
controller: 'CancelController',
controllerAs: 'cancel'
},
'footer':{
templateUrl: 'app/footer/footer.html'
}
}
});
$urlRouterProvider.otherwise('/');
}
})();
and if you have suggestion on another improvements please comment

I'm not sure if this is possible to prevent executing controller every time. But, I do have another solution. That might work. The Ionic Framework do this internally by preserving controller state.
Create a global controller and add it to <body> or <html> tag so that its scope can be available every time.
app.controller('GlobalController', function($scope) {
var navDetalleCtrlInstantiated = false;
$scope.$on('$stateChangeStart', function(e, toState, toParams) {
if (!navDetalleCtrlInstantiated && toState.views && toState.views.navdetalle) {
// Do the common logic on controller instantiation
// Mark so that we don't have to do the same logic again
navDetalleCtrlInstantiated = true;
}
});
});
In your view:
<body ng-controller="GlobalController">
</body>
And remove logic from your NavDetalleController.

I don't know if what you want is possible. Because ui-router always creates a new instance to the view's controller, and that makes sense cause every single view has your own scope.

Related

URL doesn't change after changing ui-router state

I have followed the documentation and online tutorials to create my ui-router states however when I enter my state, the URL doesn't update. Same happens when I do $state.go('main.about-me'); for example. The state changes but not the URL. Is there something wrong with my config that I can't see clearly?
Will be thankful for any tips. Thanks.
'use strict';
angular
.module('myApp.config', ['ui.router'])
.config(routes)
.run(['$rootScope', '$location', '$stateParams', '$state', appRun]);
function routes($stateProvider, $urlRouterProvider) {
$stateProvider
.state('main', {
url: 'main',
abstract: true,
views: {
'': {
templateUrl: 'main/main.tpl.html',
controller: 'mainController as vm'
},
'nav-bar#main': {
templateUrl: 'main/nav-bar/nav-bar.tpl.html',
controller: 'navigationBarController as vm'
}
}
})
.state('main.home', {
url: '/home',
views: {
'content': {
templateUrl: 'home/home.tpl.html',
controller: 'homeController as vm'
}
}
})
.state('main.about-me', {
url: '/about-me',
views: {
'content': {
templateUrl: 'about-me/about-me.tpl.html',
controller: 'aboutMeController as vm'
}
}
})
.state('main.contact-me', {
url: '/contact-me',
views: {
'content': {
templateUrl: 'contact-me/contact-me.tpl.html',
controller: 'contactMeController as vm'
}
}
});
$urlRouterProvider.otherwise('');
}
function appRun($rootScope, $location, $stateParams, $state) {
console.log('run the app');
console.log('$location', $location);
console.log('$rootScope', $rootScope);
console.log('$stateParams', $stateParams);
$state.go('main.home');
}
Inside my body I have this
<div ng-cloak>
<div ui-view class="content-container"></div>
</div>

Angular JS - go to otherwise state without changing the URL

Hi I have been using stateprovider for a long time and this time I want to implement a functionality in which without changing the url, I want to go not found state but not on url.
This is my code.
(function () {
angular.module('mean').config(aosOfferConfig);
function aosOfferConfig($stateProvider, $urlRouterProvider) {
$urlRouterProvider.otherwise("/link/404_not_found");
$stateProvider
.state('offers', {
url: '/offers/packages',
templateUrl: 'views/aosOffers/aosoffers.html',
controller: 'offersController',
controllerAs: 'packages'
})
.state('offersignup', {
url: '/offers/signup',
templateUrl: 'views/aosOffers/offerssignup.html',
controller: 'offersSignupController',
controllerAs: 'offerssignup'
})
.state('thankyou', {
url: '/offers/thankyou',
templateUrl: 'views/aosOffers/offersthankyou.html'
});
}
})();
Now in my otherwise part I am taking the user to /link/404_not_found. I don't want this. Instead, I want that it will remain in current url but the state should be changed. Just like github does.
How can I do this ?
do it this way
(function () {
angular.module('mean').config(aosOfferConfig);
function aosOfferConfig($stateProvider, $urlRouterProvider) {
$urlRouterProvider.otherwise(function ($injector, $location) {
var state = $injector.get('$state');
state.go('404_not_found', {}, { location: false });
return $location.path();
});
$stateProvider
.state('offers', {
url: '/offers/packages',
templateUrl: 'views/aosOffers/aosoffers.html',
controller: 'offersController',
controllerAs: 'packages'
})
.state('offersignup', {
url: '/offers/signup',
templateUrl: 'views/aosOffers/offerssignup.html',
controller: 'offersSignupController',
controllerAs: 'offerssignup'
})
.state('thankyou', {
url: '/offers/thankyou',
templateUrl: 'views/aosOffers/offersthankyou.html'
})
.state('404_not_found', {
template: '<h1>Notfound</h1>'
});
}
})();

UI Router multiple views share same controller

I am attempting to get multiple views to use the same controller. I've tried a couple of things so far, none seem to work. By "doesnt work" I mean the controller MapController isnt instantiated and the views cannot see the controller
1
$stateProvider.state(PageStateNames.COMPONENTS_LIVEMAP, {
url: "/components/vehicles/:vehicle/:panel",
views: {
"": {
controller: "MapController as vm"
},
"content#app": {
templateUrl: "....html"
},
"sidenav#app": {
templateUrl: "....html"
}
}
});
2
$stateProvider.state(PageStateNames.COMPONENTS_LIVEMAP, {
url: "/components/vehicles/:vehicle/:panel",
controller: "MapController as vm"
views: {
"content#app": {
templateUrl: "....html"
},
"sidenav#app": {
templateUrl: "....html"
}
}
});
Having looked at existing questions this should work. Have I missed something?
$stateProvider.state(PageStateNames.COMPONENTS_LIVEMAP, {
url: "/components/vehicles/:vehicle/:panel",
views: {
"": {
templateUrl: "......html",
controller: "MapController as vm"
},
"content#app": {
templateUrl: "....html",
controller: "MapController as vm"
},
"sidenav#app": {
templateUrl: "....html",
controller: "MapController as vm"
}
}
});
To use the same controller in a state, you can use child-parent nested state. For example :
$stateProvider.state('home', {
templateUrl: '....html',
controller: 'ParentController'
})
.state('home.livemap', { // << this state will use parent controller instance, this is the dot notation to make livemap a child state of home (more info here https://github.com/angular-ui/ui-router/wiki/Nested-States-and-Nested-Views
templateUrl: '....html'
});

Angular app initialization with asynchronous data

I need to initialize my Angular with some User data that the whole app depends on. Therefore I need the initialization to be resolved before the router kicks in and controllers are initialized.
Currently, I wrote the initialization code in a run() block of the angular module. The initialization involves an asynchronous http request to get user data and the rest of the application relies upon the user data.
How can I ensure that the http request is resolved before the router kicks-in initializing the controllers?
I am using the ui-router.
The initialization consists in the following:
1) get cookie 'userId'
2) get User from server (asynchronous http request, the whole app depends upon the User)
3) set authService.currentUser
this is a sample of the code
.run(['$cookies', 'userApiService', 'authService',
function($cookies, userApiService, authService){
var userId = $cookies.get('userId');
userId = parseCookieValue(userId);
userApiService.getOne(userId).then(function(user){
authService.currentUser = user;
});
}])
.config(['$stateProvider', '$urlRouterProvider', '$locationProvider',
function($stateProvider, $urlRouterProvider, $locationProvider) {
$locationProvider.html5Mode(true);
$urlRouterProvider.when('/', '/main');
$stateProvider
.state('login', {
url: '/login',
views: {
'header': {
templateUrl: 'views/header.html',
controller: 'HeaderCtrl'
},
'content': {
templateUrl: 'views/login.html',
controller: 'LoginCtrl'
},
'footer': {
templateUrl: 'views/footer.html',
}
}
})
.state('main', {
url: '/main',
views: {
'content#': {
template: '',
controller: function($state, authService) {
if(!authService.isAuthenticated()) {
$state.go('login');
}
if(authService.isStudent()) {
$state.go('student');
}
if(authService.isAdmin()) {
$state.go('admin');
}
}
}
}
})
.state('student', {
url: '/student',
views: {
'header#': {
templateUrl: 'views/header.html',
controller: 'HeaderCtrl'
},
'content#': {
templateUrl: 'views/student.html',
controller: 'StudentCtrl'
},
'footer#': {
templateUrl: 'views/footer.html',
}
}
})
.state('admin', {
url: '/admin',
views: {
'header#': {
templateUrl: 'views/header.html',
controller: 'HeaderCtrl'
},
'content#': {
templateUrl: 'views/admin.html',
controller: 'AdminCtrl'
},
'footer#': {
templateUrl: 'views/footer.html',
}
}
})
}])
Expanding on someone's comment, you can create a root state that is a parent to all of your other app's states (children to the root). The root state resolves all the user data and then you can inject the user data to any controller or store it in a service.
$stateProvider
.state('root', {
url: '',
abstract: true,
template: '', // some template with header, content, footer ui-views
resolve: {
// fetch user data
}
})
.state('root.login', {
url: '/login',
views: {
'header': {
templateUrl: 'views/header.html',
controller: 'HeaderCtrl'
},
'content': {
templateUrl: 'views/login.html',
controller: 'LoginCtrl'
},
'footer': {
templateUrl: 'views/footer.html',
}
}
})
.state('root.main', {
url: '/main',
views: {
'content#': {
template: '',
controller: function($state, authService) {
if(!authService.isAuthenticated()) {
$state.go('login');
}
if(authService.isStudent()) {
$state.go('student');
}
if(authService.isAdmin()) {
$state.go('admin');
}
}
}
}
})
... // your other states
The key is that all of your app states must be a child of your root state i.e. root.<name> in your state declaration. This will ensure that no other controller starts until your user data is available. For more information on resolve and how to use it read here. Also, parent and child states.

Ionic: Method call on page enter

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);
});

Resources