angular-ui / ui-router

The de-facto solution to flexible routing with nested views in AngularJS
http://ui-router.github.io/
MIT License
13.53k stars 3k forks source link

Possibility to not reload state when state parameter is changed #100

Closed stasgrom closed 11 years ago

stasgrom commented 11 years ago

Hi,

currently our URLs look like:

/requirements/:id/details/comments

When id changes, several views are reloaded (specifically the details and the comments related ones). We would like a possibility to get a notification on the change of a parameter without actually reloading the state. The problem with reloading the details and comments states right now is that the views are re-generated and one could see the flickering on the screen, while in fact the only thing we want to change is the data inside the view.

I understand that currently the ui-router doesn't support reloadOnSearch=false or a similar concept, which could help me here if I decided to move the id parameter to the search part of the URL. Is this option planned to be supported? Are there any suggestions of how to handle the problem right now?

Thanks.

stasgrom commented 11 years ago

I checked and saw that if I use a search URL parameter that isn't modeled in any state then changing this parameter in the URL doesn't reload any state. This is of course good, since I can actually listen to the changes on $location and perform my own logic. The problem however is that once I transfer to another state using the $state.transitionTo() API (for example from ../comments to ../attachments) I'm losing this parameter in the URL, since ui-router lets through only parameters that are known to the new state.

jssebastian commented 11 years ago

I am also looking for the ability to not reload on URL changes, although in my case I am changing the path rather than the query parameters. This is actually the reason I started looking at ui-router instead of angular's $routeProvider, because $routeProvider's reloadOnSearch option does not prevent reloading when the path part of the url changes.

I have several use cases for it. One is the familiar master/details pattern similar to what @stasgrom mentioned. That is, when transitioning from:

/user/33/events

to

/user/33/events/245

the parts of the page that are not related to the individual event number 245 should not need to reload.

Another is that even for widgets that need to be updated on the URL change, I would prefer to manually call an update method that preserves some UI state. This is for UI state that is not reflected in the URL, such as what type of graph to use to display some data, which columns of a table to show, etc. I could refactor this state out of the controller but I think that is not the cleanest architecture (I have multiple instances of the same widget on a page, so I would need to look up the state of the correct instance on reload).

ksperling commented 11 years ago

I'm wondering if another interesting approach to preserving some UI state would be to give the (new) controller access to the scope of the previous controller for the same view in cases where they are 'compatible', so you could copy across that data.

(per-parameter reload=false support is still on the TODO list though)

laurelnaiad commented 11 years ago

Great work on the router. I mean it. I realize I'm appearing out of nowhere but this issue just gave me the heebeejeebies.

I just left the Ember camp for Angular (at least in theory) and was feeling awesome about it -- studied up, had some good architectural thoughts about how to accomplish my goals... and it never occurred to me that parent views would be reloaded when they're still in the URL hierarchy... It's after 1 am and now I'm not going to be able to sleep!!!

Just kidding (sort of). That'll teach me to read GitHub issues and study this late at night. So if the path "/user/33/events/245" has nested ui-views for "user" and "user/:userId" and "user/:userId/events" and "user/:userId/events/:eventId" then transitioning to event ID 246 is going to rerender all of those templates? Uhoh. If the user template is long and scrolls vertically on the left side of the page (just for example) and the user has scrolled to the bottom, then it will be rerendered and "unscrolled" (i.e. scrolled to the top) due to transitioning to a different event? And all of that code is rerun? AJAX calls re-called?

You guys are scaring me! So much of the Angular API -- and ui-router -- fits my needs... I hope I'm misunderstanding.

jeme commented 11 years ago

@jssebastian I might actually have something covering that, basically it's a feature called sticky views, this cause the view not to reload if the sticky matches (unless it is a child of another view that is reloaded).

Instead if it's sticky, it will call a refresh method on the scope if defined, otherwise it will raise a $refresh event on the scope.

That is a feature of the views and not the state though, but I think it can cover it though.

I don't know it it makes sense for the ui-router though, as it has quite a different internal structure, and it also depends on weather or not @ksperling think it would be a fit.

laurelnaiad commented 11 years ago

@jeme you may have saved my night. I'm going to take a look at your project with fresh eyes tomorrow. Cheers! I really don't want to go back to Ember.

jeme commented 11 years ago

@stu-salsbury Keep in mind that both ui-router and angular-routing are in pre-release phases as opposed to Ember which is stable.

So in that way, they may still present a greater set of challenges or bugs. But feedback and contributions are more than welcome.

jssebastian commented 11 years ago

@jeme thanks for the pointer: I actually have not looked at angular-routing yet, I'll give it a look

laurelnaiad commented 11 years ago

Thanks @jeme. I'm totally fine with the earlier phases. More chance to contribute!

I do need to settle on something that supports views/controllers/models where parents don't reload when their children are swapped out -- that's just a requirement for what I'm building (it requires a very deep and featureful automagically-generated navigation UI). Somehow I got pretty far into Angular before realizing it didn't have that kind of nesting support by default (too much architecture and design in my head is the obvious culprit -- I was busy designing my navigation service while grocery shopping).

