angular / angular.js

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

Directly modifying the URL path & search without any side effects. #3789

Open CMCDragonkai opened 11 years ago

CMCDragonkai commented 11 years ago

Is it possible to directly modify the URL's path and search without any sideeffects in terms of reloading, refreshing, reinitialising, or affecting AngularJS in anyway. Basically a pure cosmetic effect.

I need this sometimes when I need to correct the URL without doing anything else.

This could be useful for overlays that need a directly accessible URL. Or when someone enters the wrong title for a particular ID. See Stackoverflow, when you change the title but enter just the ID, it will correct the title portion of the URL to be correct.

I know history.pushState can do this, but when I try this inside AngularJS it results in 10 digest errors.

jankuca commented 11 years ago

Can you make a demo? I tried to call both history.pushState() and history.replaceState() with angular loaded and nothing happened. You are probably using routing right?

CMCDragonkai commented 11 years ago

Yes I was using ui-router state machine to do routing. Well regardless of whether it gives digest errors or nothing happening, manipulating the URL doesn't seem possible...?

jankuca commented 11 years ago

Are you saying that history.replaceState('', '', '/new-path') does not change the location for you?

CMCDragonkai commented 11 years ago

Well when I tried it gave me digest errors. Are saying it works for you?

petebacondarwin commented 11 years ago

If you replace the location outside of AngularJS it gets its knickers in a twist. $location relies on its own previous knowledge of what the location is in order to decide whether to update or not. If you change location outside of Angular, then $location gets stuck in a constant toing and froing causing a digest overflow. This should probably be fixed but I don't know how.

CMCDragonkai commented 11 years ago

Would it be possible to simply patch the location service to force a cosmetic change if possible?

petebacondarwin commented 11 years ago

I am going to look into this as it is going to be a problem for my github chrome extension.

btford commented 10 years ago

From what I understand, you can already accomplish this with either:

I have a modal services I implemented a wile ago that watches $location.hash(). It might be nice to compare notes. :)

I'm moving this to 1.2.1, but I think this can be closed.

CMCDragonkai commented 10 years ago

I know about those methods, but they only affect the hash or query parameters, not the path. Try using it with html5 urls. It doesn't work.From: Brian Ford Sent: Thu, Oct 3, 2013 07:38To: angular/angular.js angular.js@noreply.github.comCC: Roger Qiu roger.qiu@polycademy.comSubject: Re: [angular.js] Directly modifying the URL path & search without any side effects. (#3789)From what I understand, you can already accomplish this with either:

reloadOnSearch: false $location.hash() I have a modal services I implemented a wile ago that watches $location.hash(). It might be nice to compare notes. :)

I'm moving this to 1.2.1, but I think this can be closed.

—Reply to this email directly or view it on GitHub.

josebalius commented 10 years ago

@CMCDragonkai Did you ever find a solution for this? I need to change to do a history.replaceState outside of angular and digest is going crazy because of what @petebacondarwin said.

CMCDragonkai commented 10 years ago

I hacked up this solution using ui-router, but only with 0.4.0 not the latest version. To elegantly solve this problem, I think you will need to create the equivalent of ui-router and abstract the entire routing mechanism from AngularJS so that you can strictly control how routes map to the state changes. Sadly no easy solution as of yet. Check the issues of ui-router.

gkoberger commented 10 years ago

I am also having this problem. I'm trying to update the URL as a user scrolls, and it seems Angular has no desire to let this happen.

The closest hack I've managed is using $browser.url(...), however this reverts as soon as $apply() is called.

It would be great if there was a way to update $location's internals manually, perhaps?

mattuuh7 commented 9 years ago

now you should use the $locationProvider.html5Mode(mode) to enable html5 mode, then you can use history.pushState|replaceState

chenasraf commented 8 years ago

+1, still not working; history.replaceState for me causes the route to try to reload

gkalpak commented 8 years ago

I still don't see why this would be useful (assuming you rely on client side routing). Why change the URL to something that doesn't have any meaning (or a different meaning) in Angular? Wouldn't that break routing (e.g. deep-linking, forward/backward actions etc)?

Off the top of my head, it should be possible (famous last words :smile:) to implement something in $location for "cosmetically" changing the URL, but I haven't seen a compelling usecase yet (and we'll need a pretty compelling one if we are to add more complexity to one of our most complex services :wink:).

chenasraf commented 8 years ago

In my case, I have a temporary param that tells me where to scroll when the page loads. After it's done scrolling, I want to remove that param, without refreshing/reloading etc. transitionTo ended up being a solution for this case, though it didn't work for all cases for me.

gkalpak commented 8 years ago

This sounds like a good candidate for the hash, not part of the path. Also, removing it means that if the user copy-pastes the URL, they won't be taken to the same spot as they were taken by the initial URL (before the extra param was removed). That sounds confusing and unnecessary to me (but maybe I don't understand the usecase correctly).

