Why controller doesn't respond on route update? - angularjs

I would like preserve instance of controller without reloading. I set reloadOnSearch to false and I manage route change in my controller. Here is the code.
This is example of my link next. I have defined following module.
angular.module('app.products', ['ngRoute', 'ngResource'])
.config(['$routeProvider', function ($routeProvider) {
$routeProvider
.when('/:section/:view/:number', {
templateUrl: 'tpl/table.html',
controller: 'ProductsCtrl',
controllerAs: 'productsCtrl',
reloadOnSearch: false
});
}])
.controller('ProductsCtrl', ['$scope', '$routeParams', '$location', ProductsCtrl]);
Controller
function ProductsCtrl($scope, $routeParams, $location) {
$scope.$on('$routeUpdate', function () {
console.log("routeUpdate");
});
}
But the controller doesn't respond on changed route and text is not written to console output.

In the angular jargon, "search" refers only to the query string parameters part of the URL. For instance: ?key=value&page=42
And the "path" refers to the URL without that query string. Here /products/page/2 is a path.
When setting reloadOnSearch: false you're telling angular not to reload the view and the associated controller when only the query string parameters changes.
So if the path changes, for instance you navigate from /products/page/2 to /products/page/3, then the view will still be reloaded. No $routeUpdate will be fired because there is no need for that. You'll get the new parameters from $routeParams when your controller initialization function is called again.
However if the path doesn't change, but only the query string parameters do change. For instance when you navigate from /products?page=2 to /products?page=3. Then the view will not be reloaded and a $routeUpdate will be broadcast.
So the solution here would be to define page as a query string parameter instead of a path parameter:
angular.module('app.products', ['ngRoute', 'ngResource'])
.config(['$routeProvider', function ($routeProvider) {
$routeProvider
.when('/:section', {
templateUrl: 'tpl/table.html',
controller: 'ProductsCtrl',
controllerAs: 'productsCtrl',
reloadOnSearch: false
});
}])
.controller('ProductsCtrl', ['$scope', '$routeParams', '$location', ProductsCtrl]);
Controller:
function ProductsCtrl($scope, $routeParams, $location) {
$scope.setupView = function setupView(section, page) {
// Setup you view here
// ...
};
$scope.$on('$routeUpdate', function () {
// This is called when only the query parameters change. E.g.: ?page=2
$scope.setupView($routeParams.section, $routeParams.page)
});
// This one is called when the the path changes and the view is reloaded.
$scope.setupView($routeParams.section, $routeParams.page)
}

Instead of $routeUpdate, try to use $routeChangeSuccess.
$scope.$on('$routeChangeSuccess', function (scope, next, current) {
console.log("Your text goes here.");
});
You can use next and current to check your previous and next route.
Hope it helps.

Related

angular js $routeParam not working properly

i have used $routeProvide to redirect page from one page to another and in that i am passing some dynamic parameters in url.
it working at one place and not working with another place.
my code : in app.js
app.config(['$routeProvider', '$locationProvider',
function ($routeProvider, $locationProvider) {
$routeProvider
.when('/', {
templateUrl: "client/home/home.html",
controller: "IndexCtrl",
access: {
isloggedIn: false
}
})
.when('/test/:searchTerm', {
templateUrl: "client/test.html",
controller: "testController",
access: {
isloggedIn: false
}
})
.when('/test2/:id', {
templateUrl: "client/index.html",
controller: "indexController",
access: {
isloggedIn: true
}
})
.otherwise({
redirectTo: "/"
});
}]);
in controllers file : for testController when i hit url
server.com/#/test/45 got in console 45 and for indexController when i hit url server.com/#/test2/45 then i get :id in console.
app.controller("testController", ['$location', '$rootScope', '$routeParams', function ($location, $rootScope, $routeParams) {
console.log($routeParams.searchTerm); //get result 45
}]);
app.controller("indexController", ['$location', '$rootScope', '$routeParams', function ($location, $rootScope, $routeParams) {
console.log($routeParams.id); //get result :id
}]);
why this happen can anyone help? and what should be the issue?
Try ($routeParams.id); instead of ($routParams.id);, I don't know why your $routParams.searchTerm is working, but it shouldn't (should be route, not rout).
Seems like your code is correct, other than the spelling mistake. The only difference i can find is that the IsLoggedIn property value. Did you try by making it false?. And what is the significance of IsLoggedIn property.
check your hyperlink to the route /test2/:id.
It may be like ng-href="#/test2/:id"
change it to ng-href="#/test2/{{id}}"
don't forgot to define variable id
recreated the issue in fiddle

How to change templateURL for angular UI router based on customer settings

