angular / angular.js

AngularJS - HTML enhanced for web apps!
https://angularjs.org
MIT License
58.85k stars 27.52k forks source link

Add parameter to $routeUpdate to indicate the URL change is triggered programatically #14553

Open buremba opened 8 years ago

buremba commented 8 years ago

Do you want to request a feature or report a bug? Feature.

When html5Mode is active and reloadOnSearch parameter is false, Angular fires $routeUpdate event when the URL is changed either by user via back/forward button of the browser or programmatically via $location.search. Currently, Angular doesn't send any parameter to $routeUpdate to indicate either the change is triggered by user action or programmatically.

I think we should add an extra parameter to $routeUpdate to indicate this information so that developers can reload the route if the URL change is triggered by user action.

When user changes the url via back / forward button of their browser, the state in controller must change. However if reloadOnSearch is false, back/forward button doesn't work because URL change doesn't cause controller to reload. The only way to get notified when user presses the back button is using $routeUpdate event; however currently it's not possible to find out if the change is triggered by user action.

$scope.$on('$routeUpdate', function() {
            if (routeChangeTriggerredByUser) {
                $route.reload();
            }
});

Related Stackoverflow questions: http://stackoverflow.com/questions/31084176/why-does-angular-ui-router-reloads-the-page-even-when-reloadonsearch-is-false-in http://stackoverflow.com/questions/23239683/browsers-back-button-doesnt-work-when-reloadonsearch-is-false-in-angularjs http://stackoverflow.com/questions/18970727/back-button-when-reloadonsearchfalse

Narretz commented 8 years ago

Hm, I don't really understand why there would be a difference between user interaction and programatic change. Shouldn't you do the same thing in both cases? Can you give an example where you'd want to do something different?

buremba commented 8 years ago

@Narretz The reason that I use reloadOnSearch is that the controller makes a bunch of XHR requests and draw some charts in DOM. For example, when the user wants to change a chart to from line chart to pie chart, I can change the chart type dynamically without redrawing whole UI and update the URL so that user can copy and share it. If I change the URL with $location.search, I don't want to do anything when Angular fires $routeUpdate but if user manually changes the URL, I want to reload the controller.

When Angular fires $routeUpdate, I need to change the state only if the user manually changes the URL. Currently the only way to do that is to deserialize the URL parameters and compare the deserialized state with the controller state and update the state if they don't match. However it comes with an overhead of unnecessarily deserializing the URL parameter and deep-comparing the objects that represent the states since $routeUpdate will also be fired when I change the URL automatically with $location.search.

Hope that helps.

gkalpak commented 8 years ago

Without looking much into it, I believe there is no direct connection between calling a $location method and firing the $routeUpdate event (or even the $locationChangeStart event). Thus, there is no straight-forward way for Angular to know if a change in the URL came from within the app or externally, except for setting a flag when calling a method that might change the URL and then checking whether the flag is set (e.g. from inside the event listener).

Since (a) this is an uncommon usecase and (b) there is no benefit in having the implementation built into the core, I think it's better to leave it for 3rd-party modules.

The exact implementation depends on your specific needs (e.g. do I care about updating the hash or not etc), but here is a POC: demo 1 One might be even tempted to decorate a service or two, in order to make the consumption of this feature transparent and easy: demo 2

Bottom line, I don't believe this belongs in core (but I'd be happily proven wrong :smiley:). I'm going to move this into the Ice Box, but I would like to hear what other think.