I have a controller, with a route like this:
#/articles/1234
I want to change the route without completely reloading the controller, so I can keep the positi
Had the very same challange,
Found a hack in another StackOverflow response that did the trick
Fairly clean solution - all I did was to add these lines to the controller that sets $location.path:
var lastRoute = $route.current;
$scope.$on('$locationChangeSuccess', function(event) {
$route.current = lastRoute;
});
..and made sure $route in injected into the controller of course.
But still, feels like "DoNotFollowRoutesOnPathChange" is a missing feature in AngularJS.
/Jens
Update: Since listening to this event effectively kills further usage of $routeProvider configs, I had to limit this catch to current path only:
var lastRoute = $route.current;
if ($route.current.$route.templateUrl.indexOf('mycurrentpath') > 0) {
$route.current = lastRoute;
}
Getting ugly...
Here is plugin: https://github.com/anglibs/angular-location-update
Usage:
$location.update_path('/notes/1');
You can use the $location.search()
method as you mentioned. You should listen to the "$routeUpdate"
event on scope instead of other route events. $route API.
First of all (you already know), add reloadOnSearch: false
to your $routeProvider
:
$routeProvider.when('/somewhere', {
controller: 'SomeCtrl',
reloadOnSearch: false
})
Change your anchor tag href
or ng-href
to href="#/somewhere?param=value"
this will trigger $routeChangeSuccess
event if the path part (/somewhere
) is not the same as current location. Otherwise it will trigger $routeUpdate
event.
Listen event on scope:
$scope.$on("$routeUpdate", function(event, route) {
// some code here
});
If you want to change search params in code, you can use $location.search()
method. $location.search API.
define a factory to handle HTML5's window.history like so (note I keep an own Stack to make it work on android as well since it has a few issues there):
.factory('History', function($rootScope) {
var StateQueue = [];
var StatePointer = 0;
var State = undefined;
window.onpopstate = function(){
// called when back button is pressed
State = StateQueue.pop();
State = (State)?State:{};
StatePointer = (StatePointer)?StatePointer-1:0;
$rootScope.$broadcast('historyStateChanged', State);
window.onpopstate = window.onpopstate;
}
return {
replaceState : function(data, title, url) {
// replace current state
var State = this.state();
State = {state : data};
window.history.replaceState(State,title,url);
StateQueue[StatePointer] = State;
$rootScope.$broadcast('historyStateChanged', this.state());
},
pushState : function(data, title, url){
// push state and increase pointer
var State = this.state();
State = {state : data};
window.history.pushState(State,title,url);
StateQueue.push(State);
$rootScope.$broadcast('historyStateChanged', this.state());
StatePointer ++;
},
fakePush : function(data, title, url) {
// call this when you do location.url(url)
StateQueue.push((StateQueue.length - 1 >= 0)?StateQueue[StateQueue.length -1]:{});
StatePointer ++;
$rootScope.$broadcast('historyStateChanged', this.state());
},
state : function() {
// get current state
return (StateQueue[StatePointer])?StateQueue[StatePointer]:{};
},
popState : function(data) {
// TODO manually popState
},
back : function(data) {
// TODO needed for iphone support since iphone doesnt have a back button
}
}
})
add a few listeners on your dependent scopes and you will be fine like so:
$scope.$on('historyStateChanged', function(e,v) {
if(v)
$scope.state = v;
else
$scope.state = {}
});
thats how I do it. In my point of view the URL should only change when a new view is loaded. I think thats how the Angular Team intended it anyways. If you want to map forward/back button on your model try to do it with HTML5's window.history
Hope I could help. Cheers, Heinrich