I have multiple clients on my angular app and I want to create different themes inside angular (only the visual part will change, controllers remain the same.
I have a "security" module which manages the authentication, currentLoggedIn user and so on.
var security = angular.module('security', ['ui.router'])
// .factory('authService', authService);
.service('authService', ['$http', '$q', '$window', 'CONSTANTS', '$location', 'currentUser', '$state', '$rootScope', authService])
.factory('authCheck', ['$rootScope', '$state', 'authService', securityAuthorization])
and authService is basically having these methods/values
service.login = _login;
service.logout = _logout;
service.reset = _reset;
service.isAuthenticated = _isAuthenticated;
service.requestCurrentUser = _requestCurrentUser;
service.returnCurrentUser = _returnCurrentUser;
service.hasRoleAccess = _hasRoleAccess;
How can I get access to currentUser inside templateURL function to modify the URL based on data for currentUser?
AuthService and AuthCheck are empty when accessed in templateURL function.
$stateProvider
.state('home', {
url: '/home',
templateUrl: function(authService, authCheck) {
console.log (authService, authCheck);
return 'components/home/home.html'
},
data: {
roles: ['Admin']
},
resolve: {
"authorize": ['authCheck', function(authCheck) {
return authCheck.authorize();
}],
"loadedData": ['metricsFactory', 'campaignFactory', '$q', '$rootScope', 'selectedDates', loadHomeController]
},
controller: 'HomeController',
controllerAs: 'home'
});
In case, we want to do some "magic" before returning the template... we should use templateProvider. Check this Q & A:
Trying to Dynamically set a templateUrl in controller based on constant
Because template:... could be either string or function like this (check the doc:)
$stateProvider
template
html template as a string or a function that returns an html template
as a string which should be used by the uiView directives. This
property takes precedence over templateUrl.
If template is a function, it will be called with the following
parameters:
{array.} - state parameters extracted from the current
$location.path() by applying the current state
template: function(params) {
return "<h1>generated template</h1>"; }
While with templateProvider we can get anything injected e.g. the great improvement in angular $templateRequest. Check this answer and its plunker
templateProvider: function(CONFIG, $templateRequest) {
console.log('in templateUrl ' + CONFIG.codeCampType);
var templateName = 'index5templateB.html';
if (CONFIG.codeCampType === "svcc") {
templateName = 'index5templateA.html';
}
return $templateRequest(templateName);
},
From the documentation:
templateUrl (optional)
path or function that returns a path to an html template that should be used by uiView.
If templateUrl is a function, it will be called with the following parameters:
{array.<object>} - state parameters extracted from the current $location.path() by applying the current state
So, clearly, you can't inject services to the templateUrl function.
But right after, the documentation also says:
templateProvider (optional)
function
Provider function that returns HTML content string.
templateProvider:
function(MyTemplateService, params) {
return MyTemplateService.getTemplate(params.pageId);
}
Which allows doing what you want.

Angular ui.router. Deep nested routes

Here is an example to check http://embed.plnkr.co/uVMlkk/preview
When we navigate to 'page2' route there is a 'hey, I'm a subroute' note.
But once we navigate anywhere else that note will disappear forever.
The goal is to make some nested states to be shown right away (as a default ones).
I assume there should be some cases using $state.go(), but can't figure it out so far. Any help is highly appreciated.
State definition snippet:
.state('root.page2.tab', {
url: '/:tabId',
templateUrl: 'tpl.page2.tab.html',
controller: 'Page2TabController'
})
.state('root.page2.tab.subroute', {
url: '',
templateUrl: 'tpl.page2.tab.subroute.html'
})
the content of the 'tpl.page2.tab.subroute.html':
hey, I'm a subroute
related controller:
.controller('Page2TabController', ['$scope', '$state', function($scope, $state) {
$scope.tabId = $state.params.tabId;
$state.go('root.page2.tab.subroute');
}])
There is a fixed version.
I removed the url from the 'root.page2.tab.subroute'
.state('root.page2.tab.subroute', {
//url: '',
templateUrl: 'tpl.page2.tab.subroute.html'
})
And because the parent has defined paramater tabId:
.state('root.page2.tab', {
url: '/:tabId',
templateUrl: 'tpl.page2.tab.html',
controller: 'Page2TabController'
})
We have to pass that param inside of the redicrection:
.controller('Page2TabController', ['$scope', '$state', function($scope, $state) {
$scope.tabId = $state.params.tabId;
// instead of this
// $state.go('root.page2.tab.subroute');
// we need this
$state.go('root.page2.tab.subroute', $state.params);
}])
Check the working, fixed version here
ANOTHER approach - using redirectTo - there is a working plunker
One way, inspired by this:
Redirect a state to default substate with UI-Router in AngularJS
could be to add a very smart but small redirect code snippet:
.run(['$rootScope', '$state', function($rootScope, $state) {
$rootScope.$on('$stateChangeStart', function(evt, to, params) {
if (to.redirectTo) {
evt.preventDefault();
$state.go(to.redirectTo, params)
}
});
}])
And adjust our state like this:
.state('root.page2.tab', {
url: '/:tabId',
templateUrl: 'tpl.page2.tab.html',
controller: 'Page2TabController',
redirectTo: 'root.page2.tab.subroute',
})
Check it here
There is a trick how to handle scenarios:
Parent should trigger some action in case that
it is accessed, or
its reached again, when navigating back from child in a parent state
In that case, we can use the "target (ui-view) for a child" as a place where sits the special view, with special controller. This will be
injected into that position once parent is created and
re-injected into that position again, once child is left. In that case, it will be re-init.
Enough explanation. There is a working plunker. There is adjusted state:
.state('root.page2', {
url: '/page2',
views: {
'content#root': {
templateUrl: './tpl.page2.html',
controller: 'Page2Controller'
},
'#root.page2': {
template: '<div></div>',
controller: 'RedirectorController'
}
}
})
So, now we can do some magic inside of our 'RedirectorController'
.controller('RedirectorController', ['$scope', '$state',
function($scope, $state) {
$state.go('root.page2.tab', { tabId: $scope.activeTabId });
}])
Check it in action here
Read more about what that new view/controller get from the other (Scope Inheritance by View Hierarchy Only) one here
Nested states or views for layout with leftbar in ui-router?
How do I share $scope data between states in angularjs ui-router?

angular-ui ui-router controller not getting instantiated with $state.go

I am trying to build an app which on login changes the state to a view with $state.go but when calling $state.go the controller is not instantiated as defined for that state.
Here is my state change logic (removing other code for brevity):
config(function($stateProvider, $urlRouterProvider) {
$stateProvider
.state('login', {
url: "/",
templateUrl: "templates/login.html",
controller: 'LoginController'
})
// 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.dash', {
url: '/dash',
views: {
'tab-dash': {
templateUrl: 'templates/tab-dash.html',
controller: 'DashboardController'
}
},
resolve: {
authenticated: ['RestService', function(restService) {
return restService.authenticationStatus();
}],
sessionService: ['SessionService', function(sessionService) {
return sessionService;
}]
}
})
My LoginController is something like:
// Perform the login action when the user submits the login form
$scope.doLogin = function(loginForm) {
if (loginForm.$invalid) {
$log.debug('Invalid Form...', $scope.loginData);
return;
}
$log.debug('Doing login', $scope.loginData);
RestService.login($scope.loginData)
.then(function(data) {
$log.debug("Inside loginController...");
$log.debug(data);
$scope.$state.go("tab.dash");
}, function(data) {
$log.debug(data);
$scope.formErrors = data.errors;
});
};
And my DashboardController is something like:
angular.module('starter.controller.dashboard', [])
.controller('DashboardController', ['$scope', '$log', '$http',
function($scope, $log, $http) {
$log.debug("reaching here..................");
$log.debug($scope.authenticated);
}]);
Now when the login succeeds, the state is transitioned to /tab/dash but the controller is not instantiated i.e. the debug logs in DashboardController are not printed. If I directly navigate to /tab/dash then the controller does get instantiated and I do see the logs getting printed.
Moreover the value of "authenticated" passed via resolve in state definition is not available via scope in templates.
Well turns out that the controller is getting instantiated but it gets instantiated only once. If I don't refresh the page while testing and just change the path then since the controller is already instantiated, it is not instantiated again. Only if I refresh the page (LoginPage) and then navigate to Dashboard page (via $state.go on logging in) the controller gets instantiated again.
And the issue with resolve data not available in the controller is because my assumption was that it is auto injected in $scope but actually it is not so. The resolve params get injected explicitly via the passed params to constructor function of controller and then one needs to assign the values manually in the scope. Something like:
.controller('DashboardController', ['$rootScope', '$scope', '$log', '$ionicLoading', '$http', 'authenticated',
function($rootScope, $scope, $log, $ionicLoading, $http, authenticated) {
$scope.authenticated = authenticated;
}]);

How to call a function in AngularJs when route matches?

$routeProvider.when('/ticket', {
controller: TicketController,
templateUrl: Routing.generate('ticket_list')
});
displays a simple list where each entry is selectable. However on select no extra view is loaded. Every thing is in ticket_lost template. The template has some hidden fields that are revealed when entry is clicked.
I can define which entry is selected internally by setting
selectedTicket = 1;
So when there is a route like
/ticket/1
I want to call a function that sets selectedTicket to 1. Is that possible? How to do that? What do I have to change in routing?
Take a look at $routeParams service.
It allows to set up route with parameters which will be parsed by service:
// Given:
// URL: http://server.com/index.html#/ticket/1
// Route: /ticket/:ticketId
//
// Then
$routeParams ==> {ticketId:1}
In your controller:
angular.module('myApp')
.config(['$routeProvider', function($routeProvider) {
$routeProvider.when('/ticket', {controller: 'TicketController'});
$routeProvider.when('/ticket/:ticketId', {controller: 'TicketController'});
$routeProvider.otherwise({redirectTo: '/ticket'});
}])
.controller('TicketController', function ($scope, $routeParams) {
var init = function () {
if ($routeParams.ticketId) {
$scope.ticketSelected($routeParams.ticketId);
}
};
// fire on controller loaded
init();
});

Resources