Related
Currently I'm doing something like this
link.on('click', function () {
if (link.attr('href') !== $route.current.originalPath)
return;
$route.reload();
});
I'm not aware of side effects but I guess there can be some.
Is there more straightforward way to handle this in ngRoute, e.g. through $location?
What is the way to do the same thing in UI Router when the app will be updated to use it?
With UI-Router we have a set of options, and one of them is {reload: true}
go(to, params, options)
location - {boolean=true|string=} - If true will update the url in the location bar, if false will not. If string, must be "replace",
which will update url and also replace last history record.
inherit - {boolean=true}, If true will inherit url parameters from current url.
relative - {object=$state.$current}, When transitioning with relative path (e.g '^'), defines which state to be relative from.
notify - {boolean=true}, If true will broadcast $stateChangeStart and $stateChangeSuccess events.
reload (v0.2.5) - {boolean=false}, If true will force transition even if the state or params have not changed, aka a reload of the same
state. It differs from reloadOnSearch because you'd use this when you
want to force a reload when everything is the same, including search
params.
So we can force state reload with:
$state.go("stateName", stateParams, {reload: true});
Your code could get tranformed to below change $route.reload() to $state.reload()
Code
link.on('click', function () {
if (link.attr('href') !== $route.current.originalPath)
return;
//though this will not reload the controller content
$state.reload(); //will force to reload state..
$scope.$apply(); //needed here to tell angular js to run digest cycle.
});
From git-hub issue it seems like $state.reload() reload the state but controller doesn't get re-instantiate. For that you need to use below code instead of $state.reload()
$state.transitionTo('tab.locations', $state.$current.params, {
reload: true, inherit: true, notify: true
});
I found this to be the shortest way with UI-router :
$state.go($state.current, {}, {reload: true});
or you can do this :
$state.transitionTo($state.current, $stateParams, {
reload: true,
inherit: false,
notify: true
});
If you are using Ui.Router:
link.on('click', function (event) {
if (link.attr('href') !== $route.current.originalPath)
return;
event.preventDefault();
return $scope.$apply(function() {
return $state.reload(); // $state.go($state.current.name, $stateParams, { inherit: false, reload: true, notify: true });
});
});
If you are using the simply ngRoute, you must to remove your the currentTemplate from templateCache and the return $route.reload() in a $timout!
I've been looking at these pages (1, 2, 3). I basically want to change my $state, but I don't want the page to reload.
I am currently in the page /schedules/2/4/2014, and I want to go into edit mode when I click a button and have the URL become /schedules/2/4/2014/edit.
My edit state is simply $scope.isEdit = true, so there is no point of reloading the whole page. However, I do want the $state and/or url to change so that if the user refreshses the page, it starts in the edit mode.
What can I do?
For this problem, you can just create a child state that has neither templateUrl nor controller, and advance between states normally:
// UPDATED
$stateProvider
.state('schedules', {
url: "/schedules/:day/:month/:year",
templateUrl: 'schedules.html',
abstract: true, // make this abstract
controller: function($scope, $state, $stateParams) {
$scope.schedDate = moment($stateParams.year + '-' +
$stateParams.month + '-' +
$stateParams.day);
$scope.isEdit = false;
$scope.gotoEdit = function() {
$scope.isEdit = true;
$state.go('schedules.edit');
};
$scope.gotoView = function() {
$scope.isEdit = false;
$state.go('schedules.view');
};
},
resolve: {...}
})
.state('schedules.view', { // added view mode
url: "/view"
})
.state('schedules.edit', { // both children share controller above
url: "/edit"
});
An important concept here is that, in ui-router, when the application is in a particular state—when a state is "active"—all of its ancestor states are implicitly active as well.
So, in this case,
when your application advances from view mode to edit mode, its parent state schedules (along with its templateUrl, controller and even resolve) will still be retained.
since ancestor states are implicitly activated, even if the child state is being refreshed (or loaded directly from a bookmark), the page will still render correctly.
REF: https://github.com/angular-ui/ui-router/wiki/Quick-Reference#statetransitiontoto-toparams--options
$state.transitionTo('yourState', params, {notify: false});
Adding my answer because I think it's different enough from the accepted answer and may be useful to others:
I had two states, begin and view, with a bunch of optional parameters being synced with the URL for view, like so:
$stateProvider
.state('begin',
{
url: '/',
template: '<app-element></app-element>'
})
.state('view',
{
url: '/View?param1¶m2&...¶mN',
template: '<app-element></app-element>'
params: {
param1: {
value: null,
squash: true
},
...
}
});
The link function for <app-element> would run any time I tried to sync the parameters using $state.go. Using {notify: false, reload: false} did not work for me. The link function still ran each time. I'm on 0.2 so dynamic isn't an available param option, either. I followed #b0nyb0y's suggestion and turned it into a parent/child relationship, which worked:
$stateProvider
.state('app',
{
url: '/',
template: '<app-element></app-element>'
})
.state('app.view',
{
url: 'View?param1¶m2&...¶mN',
params: {
param1: {
value: null,
squash: true
},
...
}
});
Currently our project is using default $routeProvider, and I am using this "hack", to change url without reloading page:
services.service('$locationEx', ['$location', '$route', '$rootScope', function($location, $route, $rootScope) {
$location.skipReload = function () {
var lastRoute = $route.current;
var un = $rootScope.$on('$locationChangeSuccess', function () {
$route.current = lastRoute;
un();
});
return $location;
};
return $location;
}]);
and in controller
$locationEx.skipReload().path("/category/" + $scope.model.id).replace();
I am thinking of replacing routeProvider with ui-router for nesting routes, but cant find this in ui-router.
Is it possible - do the same with angular-ui-router?
Why do I need this?
Let me explain with an example :
Route for creating new category is /category/new
after clicking on SAVE I show success-alert and I want to change route /category/new to /caterogy/23 (23 - is id of new item stored in db)
Simply you can use $state.transitionTo instead of $state.go . $state.go calls $state.transitionTo internally but automatically sets options to { location: true, inherit: true, relative: $state.$current, notify: true } . You can call $state.transitionTo and set notify: false . For example:
$state.go('.detail', {id: newId})
can be replaced by
$state.transitionTo('.detail', {id: newId}, {
location: true,
inherit: true,
relative: $state.$current,
notify: false
})
Edit: As suggested by fracz it can simply be:
$state.go('.detail', {id: newId}, {notify: false})
Ok, solved :)
Angular UI Router has this new method, $urlRouterProvider.deferIntercept()
https://github.com/angular-ui/ui-router/issues/64
basically it comes down to this:
angular.module('myApp', [ui.router])
.config(['$urlRouterProvider', function ($urlRouterProvider) {
$urlRouterProvider.deferIntercept();
}])
// then define the interception
.run(['$rootScope', '$urlRouter', '$location', '$state', function ($rootScope, $urlRouter, $location, $state) {
$rootScope.$on('$locationChangeSuccess', function(e, newUrl, oldUrl) {
// Prevent $urlRouter's default handler from firing
e.preventDefault();
/**
* provide conditions on when to
* sync change in $location.path() with state reload.
* I use $location and $state as examples, but
* You can do any logic
* before syncing OR stop syncing all together.
*/
if ($state.current.name !== 'main.exampleState' || newUrl === 'http://some.url' || oldUrl !=='https://another.url') {
// your stuff
$urlRouter.sync();
} else {
// don't sync
}
});
// Configures $urlRouter's listener *after* your custom listener
$urlRouter.listen();
}]);
I think this method is currently only included in the master version of angular ui router, the one with optional parameters (which are nice too, btw). It needs to be cloned and built from source with
grunt build
The docs are accessible from the source as well, through
grunt ngdocs
(they get built into the /site directory) // more info in README.MD
There seems to be another way to do this, by dynamic parameters (which I haven't used).
Many credits to nateabele.
As a sidenote, here are optional parameters in Angular UI Router's $stateProvider, which I used in combination with the above:
angular.module('myApp').config(['$stateProvider', function ($stateProvider) {
$stateProvider
.state('main.doorsList', {
url: 'doors',
controller: DoorsListCtrl,
resolve: DoorsListCtrl.resolve,
templateUrl: '/modules/doors/doors-list.html'
})
.state('main.doorsSingle', {
url: 'doors/:doorsSingle/:doorsDetail',
params: {
// as of today, it was unclear how to define a required parameter (more below)
doorsSingle: {value: null},
doorsDetail: {value: null}
},
controller: DoorsSingleCtrl,
resolve: DoorsSingleCtrl.resolve,
templateUrl: '/modules/doors/doors-single.html'
});
}]);
what that does is it allows to resolve a state, even if one of the params is missing.
SEO is one purpose, readability another.
In the example above, I wanted doorsSingle to be a required parameter. It is not clear how to define those. It works ok with multiple optional parameters though, so not really a problem. The discussion is here https://github.com/angular-ui/ui-router/pull/1032#issuecomment-49196090
After spending a lot of time with this issue, Here is what I got working
$state.go('stateName',params,{
// prevent the events onStart and onSuccess from firing
notify:false,
// prevent reload of the current state
reload:false,
// replace the last record when changing the params so you don't hit the back button and get old params
location:'replace',
// inherit the current params on the url
inherit:true
});
Calling
$state.go($state.current, {myParam: newValue}, {notify: false});
will still reload the controller, meaning you will lose state data.
To avoid it, simply declare the parameter as dynamic:
$stateProvider.state({
name: 'myState',
url: '/my_state?myParam',
params: {
myParam: {
dynamic: true, // <----------
}
},
...
});
Then you don't even need the notify, just calling
$state.go($state.current, {myParam: newValue})
suffices. Neato!
From the documentation:
When dynamic is true, changes to the parameter value will
not cause the state to be entered/exited. The resolves will not be
re-fetched, nor will views be reloaded.
This can be useful to build
UI where the component updates itself when the param values change.
This setup solved following issues for me:
The training controller is not called twice when updating the url from .../ to .../123
The training controller is not getting invoked again when navigating to another state
State configuration
state('training', {
abstract: true,
url: '/training',
templateUrl: 'partials/training.html',
controller: 'TrainingController'
}).
state('training.edit', {
url: '/:trainingId'
}).
state('training.new', {
url: '/{trainingId}',
// Optional Parameter
params: {
trainingId: null
}
})
Invoking the states (from any other controller)
$scope.editTraining = function (training) {
$state.go('training.edit', { trainingId: training.id });
};
$scope.newTraining = function () {
$state.go('training.new', { });
};
Training Controller
var newTraining;
if (!!!$state.params.trainingId) {
// new
newTraining = // create new training ...
// Update the URL without reloading the controller
$state.go('training.edit',
{
trainingId : newTraining.id
},
{
location: 'replace', // update url and replace
inherit: false,
notify: false
});
} else {
// edit
// load existing training ...
}
If you need only change url but prevent change state:
Change location with (add .replace if you want to replace in history):
this.$location.path([Your path]).replace();
Prevent redirect to your state:
$transitions.onBefore({}, function($transition$) {
if ($transition$.$to().name === '[state name]') {
return false;
}
});
i did this but long ago in version: v0.2.10 of UI-router like something like this::
$stateProvider
.state(
'home', {
url: '/home',
views: {
'': {
templateUrl: Url.resolveTemplateUrl('shared/partial/main.html'),
controller: 'mainCtrl'
},
}
})
.state('home.login', {
url: '/login',
templateUrl: Url.resolveTemplateUrl('authentication/partial/login.html'),
controller: 'authenticationCtrl'
})
.state('home.logout', {
url: '/logout/:state',
controller: 'authenticationCtrl'
})
.state('home.reservationChart', {
url: '/reservations/?vw',
views: {
'': {
templateUrl: Url.resolveTemplateUrl('reservationChart/partial/reservationChartContainer.html'),
controller: 'reservationChartCtrl',
reloadOnSearch: false
},
'viewVoucher#home.reservationChart': {
templateUrl: Url.resolveTemplateUrl('voucher/partial/viewVoucherContainer.html'),
controller: 'viewVoucherCtrl',
reloadOnSearch: false
},
'addEditVoucher#home.reservationChart': {
templateUrl: Url.resolveTemplateUrl('voucher/partial/voucherContainer.html'),
controller: 'voucherCtrl',
reloadOnSearch: false
}
},
reloadOnSearch: false
})
Try something like this
$state.go($state.$current.name, {... $state.params, 'key': newValue}, {notify: false})
In Angular 2, the accepted answer from RezKesh translates to the following:
this.uiRouter.stateService.go(
"home.myRouteState",
{
"param1": this.myParam1,
"param2": this.myParam2
},
{ notify: false }
);
Assuming you have injected UIRouter into your component's constructor as follows:
constructor(
private uiRouter: UIRouter
) { }
I don't think you need ui-router at all for this. The documentation available for the $location service says in the first paragraph, "...changes to $location are reflected into the browser address bar." It continues on later to say, "What does it not do? It does not cause a full page reload when the browser URL is changed."
So, with that in mind, why not simply change the $location.path (as the method is both a getter and setter) with something like the following:
var newPath = IdFromService;
$location.path(newPath);
The documentation notes that the path should always begin with a forward slash, but this will add it if it's missing.
My app uses $state.go when switching between tabs and this cause to re-initializes the controllers and scope variable in those controllers get updated and causes memory leaks. Is there a way to stop re-initializing the controllers but change URL on state change?
Example below is routes.js in my app.
.state('home',
{
abstract : true,
url : '/home',
templateUrl : 'scripts/home.html'
})
.state('home.summary',
{
url : '/summary?userId',
controller : 'SummaryCtrl',
views :
{
summary:
{
templateUrl: 'scripts/home/summary.html'
}
}
})
.state('home.summary.detail',
{
url : '/detail/:id',
controller : 'DetailCtrl',
views :
{
detail:
{
templateUrl: 'scripts/home/detail.html'
}
}
})
How to stop reloading DetailCtrl but change URL when going to state home.summary.detail if the DetailCtrl is already loaded for unique id???
Also tried to $q.reject in resolve of child state, it stops reload of controllers but doesn't change url.
Also tried reloadOnSearch=false it stops reload of controllers but doesn't change url.
You can use $state.transitionTo instead of $state.go . $state.go calls $state.transitionTo internally but automatically sets options to { location: true, inherit: true, relative: $state.$current, notify: true } . You can call $state.transitionTo and set location: false . For example:
$state.go('.detail', {id: newId})
can be replaced by
$state.transitionTo ('.detail', {id: newId}, { location: false, inherit: true, relative: $state.$current, notify: true })
You can have a abstract controller that contain your memory leak code.
And a abstract controller will not update when url change.
Here is the doc from official github repo
https://github.com/angular-ui/ui-router/wiki/Nested-States-%26-Nested-Views
look at the Combination part.
here is the plnkr, contacts controller will only call once in this demo
contacts.detail controller will update every time you change state but share the scope with contacts controller
http://plnkr.co/edit/gmtcE2?p=preview
I'm using Angular UI Router and would like to reload the current state and refresh all data / re-run the controllers for the current state and it's parent.
I have 3 state levels: directory.organisations.details
directory.organisations contains a table with a list of organisations. Clicking on an item in the table loads directory.organisations.details with $StateParams passing the ID of the item. So in the details state I load the details for this item, edit them and then save data. All fine so far.
Now I need to reload this state and refresh all the data.
I have tried:
$state.transitionTo('directory.organisations');
Which goes to the parent state but doesn't reload the controller, I guess because the path hasn't changed. Ideally I just want to stay in the directory.organisations.details state and refresh all data in the parent too.
I have also tried:
$state.reload()
I have seen this on the API WIKI for $state.reload "(bug with controllers reinstantiating right now, fixing soon)."
Any help would be appreciated?
I found this to be the shortest working way to refresh with ui-router:
$state.go($state.current, {}, {reload: true}); //second parameter is for $stateParams
Update for newer versions:
$state.reload();
Which is an alias for:
$state.transitionTo($state.current, $stateParams, {
reload: true, inherit: false, notify: true
});
Documentation: https://angular-ui.github.io/ui-router/site/#/api/ui.router.state.$state#methods_reload
This solution works in AngularJS V.1.2.2:
$state.transitionTo($state.current, $stateParams, {
reload: true,
inherit: false,
notify: true
});
That would be the final solution. (inspired by #Hollan_Risley's post)
'use strict';
angular.module('app')
.config(function($provide) {
$provide.decorator('$state', function($delegate, $stateParams) {
$delegate.forceReload = function() {
return $delegate.go($delegate.current, $stateParams, {
reload: true,
inherit: false,
notify: true
});
};
return $delegate;
});
});
Now, whenever you need to reload, simply call:
$state.forceReload();
for ionic framework
$state.transitionTo($state.current, $state.$current.params, { reload: true, inherit: true, notify: true });//reload
$stateProvider.
state('home', {
url: '/',
cache: false, //required
https://github.com/angular-ui/ui-router/issues/582
Probably the cleaner approach would be the following :
<a data-ui-sref="directory.organisations.details" data-ui-sref-opts="{reload: true}">Details State</a>
We can reload the state from the HTML only.
#Holland Risley 's answer is now available as an api in latest ui-router.
$state.reload();
A method that force reloads the current state. All resolves are
re-resolved, controllers reinstantiated, and events re-fired.
http://angular-ui.github.io/ui-router/site/#/api/ui.router.state.$state
$state.go($state.current, $stateParams, {reload: true, inherit: false});
$scope.reloadstat = function () { $state.go($state.current, {}, {reload: true}); };
if you want to reload your entire page, like it seems, just inject $window into your controller and then call
$window.location.href = '/';
but if you only want to reload your current view, inject $scope, $state and $stateParams (the latter just in case you need to have some parameters change in this upcoming reload, something like your page number), then call this within any controller method:
$stateParams.page = 1;
$state.reload();
AngularJS v1.3.15
angular-ui-router v0.2.15
Silly workaround that always works.
$state.go("otherState").then(function(){
$state.go("wantedState")
});
For angular v1.2.26, none of the above works. An ng-click that calls the above methods will have to be clicked twice in order to make the state reload.
So I ended up emulating 2 clicks using $timeout.
$provide.decorator('$state',
["$delegate", "$stateParams", '$timeout', function ($delegate, $stateParams, $timeout) {
$delegate.forceReload = function () {
var reload = function () {
$delegate.transitionTo($delegate.current, angular.copy($stateParams), {
reload: true,
inherit: true,
notify: true
})
};
reload();
$timeout(reload, 100);
};
return $delegate;
}]);
Everything failed for me. Only thing that worked...is adding cache-view="false" into the view which I want to reload when going to it.
from this issue https://github.com/angular-ui/ui-router/issues/582
Not sure why none of these seemed to work for me; the one that finally did it was:
$state.reload($state.current.name);
This was with Angular 1.4.0
I had multiple nested views and the goal was to reload only one with content.
I tried different approaches but the only thing that worked for me is:
//to reload
$stateParams.reload = !$stateParams.reload; //flip value of reload param
$state.go($state.current, $stateParams);
//state config
$stateProvider
.state('app.dashboard', {
url: '/',
templateUrl: 'app/components/dashboard/dashboard.tmpl.html',
controller: 'DashboardController',
controllerAs: 'vm',
params: {reload: false} //add reload param to a view you want to reload
});
In this case only needed view would be reloaded and caching would still work.
I know there have been a bunch of answers but the best way I have found to do this without causing a full page refresh is to create a dummy parameter on the route that I want to refresh and then when I call the route I pass in a random number to the dummy paramter.
.state("coverage.check.response", {
params: { coverageResponse: null, coverageResponseId: 0, updater: 1 },
views: {
"coverageResponse": {
templateUrl: "/Scripts/app/coverage/templates/coverageResponse.html",
controller: "coverageResponseController",
controllerAs: "vm"
}
}
})
and then the call to that route
$state.go("coverage.check.response", { coverageResponse: coverage, updater: Math.floor(Math.random() * 100000) + 1 });
Works like a charm and handles the resolves.
You can use #Rohan answer https://stackoverflow.com/a/23609343/3297761
But if you want to make each controller reaload after a view change you can use
myApp.config(function($ionicConfigProvider) { $ionicConfigProvider.views.maxCache(0); ... }
If you are using ionic v1, the above solution won't work since ionic has enabled template caching as part of $ionicConfigProvider.
Work around for that is a bit hacky - you have to set cache to 0 in ionic.angular.js file:
$ionicConfigProvider.views.maxCache(0);
I had this problem it was wrecking my head, my routing is read from a JSON file and then fed into a directive. I could click on a ui-state but I couldn't reload the page. So a collegue of mine showed me a nice little trick for it.
Get your Data
In your apps config create a loading state
Save the state you want to go to in the rootscope.
Set your location to "/loading" in your app.run
In the loading controller resolve the routing promise and then set the location to your intended target.
This worked for me.
Hope it helps