There are a number of reasons why I'd prefer to stick with Angular, so for now my plan is to figure out how to do that, even if it means getting my hands dirty on the way. I look forward to making it work with ui-router or angular-routing or whatever ends up being a good fit. Both projects are exciting.

jssebastian commented 11 years ago

@ksperling:

I'm wondering if another interesting approach to preserving some UI state would be to give the (new) controller access to the scope of the previous controller for the same view in cases where they are 'compatible', so you could copy across that data.

I wonder how you would know which scope in the old view maps to which scope in the new view... with ng-repeats and ui-ifs, this could get complicated. Not sure if there is a general solution.

My app is built as a tree of widgets, so perhaps the best way to keep state is to not burden the routing with this task but use a service to preserve this state outside the scope, with some application-specific logic to find the state for a widget given an identifier or a position in the tree or something.

(per-parameter reload=false support is still on the TODO list though)

I think there are certainly cases where it could be useful. There are situations where I know how to update the view to react to an event, and the only reason I want to update the URL is so it is a working deep-link for the user. For instance if a user changes graph type, I have an animated transition between the two types. I don't want the entire page and graph to reload, but I would like to record the graph type in the URL. Also I don't want to lose the user's scroll position.

Perhaps a mechanism to tell ui-router to ignore a specific url update completely could do the trick, though it may not be the cleanest design choice.

laurelnaiad commented 11 years ago

I think there are certainly cases where it could be useful. There are situations where I know how to update the view to react to an event, and the only reason I want to update the URL is so it is a working deep-link for the user. For instance if a user changes graph type, I have an animated transition between the two types. I don't want the entire page and graph to reload, but I would like to record the graph type in the URL. Also I don't want to lose the user's scroll position.

That pretty much says what I meant. Having reviewed the original issue of this thread, I realize that this talk of "keeping the M, V and C intact in parent states/routes" thing is turning into hijacking, so I'll stop.

I'm green on Angular, but it seems like the idea of passing scope to the subsequent controller with something that signals "you're new to this page and here's the pre-existing scope, take it or leave it" would be a good start for the issue of preserving model data during transitions -- especially if the new controller has enough context to know what to do with the previous scope (i.e. how to interpret it) -- who gave this to me? Or as U.S. Vice Presidential Candidate Admiral John Stockdale famously said in his debate, seemingly in senility, "Who am I? Why am I here?" So if the controller function in the state got parameters for "departingScopes" and "departingState" (or had access to them) it could make the decisions on what to carry forward and how.

lmessinger commented 11 years ago

one option is to patch (which is what I resorted to) as follows: function transitionTo(to, toParams, updateLocation,reloadOnSearch) { if (!isDefined(updateLocation)) updateLocation = true; if (!isDefined(reloadOnSearch)) reloadOnSearch = false /or true, up to you/;

not sure if it'll make sense for you or maybe even for a pull (@ksperling?)

ksperling commented 11 years ago

@stu-salsbury Just to clarify the current behaviour, parent views are only reloaded if a parameter value belonging to that state is changed, i.e. if you navigate from http://angular-ui.github.io/ui-router/sample/#/contacts/42 to http://angular-ui.github.io/ui-router/sample/#/contacts/1 in the sample app, the outer 'contacts.html' template and associated controller are NOT reloaded/recreated. This is intentional and is not going to change.

What this issue is about is keeping some views (i.e. controllers + templates) around even when the associated parameters DO change, and having some ability for the controller to detect and handle that change itself.

laurelnaiad commented 11 years ago

@ksperling: gotcha -- thanks very much for clarifying... that sounds very useful, too :)

carloc commented 11 years ago

@ksperling that last comment was VERY interesting and was the reason I started using ui-router straight away (I'm new to angular, doing my first project). I can see that this is the case because I'm looking also at the backend log and I see that when changing a parameter that causes a reload of a single view, only the queries related to it are carried out. The one thing I don't understand, though, is why my page scrolls back up. Using your example, if the contacts.list view becomes too long to cause scrolling, when clicking on a contact the page would scroll back up. Is there any way to avoid this behavior? I hope you can understand my concerns, sorry for my english! Keep up the good work! ;)

timkindberg commented 11 years ago

scrolling is being discussed here in #110

carloc commented 11 years ago

Thank you very much! :+1:

stasgrom commented 11 years ago

I'd like to follow up on this issue, as we're desperately in need of this functionality.

I saw that in the roadmap of the project there's a mentioning of implementing the "reload=false". Is this the support to not reload the state if a URL parameter changes? If yes, great! When do you think this API will become available?

Thanks.

nateabele commented 11 years ago

@stasgrom It'll become available as soon as someone has time to implement it. If you'd like to help, you can find me in the #angularjs IRC channel on Freenode.

nateabele commented 11 years ago

Redirecting this discussion to #125, which covers complex parameters, parameter typing, and configurable reloads.