There is a possibility in app.run I record any information in this case last at $stateParams and write to the $rootScoope?
It would be simple because I pull the server a coming collection mongodb and I just editing it throughout the app.
I want to prevent in all state entries I have to pull the less data.
my app.run in test:
.run(function($rootScope, $state, $stateParams, CLIENTS) {
$rootScope.$state = $state;
$rootScope.$stateParams = $stateParams;
console.log($stateParams);
CLIENTS.getList({client: client})
.then(function(res){
console.log(res);
});
});
Follow this path:
$rootScope.$on('$stateChangeStart', function(event, toState, toParams, fromState, fromParams) {
// get Client data
if(!$rootScope.clientSet) {
CLIENTS.getList({client: toParams.client})
.then(function(res){
$rootScope.clientSet = res[0];
});
}
}
https://github.com/angular-ui/ui-router/wiki#state-change-events
You can listen for events so you can refresh data in rootScope.
$rootScope.$on('$stateChangeSuccess',
function(event, toState, toParams, fromState, fromParams){ ... })
Related
This question already has answers here:
Cannot get to $rootScope
(3 answers)
Closed 5 years ago.
I am using angular 1.5.x
Below is my routing logic but I am not able to get a handle of the rootScope.
Please can you advise what could be wrong?
UPDATE: I have updated the routing logic as per the answer post below.
I am still not able to hit the controller.
(function() {
'use strict';
angular
.module('icebergApp')
.config(setupRouting);
setupRouting.$inject = ['$stateProvider', '$urlRouterProvider', '$rootScope'];
function setupRouting($stateProvider, $urlRouterProvider, $rootScope) {
$urlRouterProvider.otherwise("/iceberg-ui");
$stateProvider
.state('iceberg.reconlist', {
url: "/iceberg-ui",
templateUrl: "app/iceberg/reconlist/reconlist.view.html",
controller: 'ReconListController as vm'
})
$rootScope.$on('$stateChangeStart',function(event, toState, toParams, fromState, fromParams){
console.log('$stateChangeStart to '+toState.name+'- fired when the transition begins. toState,toParams : \n',toState, toParams);
});
$rootScope.$on('$stateChangeError',function(event, toState, toParams, fromState, fromParams, error){
console.log('$stateChangeError - fired when an error occurs during transition.');
console.log(arguments);
});
$rootScope.$on('$stateChangeSuccess',function(event, toState, toParams, fromState, fromParams){
console.log('$stateChangeSuccess to '+toState.name+'- fired once the state transition is complete.');
});
$rootScope.$on('$viewContentLoading',function(event, viewConfig){
console.log('$viewContentLoading - view begins loading - dom not rendered',viewConfig);
});
$rootScope.$on('$stateNotFound',function(event, unfoundState, fromState, fromParams){
console.log('$stateNotFound '+unfoundState.to+' - fired when a state cannot be found by its name.');
console.log(unfoundState, fromState, fromParams);
});
}
}());
CONTROLLER
(function() {
'use strict';
var myApp = angular.module('iceberg.reconlist', ['ui.router']);
myApp.controller('ReconListController', ReconListController);
ReconListController.$inject = ['ReconListService'];
function ReconListController(ReconListService) {
var vm = this;
}
}());
MODULE
(function() {
'use strict';
angular.module('icebergApp', [
'ui.router',
'iceberg.reconlist'
]);
angular.module('iceberg.reconlist', [
]);
}());
$rootScope is not available in .config(). Only providers are available there. The thing you must do is splitting your code to put the $rootScope part into a .run method.
angular
.module('app')
.config(setupRouting);
setupRouting.$inject = ['$stateProvider', '$urlRouterProvider'];
function setupRouting($stateProvider, $urlRouterProvider) {
$urlRouterProvider.otherwise("/iceberg-ui");
$stateProvider
.state('iceberg.reconlist', {
url: "/iceberg-ui",
templateUrl: "app/iceberg/reconlist/reconlist.view.html",
controller: 'ReconListController as vm'
})
}
}());
angular.module('app').run(setupRoutingEventListeners);
setupRouting.$inject = ['$rootScope'];
function setupRoutingEventListeners($rootScope) {
$rootScope.$on('$stateChangeStart',function(event, toState, toParams, fromState, fromParams){
console.log('$stateChangeStart to '+toState.name+'- fired when the transition begins. toState,toParams : \n',toState, toParams);
});
$rootScope.$on('$stateChangeError',function(event, toState, toParams, fromState, fromParams, error){
console.log('$stateChangeError - fired when an error occurs during transition.');
console.log(arguments);
});
$rootScope.$on('$stateChangeSuccess',function(event, toState, toParams, fromState, fromParams){
console.log('$stateChangeSuccess to '+toState.name+'- fired once the state transition is complete.');
});
$rootScope.$on('$viewContentLoading',function(event, viewConfig){
console.log('$viewContentLoading - view begins loading - dom not rendered',viewConfig);
});
$rootScope.$on('$stateNotFound',function(event, unfoundState, fromState, fromParams){
console.log('$stateNotFound '+unfoundState.to+' - fired when a state cannot be found by its name.');
console.log(unfoundState, fromState, fromParams);
});
}
Using ui-router how can I accomplish the following URL change? For example, when a user goes to "/page?hello=True" I want the url to change to "/page?status=hello".
$stateProvider.state('page', {
url: '/page/:id?status',
controller: 'MyCtrl as myCtrl',
templateProvider: function($templateCache) {
return $templateCache.get('templates/route.html');
}
});
You can use the stateChangeStart event in the app.run method to listen for state changes into that route, and change anything you want on the params. Something like this, might need to play with toState and toParams to get it right for you...
.run(['$state', '$rootScope', function($state, $rootScope) {
$rootScope.$on('$stateChangeStart', function(e, toState, toParams, fromState, fromParams) {
if (toState.name === 'invitation') {
//change toParams to whatever you want
}
});
});
I want to impliment a loader image for each state change, and that will be apply to the whole page whenever the state changes. I tried to at least see if they exist, but somehow, I receive not definfed for $stateChangeStart for both when state change start and completed. I wonder what is the best way to do it? Because I want to put that $stateChangeStart as a var for ng-hide directive, so that when every time a state change starts, the loader will show, and (I assume) when $stateChangeSuccess is becoming defined, then its change of state is completed?
Please advice if anything of what I intent to do is wrong and better way to do it. Because I want to build the loader to be directive by itself and its reusable.
Below is the code I try to test these 3 objects.
app.run([
'$rootScope','$location','$state','$stateParams',/*'$templateCache',*/
function (
$rootScope, $location, $state, $stateParams/*, $templateCache*/ ) {
console.log("app run")
$rootScope.$on('$stateChangeStart', function(event, toState, toParams, fromState, fromParams) {
console.log("state start-------------------------")
console.log($stateChangeStart,"$stateChangeStart ")
console.log($stateChangeSuccess,"$stateChangeSuccess ")
console.log($state);
});
$rootScope.$on('$stateChangeSuccess', function(event, toState, toParams, fromState, fromParams) {
console.log("state $stateChangeSuccess-------------------------")
console.log($stateChangeStart,"$stateChangeStart ")
console.log($stateChangeSuccess,"$stateChangeSuccess ")
console.log($state);
});
}]);
Try something like this:
app.run([
'$rootScope','$location','$state','$stateParams',/*'$templateCache',*/
function (
$rootScope, $location, $state, $stateParams/*, $templateCache*/ ) {
console.log("app run")
$rootScope.$on('$stateChangeStart', function(event, toState, toParams, fromState, fromParams) {
$rootScope.showLoading = true;
});
$rootScope.$on('$stateChangeSuccess', function(event, toState, toParams, fromState, fromParams) {
$rootScope.showLoading = false;
});
}]);
and then bind the ng-show to the showLoading property.
I've tested this in chrome, and added a break in the $scope.init in Ctrl2
but then when I go to route2 from route1, the chrome debugger stays at $scope.init of Ctrl2, but I see that ctrl1.html is still there.
.state("main.route1", {
url: "/route1",
controller: 'Ctrl1',
templateUrl: 'views/ctrl1.html'
})
.state("main.route2", {
url: "/route2",
controller: 'Ctrl2',
templateUrl: 'views/ctrl2.html'
})
So, how does the ui-router work? Isn't it supposed to go to ctrl2.html, and then execute Ctrl2? Why did it enter Ctrl2 but the ctrl1.html is still displayed?
I'm not sure if it is the same in ng-view, I haven't tested that.
Check js console to make sure you don't have errors in your angular controller
Add following run block to your module to catch state change error
.run(function ($rootScope, $state) {
$rootScope.$on('$stateChangeStart',
function(event, toState, toParams, fromState, fromParams){
console.log(fromState.name + ' to ' + toState.name);
});
$rootScope.$on('$stateChangeError',
function (event, toState, toParams, fromState, fromParams, error) {
alert(error);
event.preventDefault();
});
});
I am trying to test a module that has the following code:
angular.module('angularEnterpriseAuthorization').run(['$rootScope', '$state', 'AppConfig',
function($rootScope, $state, AppConfig) {
// On every time the user changes state check to see if the user has permissions to go to the new state
$rootScope.$on("$stateChangeStart", function(event, toState, toParams, fromState, fromParams) {
// If the state is not one of the public states as defined in the modules config
if (AppConfig.publicStates.indexOf(toState.name) < 0) {
event.preventDefault();
$state.go(toState, toParams, {notify: false}).then(function() {
$rootScope.$broadcast('$stateChangeSuccess', toState, toParams, fromState, fromParams);
});
}
});
]);
My test looks like this:
beforeEach(module('angularEnterpriseAuthorization', 'coreConfiguration'));
beforeEach(inject(function(_$rootScope_, _$httpBackend_, _AppConfig_) {
$scope = _$rootScope_.$new();
$httpBackend = _$httpBackend_;
AppConfig = _AppConfig_
spyOn($scope, '$broadcast').andCallThrough();
}));
it('should allow navigation to public states', function() {
$scope.$broadcast('$stateChangeStart', [{},{name:AppConfig.publicStates[0]}]);
expect($scope.$broadcast).toHaveBeenCalledWith('$stateChangeStart', [{}, {name: AppConfig.publicStates[0]}]);
$scope.$broadcast.reset();
expect($scope.$broadcast).toHaveBeenCalledWith('$stateChangeSuccess');
});
The problem I am having is that the second expect is returning false. I think the issues is that the module is not being initialized with the same $rootScope.
Any help would be appreciated!
Thanks
In your run block, you subscribe a $stateChangeStart on $rootScope and also broadcast a $stateChangeSuccess event from $rootScope.
In your test, you have to do the same, using the $rootscope. May be change this line:
$scope = _$rootScope_.$new();
to just this:
$scope = _$rootScope_;
And also you have to remove the $scope.$broadcast.reset(), that will clear all the remembered calls.
To test the second call of the same method, you could do it like this:
it('should allow navigation to public states', function() {
$scope.$broadcast('$stateChangeStart', [{},{name:AppConfig.publicStates[0]}]);
expect($scope.$broadcast).toHaveBeenCalledWith('$stateChangeStart', [{}, {name: AppConfig.publicStates[0]}]);
$scope.$apply();
expect($scope.$broadcast.calls[1].args[0]).toEqual('$stateChangeSuccess');
});
Hope this helps.