(Not sure what transitionTo refers to btw :confused: )

chenasraf commented 8 years ago

Sorry, meant transitionTo in UI router (I guess I mistakenly found this issue thinking it's directly related), and hash is no good because it has the same effects when I try to remove it

gkalpak commented 8 years ago

Hash is different in that ngRoute (and I think ui.router too) does support updating the search/hash without reloading the route.

Btw, I have submitted a PR that let's you configure a route to not reload when the URL changes (#15002).

lukemadera commented 7 years ago

@gkalpak Here's my use case where I need to update the url (to allow deep linking) but NOT update the browser history: A table that stores the current sorting, filtering, and paging, so that if a user clicks a row on the table or leaves the page, but comes back to the page, they can keep their place in the table (e.g. go directly back to page 3). I've got that working fine. HOWEVER, the problem is that by updating the url on each table state change, the back button now just cycles through these different states, and that is NOT the desired behavior. The back button should go back to the previous page, not cycle through table states. Or rather only the LAST table state should be kept, so there's only ever ONE table state to go back to. Basically we need a way to update the url for deep linking but do nothing else (e.g. do NOT update the browser history). Here's a demo: https://apps.presencetest.com/components/tables (click the headers to sort and the url will update, but then clicking "back" goes back through each state, when it should just come back to this github issue in ONE click of the back button, no matter how many table states change).

Example url states:

  1. https://github.com/angular/angular.js/issues/3789#issuecomment-285794748
  2. https://apps.presencetest.com/components/tables
  3. https://apps.presencetest.com/components/tables?tc1o_order=first&tc1l_paging=3&tc1p_pageNum=1&tdo_ordering=unused&tdl_limit=25&tdp_page=1
  4. https://apps.presencetest.com/components/tables?tc1o_order=first&tc1l_paging=3&tc1p_pageNum=1&tdo_ordering=name_combined&tdl_limit=25&tdp_page=1
  5. https://apps.presencetest.com/components/tables?tc1o_order=first&tc1l_paging=3&tc1p_pageNum=1&tdo_ordering=last&tdl_limit=25&tdp_page=1

The browser back history we want though, instead of 4 entries, is only the last one:

  1. https://github.com/angular/angular.js/issues/3789#issuecomment-285794748
  2. https://apps.presencetest.com/components/tables?tc1o_order=first&tc1l_paging=3&tc1p_pageNum=1&tdo_ordering=last&tdl_limit=25&tdp_page=1

How can we achieve this? Thanks!

gkalpak commented 7 years ago

@lukemadera, your usecase doesn't sounds related to this issue. I think $location.replace() might be what you are looking for.

lukemadera commented 7 years ago

Thanks for the reply @gkalpak and yes, I was able to use history.replaceState() directly to change the url while also preventing the back button from cycling through the table states.

However, here is a related use case that I believe is this issue - a set of tabs, each with their own children routes, that refer to different sub-pages (in this case steps). Again, the desire is to be able to deep link directly to a specific (sub)route (step) but going "back" should NOT cycle through tabs, it should go to the previous page (top level route). However, as before with the table example, if you click through the steps, then click the back button, instead of coming directly back to this github issue, you cycle through the steps first.

https://apps.presencetest.com/components/other/step-2

{path: 'other', component: OtherDemoComponent,
                children: [
                    { path: '', redirectTo: 'step-1', pathMatch: 'full' },
                    { path: 'step-1', component: Step1Component },
                    { path: 'step-2', component: Step2Component },
                    { path: 'step-3', component: Step3Component },
                ],
            },

Perhaps the true issue here is we need a way to be able to specify replaceState: true on (sub)routes or in [routerLink] directly. That way, when you click a step, it just replaces the old history, rather than adding to it.

For example with the following url changes:

  1. https://github.com/angular/angular.js/issues/3789#issuecomment-285940707
  2. https://apps.presencetest.com/components/other/step-2
  3. https://apps.presencetest.com/components/other/step-3
  4. https://apps.presencetest.com/components/other/step-1?param1=1
  5. https://apps.presencetest.com/components/other/step-2?param1=1
  6. https://apps.presencetest.com/components/other/step-3?param1=1

The back history should only include the last one:

  1. https://github.com/angular/angular.js/issues/3789#issuecomment-285940707
  2. https://apps.presencetest.com/components/other/step-3?param1=1
lukemadera commented 7 years ago

Ah, whoops, I'm in the wrong repo; I just googled to get here and didn't realize this was Angular 1; I'm working with Angular 2.

teswar commented 6 years ago

@lukemadera you aren't alone... lol...

gkalpak commented 6 years ago

PArtially addressed by #15002 (in addition to the already supported reloadOnSearch config option, as mentioned in https://github.com/angular/angular.js/issues/3789#issuecomment-25579303).