Skip to content

RFC: Default URL generation for optional parameters #1501

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
christopherthielen opened this issue Oct 31, 2014 · 54 comments
Closed

RFC: Default URL generation for optional parameters #1501

christopherthielen opened this issue Oct 31, 2014 · 54 comments
Labels
Milestone

Comments

@christopherthielen
Copy link
Contributor

Squashing URLs

With the addition of optional URL parameters, UI-Router can now generate friendlier URLs. For example, with the following states created with default parameter values:

.state('user', {
  url: '/user/:username',
  params: { username: function(SessionService) { return SessionService.username; }
}).state('user.photos', {
  url: '/gallery/:galleryid',
  params: { galleryid: 0 }
}).state('user.photos.photo', {
  url: "/:photoid"
});

This RFC hopes to determine the default url squashing behavior. When the user navigates to user ($state.go('user');), the full URL with parameter included would be #/user/christopherthielen. However, we can squash this URL down to: #/user/ or even #/user by omitting the parameter's default value.

When the user navigates to user.photos, the full URL with parameters would be #/user/christopherthielen/gallery/0, but we can squash this down to: #/user//gallery/ or even #/user/gallery/ which looks even nicer.

Of course, we can transition to the same user.photos state with non-default parameters too. Then, we cannot omit the parameter value; the URL must reflect the non-default parameters. For example, $state.go('user.photos', { username: 'someotheruser' }) will generate a full URL #/users/someotheruser/gallery/0 or a squashed version: #/users/someotheruser/gallery/.

Squashing slashes

Omitting the slashes from the URL can potentially generate ambiguous URLs

In the above example, if we navigate as follows: $state.go('user.photos.photo', { username: 'christopherthielen', galleryid: 0, photoid: 21872 }), the full URL (no squashing) would be: #/user/christopherthielen/gallery/0/21872, the squashed URL would be: /#user//gallery//21872, and the URL with slashes squashed would be: #/user/gallery/21872.

Note that #/user/gallery/21872 (slashes squashed) is ambiguous. Is 21872 the galleryid or is it the photoid? It was intended to be photoid 21872, but if the user hit reload in their browser, they would instead return to galleryid 21872.

Here is another example from #1487. It sets up a state with 3 parameters, each with defaults.

.state('test', { 
  url: '/:fooid/:barid/:bazid',
  params: { fooid: 123, barid: 456, bazid: 789 }
}

If we do $state.go('test', { bazid: 10000 }) and squash slashes, then the url generated is #/10000. After a reload, that url would load state test with param values of { fooid: 10000, barid: 456, bazid: 789 }.
#3 Parameter Squashing options

All three parameter squash options ("nosquash", "value", "slash") will likely be available for use in for 0.2.12. The code to process them is in a branch, pending merging to master. You can view the branch here: https://github.com/christopherthielen/ui-router/compare/angular-ui:master...master ...

You will be able to set UI-Router's default squash policy using $urlMatcherFactoryProvider.defaultSquashPolicy(). You will be also able to set the squash policy on individual parameters .state({ name: 'foo', url: '/foo/:fooid', params: { fooid: { value: 12345, squash: "nosquash" } } });

Default behavior? - Finally, getting to the point!

What behavior would you expect from UI-Router "Out of the box"

.state('user', {
  url: '/user/:username',
  params: { username: function(SessionService) { return SessionService.username; }
}).state('user.photos', {
  url: '/gallery/:galleryid',
  params: { galleryid: 0 }
}).state('user.photos.photo', {
  url: "/:photoid"
});

With the preceding states, when an optional parameter's default values is used, should the out-of-the-box policy be to:

  • (A) squash both the default parameter value and one slash. The developer is responsible to organize state urls and parameters such that ambiguous URLs cannot happen. URLs are prettier, such as #/user/gallery/, but the developer may not notice that they generate ambiguous URLs such as #/user/gallery/21872.
  • (B) squash default parameter values only. The default behavior generates unambiguous URLs (users can refresh and return to the same state/params). The developer must explicitly choose to squash the slashes around a default parameter value. URLs are less pretty, such as #/user//gallery//21872 but will generally map back to the state and parameters that they're generated for.
  • (C) Do no squash default parameter values from the URL by default. The URL is updated with the default values such as #/user/christopherthielen/gallery/0/21872. This is the most verbose.

Which option for UI-Router would surprise you the least?

@nateabele
Copy link
Contributor

@NevilleS
Copy link

Option B seems better for me, since I'm worried that pretty much all nested states where the parent had a default parameter defined would be, by definition, ambiguous.

For example, is the URL #/user/gallery referring to #/user/:userid' withuserId: 'gallery'`? I'm not sure how, as a developer, I can make removing the slashes non-ambiguous.

@christopherthielen
Copy link
Contributor Author

@NevilleS that can be worked around in some cases using a regexp on the default parameter, which ensures the next segment doesn't match against the parameter. For instance, if :userid is always an integer value, then you could do "/user/{userid:[\\d+]}".

@NevilleS
Copy link

I'd say those are exceptions rather than the rule, so the default behaviour would be to assume that squashing the slashes is unsafe, no? Otherwise this breaks the refresh behaviour for the majority of existing apps, I'd think...

@cesarandreu
Copy link
Contributor

I'd say squashing is a bad idea. What's the point?

It seems like it'd add complexity.
Expected behavior for me would be for the router to not do any of this.

@NevilleS
Copy link

It's nice that #/user/ would by default go to #/user/NevilleS, so you could generically say "Go to #/user/ to see your profile", for example.

@NevilleS
Copy link

By the way, I can't cite the RFC or anything, but I'm pretty sure most pages treat multiple slashes as irrelevant. In other words, https://github.com/angular-ui/ui-router/issues/1501 is the same as https://github.com/angular-ui/ui-router//////issues/1501, but in your proposal the latter would imply several squashed child states...

@wesleycho
Copy link
Contributor

I think I like A over B since urls would look prettier - I tend to prefer prettier urls, and anything to help towards that end would be great.

@christopherthielen
Copy link
Contributor Author

@wesleycho of course, you can still get squashed parameters with either scenario. With B you would have to add squash: true to the param.

@christopherthielen
Copy link
Contributor Author

I added

(C) Do no squash default parameter values from the URL by default. The URL is updated with the default values such as #/user/christopherthielen/gallery/0/21872. This is the most verbose.

@ragefuljoe
Copy link

I think from a use standpoint not squashing by default will lead to less confusions. While useful, this seems like it should be an opt-in feature.

@wbk
Copy link

wbk commented Nov 4, 2014

The potential ambiguity seems scary and bothersome. Couldn't the situation be detected and avoided? Consider this option D.

$state.go('user') understands that it needs to visit a state named user. If it creates a route which does not unambiguously resolve to the user state, certainly it could take some sort of preemptive action.

What if a special character, i.e. - or _, were introduced which would imply "I'm the default value!", but were only injected in cases of ambiguity, and were squashed entirely when targeting a route with the fewest sections among routes which could potentially match.

E.g. assume with two routes,/a/:b and /a/:b/:c, each with default values. /a/:b/c would require the near-fully-qualified /a/-/-, because it contains the most sections. /a/:b, on the other hand, could simply be /a. The only potential ambiguity would be if we were to resolve something like /a/-. I'd argue this should resolve to /a/:b, because it contains the fewest sections of any possible matching routes.

#/user/myuser/gallery/0/21872/, when targetting a photo, would become #/user/gallery/-/21872/.
#/user/myuser/gallery would become #/user/-/gallery.
#/user/myuser would be fully squashed to #/user.

I think this accomplishes the goal of creating pretty routes while remaining easy to understand and predict. It would allows multiple routes to resolve to the same state, with the same parameters, but I can't think of a simple case where you would be routed somewhere unexpected.

Is there a chance anyone is the wild is using a '-' as a path segment for anything meaningful?

@NevilleS
Copy link

NevilleS commented Nov 4, 2014

Is there a chance anyone is the wild is using a '-' as a path segment for anything meaningful?

Yes 😄. URIs aren't a new thing (original RFC published in '98) and they mean a lot. For example, you can create a folder via mkdir ~/- and reference it from your browser like file:///Users/neville/-/. The RFC is pretty clear about what constitutes a path, query param, etc., and the character set is only partially restricted (e.g. ? is not permitted in a path since it denote the start of query params).

Given that handing the URL is part of what browsers do, personally I wouldn't want ui router to do anything fancy about squashing parameters, substituting special characters, etc., since it's unclear how the behaviour would vary between Chrome, FF, IE, etc., and I'm inclined to be defensive when it comes to this kind of thing (here be dragons). But that doesn't mean I'm going to try to stop you cowboys from redefining what a URL means, by all means go for it!

... just not in the defaults 😄

@wbk
Copy link

wbk commented Nov 4, 2014

The RFC is pretty clear about what constitutes a path, query param, etc.

It's also clear about what a fragment identifier is. Specifically,

A fragment identifier component is indicated by the presence of a
number sign ("#") character and terminated by the end of the URI.

It also contains gems like

Fragment identifier semantics are independent of the
URI scheme and thus cannot be redefined by scheme specifications.

and even

The characters slash ("/") and question mark ("?") are allowed to
represent data within the fragment identifier.

According to the RFC, i think everything being discussed is fair game.

@christopherthielen
Copy link
Contributor Author

@wbk that's an interesting idea. I actually think that is how I would prefer my apps to squash urls if I were to squash them. It would preclude you from setting a parameter value to the special character "-" or "_" however.

@NevilleS
Copy link

NevilleS commented Nov 4, 2014

@wbk totally fair game, agreed. Although keep in mind ui router supports HTML5 mode which removes the #, getting you squarely back into the original definition of reserved/unreserved/escaped characters, so I'd be a bit surprised to see ui router start adding ; or = into the path 😃

@sacho
Copy link

sacho commented Nov 5, 2014

Having A as the "out of the box" configuration would be inviting disaster. It would lead to a ton of "why doesn't my routing work" questions by people who aren't aware of it. "Best practices" and "tutorials" will quickly start suggesting replacing A with C until "you know what you're doing".

On the other spectrum, the obviously least-surprising option is C. I think, if we are to only consider A, B or C, the point of contention here would be whether B is worth placing over C. While I can see the merit behind B, I can envision an outcry of "Double slash urls are ugly!" against it - it's just not something commonly seen on the web, currently.

I think you should have C as the out-of-the-box configuration - as the least hassle when supporting ui-router.

@nickeddy
Copy link

nickeddy commented Nov 6, 2014

I would be alright with having A available optionally, though most likely I would stick with C.
Explicit > implicit.

@marcghorayeb
Copy link

Hey, sorry i'm late to the discussion!
To be honest, C is the one i'm hoping for. There's too much "magic" already going on...
If a developper wishes to create a shortcut or an alias, say /user for /user/marcghorayeb, I'd rather see him handle it than ui-router.
Furthermore, if you go around and check what's being done on various websites, people are kind of going away from the whole /controller/:var. If you check on github for example, the profile is always just the username, whether i'm on my profile or someone else's. I've always thought of a URI as having to be clean, concise, and universal.
Last but not least, browsers are starting to hide more and more data of the URI (the latest safari shows only the domain, I think opera or chrome were onto something similar?). Trying to do too much on the url when, in the end, it won't be that meaningful for the enduser, is a waste of time in my opinion.
However, if this is really something being considered, squashing parameters should really be a whitelist kind of process, rather than blacklist if you see what I mean.

@JakobJingleheimer
Copy link
Contributor

I concur with everyone? above: C

C is the most obvious, and virtually no WTFs/min.

B looks weird to me, like something broke. I would rather something look a little confusing than for it to look broken. Probably a few WTFs for someone who didn't read the documentation too well.

A is more for the advanced user: The potential to "break" and high(er) rates of WTFs/min, so I think it should be an opt-in.

I'm definitely looking forward to A, but I think C should be the default.

Sidenote: A boolean seems more intuitive to me for the params object:
squash: true squash everything (value and slash) /user/photos
squash: null squash the value but leave the slash /user//photos
squash: false don't squash anything and populate with the default /user/jshado1/photos

And the policy setter would expect the string
$urlMatcherFactoryProvider.defaultSquashPolicy('value');
$urlMatcherFactoryProvider.defaultSquashPolicy('slash');
$urlMatcherFactoryProvider.defaultSquashPolicy('nosquash');

Just saw in the comparison that you already thought of the boolean thing.

@wesleycho
Copy link
Contributor

I gave my input without some of these other choices around - I think C should be the default, it does not affect current users. A is a nice feature, but as others have said, if the choice of it being opt-in exists & isn't onerous, it is preferred to avoid breaking changes.

@kevinrenskers
Copy link

Totally agree. Option C makes the most sense as the default value.

@christopherthielen
Copy link
Contributor Author

Thanks all for your comments. I have one more thing to note, considering option C (nosquash) seems to be favored. With our sample states:

.state('user', {
  url: '/user/:username',
  params: { username: function(SessionService) { return SessionService.username; }
}).state('user.photos', {
  url: '/gallery/:galleryid',
  params: { galleryid: 0 }
}).state('user.photos.photo', {
  url: "/:photoid"
});

If a user manually types a URL like:
#/user/
which maps to a default parameter value, option C is going to rewrite the URL to
#/user/christopherthielen
. I don't think it's common for users to type URLs, generally they'll be generated by navigating through the app, but I wanted to mention this.

@DovydasNavickas
Copy link

@christopherthielen you're saying that if user goes to
#/user
It redirects one to
#/user/christopherthielen
Automatically and then works as desired, right?

Or is it that
#/user/
Will not work at all?

@marcghorayeb
Copy link

@christopherthielen If the username resolves to null, will it consider the route to be invalid, and thus try to validate the route against another state, or will it just halt there ?

@stereokai
Copy link

Sorry for chiming in so late, been quite busy.

I don't want to answer in Chris's stead but I believe what he meant was that accessing a state with an optional parameter, without passing the parameter in the url, would result in falling back to the default parameter, rewriting the url suitably (i.e., no website.com/user, but website.com/user/logged_in_username, and proceeding to load that state proper.

I hope that makes sense.

In regards to the original question, having spent hours implementing my fork of UI-Router supporting optional parameters, I am well aware of all the difficulties, both in code and in user experience, the desired flexibilty of such a squashing model brings. However, I have a proposal for a fifth option -
Consider this option E.

Instead of squashing the default states (which leads to ambiguity), we should take a direct approach to avoid ambivalent URLs. If the URL part of a parent-state collapses may be collapsed, it should get substituted with the name of the next child state, so using the original example, it should like like this:

$state.go('user.photos.photo', { username: 'christopherthielen', galleryid: 0, photoid: 21872 })
Original URL: #/user/christopherthielen/gallery/0/21872,
Squashed URL: /#user/gallery/photo/21872

This makes it clear - the value after the next slash belongs to photo child state, and photos (or gallery) is ignored and populated with the default value.

Call it hindsight routing. Call pseudo-rewrite. Call it a protection mechanism or what have you - my original implementation relied on the same principle (but in reverse) to break apart URLs to figure which state the user asked for.

@DovydasNavickas
Copy link

@stereokai has a really nice option E.
This way the ambiguity of URLs is going to be quite rare (or there will be none at all).
Also, it is a user-friendly AND SEO-effective way of formatting the URL, which I think is just briliant.

Of course, code wise it might be not as easy implementation as no-squashing, but from all of the options I would choose E.

@christopherthielen
Copy link
Contributor Author

you're saying that if user goes to #/user It redirects one to #/user/christopherthielen

@DovydasNavickas yeah. basically, this:

  • $locationChangeStart (is #/user/)
  • $urlRouter.update()
    • check rules, find first match for #/user/ which is regexp /user/(?:[^/]+)?
    • run match, which calls state.go("user", { username: undefined })
  • transitionTo loads default values, now params are { username: "christopherthielen" }
  • finishes transition to user { username: 'christopherthielen' }
  • sets $location.url() to #/user/christopherthielen

@stereokai
Copy link

@christopherthielen not quite, "squash slashes" collapses URLs, my proposal replaces the parts which are supposed to be "squashed" with disambiguative (is that even a word?) information, respective to each state in the URL construction...

@DovydasNavickas
Copy link

@christopherthielen, I still think that @stereokai suggested option E is really really good balance between user and developer friendliness. Any reasons not to make it a default behavior?

@christopherthielen
Copy link
Contributor Author

@stereokai
I guess I'm not getting what you're saying because the example URLs don't seem to align with what you wrote. Help me understand; are you saying with states like these:

userstate: { name: 'user', url: '/user/:username' }
gallerystate: { name: 'gallery', url: '/:galleryid' }
photostate: { name: 'photo', url: '/:photoid' }

that:


Given: $state.go("user.gallery.photo", { username: 'christopherthielen', galleryid: 0, photoid: 21872 })
(default username and galleryid)
Defaults squashed using option E makes this url:
#/user/gallery/photo/21872

  • :username replaced with gallery because gallery is the next state
  • :galleryid replaced with photo because photo is the next state

Given: $state.go("user.gallery.photo", { username: 'johndoe', galleryid: 28, photoid: 9191 j});
Non-default params using option E makes this url:
#/user/johndoe/28/9191

  • no defaults used

@stereokai
Copy link

@christopherthielen Exactly so.

@NevilleS
Copy link

NevilleS commented Nov 9, 2014

Maybe I'm missing something, but how does this scheme remove the ambiguity
of #/user/gallery? Is "gallery" a user name or a squashed parameter
replaced by the next state? I don't think it's possible to know, unless you
somehow can guarantee that "gallery" is an invalid user name...
On Nov 9, 2014 11:53 AM, "Stereokai" [email protected] wrote:

@christopherthielen https://github.com/christopherthielen Exactly so.


Reply to this email directly or view it on GitHub
#1501 (comment)
.

@christopherthielen
Copy link
Contributor Author

@NevilleS agreed. There might be a bunch of substates too, each with their own names that may conflict with the :username parameter. It's a somewhat reasonable choice for url generation, but doesn't remove the ambiguity.

However, I find it odd because #/user/gallery/photo/21872 and #/user/johndoe/28/9191 don't look remotely similar, and as such, I wouldn't intuitively expect them to route to similar places in the app. Personally, I would prefer #/user/gallery/photo/21872 and #/user/johndoe/gallery/0/photo/21872.

At this point, I'm convinced that option C is the best choice (least surprising, always routes correctly) for defaults and the developer can then choose to squash. The code I have committed to master currently defaults to "nosquash" and supports "value" and "slash". I can't endorse option E at this time because it (1) seems more confusing to me, not less, and (2) I believe it requires knowledge of all immediate child states to properly implement.

@DovydasNavickas
Copy link

@NevilleS got a good point. Although it would be awesome to have ambiguity resolving done under-the-hood, I also agree that option E is not a solution, at least not yet.
Most people choice was option C. I'm glad you chose it to be the default one.

@christopherthielen
Copy link
Contributor Author

I've got a plunk partially implemented with 0.2.12-dev showing default param behavior with "nosquash".

http://plnkr.co/edit/VMPc8D7oUG0B1R3QuiCE?p=preview

What I said earlier about "/user/" rewriting to "/user/christopherthielen" seems false at first glance, because a registerState() sets { location: false} in the options.

@NevilleS
Copy link

NevilleS commented Nov 9, 2014

OK, sounds good,

@DovydasNavickas
Copy link

@christopherthielen I played a bit with your plunk:
http://plnkr.co/edit/PjnjE8FmiEe9p8MJcrlI?p=preview

What I added are four buttons for routing:

  • Christopher Thielen
  • John Doe
  • Null username
  • No username

Is it expected behavior for ui-sref="user" do nothing?

And if I go to a johndoe and then click on "Null username", on first click it loads default param value and on a second one, it does load actual null value. Not sure if I'm missing something, or it is some kind of issue.

@christopherthielen
Copy link
Contributor Author

Yeah that doesn't look like proper behavior. Null should be considered a value, while undefined should be considered "not a value so use default". I'll try to figure out why it's doing this a little later.

@christopherthielen
Copy link
Contributor Author

@DovydasNavickas ui-sref="user" is the same as $state.go('user', {}). However, if you're on a state and already have params set, then options.inherit: true will cause another .go() to re-use any existing params that weren't overridden. You can modify this by overriding the value (ui-sref="user({username: undefined})). This has been the case since long time ago (here's the test for it: https://github.com/angular-ui/ui-router/blob/master/test/stateSpec.js#L707) but it feels a little weird in this case with optional/default params.

The null case is as follows:

  • .go('user', { username: null })
  • string param Type does not handle nulls, so they are skipped in the url
  • successfully transitioned to 'user', { username: null }
  • url is updated to /user/
  • $locationChangeSuccess triggers $urlRouter.update()
  • /user/ is tested against routes and now matches user { username: undefined }
  • { username: undefined } causes defaults to be applied.

Then, when clicking the button again, the same process runs, but the URL doesn't doesn't change, so $locationChangeSuccess is never fired.

I'm not sure any of this is a bad thing. null can be used meaningfully for some other custom Type (as long as it encodes null to a string somehow). It can be used as the default value for a param, in order to define the param as optional. But in this case, you simply don't want to transition using { username: null }.

I updated my plunk with a fix to empty-string mapping. It's now mapped from an empty string (in the URL) to undefined, for optional and array parameters. Tell me your thoughts.

@stereokai
Copy link

@christopherthielen @NevilleS @DovydasNavickas

I can see now I haven't thought that through enough. Thank you guys!

However, I'd like to try give you an additional point of view for analysis of my proposed solution. Given the following configuration (notice I have added a "parameter name" to each state's URL):

userstate: { name: 'user', url: '/user/:username' }
gallerystate: { name: 'gallery', url: '/gallery/:galleryid' }
photostate: { name: 'photo', url: '/photo/:photoid' }

Loading $state.go("user.gallery.photo", { username: 'johndoe', galleryid: 28, photoid: 9191 j});, the full, non-squashed URL of the state user.gallery.photo would be: #/user/johndoe/gallery/28/photo/9191.

Compare this when accessing the default gallery for the "default" user (logged in user):
#/user/gallery/photo/9191.

What gets squashes are the parameters, while the real URL parts remain to reflect the "full URL" (which is something we are trying to avoid). This is one way using which it can be achieved, however, it might break the original purpose.

@JakobJingleheimer
Copy link
Contributor

I think Option E adds complexity whilst trying too hard to protect the dev from himself/herself. I would argue that the original pattern is flawed—all along it should've been:
/user[/:username]/gallery[/:galleryid]/photo/:photoid ([] = optional)
/user/jshado1/gallery/0/photo/a1b2c3 (nosquash)
/user/gallery/photo/a1b2c3 (squash all)

User is the section, Gallery is the view, and photoid is what should initially be displayed onload.

Option D is kind of growing on me. It's nice to see the placeholder:
/user[/:username]/gallery[/:galleryid]/photo/:photoid ([] = optional)
/user/jshado1/gallery/0/photo/a1b2c3 (nosquash)
/user/~/gallery/~/photo/a1b2c3 (collapse)

But I still think Option C should be the default. @christopherthielen rewriting the url is what I expected to happen (/user/gallery/photo/a1b2c3/user/jshado1/gallery/0/photo/a1b2c3). We use Prerender for SEO, so I would like to be able to add a listener for this rewrite to set the status code to be 302 Found to show that the former request is valid, but the latter is the canonical.

@stereokai
Copy link

Thanks for your feedback!

@christopherthielen
Copy link
Contributor Author

OK I have a functional demo running: http://plnkr.co/edit/VMPc8D7oUG0B1R3QuiCE?p=preview

@christopherthielen
Copy link
Contributor Author

I implemented option "D" and removed the explicit "value" option. Now, squash: can be an arbitrary string which represents the default value, i.e. squash: "~" to generate /user/~/gallery/~/photo/a2b2c3.

I also added some logic to perform pre-decode mapping. This allows us to specify the mapping behavior between the URL (String based) and $state.go (object based). The default map for optional parameters will replace the empty string "" with undefined and null with undefined. When decoded, this map to undefined will apply the default value. This should allow for consistent and specified behavior when mapping URLs to parameters.

http://plnkr.co/edit/VMPc8D7oUG0B1R3QuiCE?p=preview

@christopherthielen
Copy link
Contributor Author

Compare this when accessing the default gallery for the "default" user (logged in user):
#/user/gallery/photo/9191.

@stereokai what you ended up with is the same as option A, "slash"

@jshado1 the url is rewritten when using $state.transitionTo but is not rewritten upon a $locationChangeSuccess. This is due to the url-to-state synchronization mechanism.

sync direction 1

  • transitionTo -> activate state -> update URL (this is where we call state.url.format())

sync direction 2

  • $locationChangeSuccess -> match URL (state.url.exec(path, search)) to find correct state -> transitionTo(matchedstate, matchedparams, { location: false })

@ilanbiala
Copy link

@christopherthielen If 0.2.12 is already released, is this planned for 0.2.13 or 1.0.0?

@christopherthielen
Copy link
Contributor Author

@ilanbiala it's in there :)

@ilanbiala
Copy link

Ah okay, cool! Thanks for the great features.

@JakobJingleheimer
Copy link
Contributor

nice work @christopherthielen and such fast turn-around for option D! Thanks

@JakobJingleheimer
Copy link
Contributor

@jshado1 the url is rewritten when using $state.transitionTo but is not rewritten upon a $locationChangeSuccess. This is due to the url-to-state synchronization mechanism.

So… does that mean there's nothing to listen to? (if not, any chance for something like $stateFound or worst case, piggy-back on $stateChangeError?)

@christopherthielen
Copy link
Contributor Author

@jshado1 here is the code that, upon matching a url, transitions to the state that was matched. Note location: false.

    // Register the state in the global state list and with $urlRouter if necessary.
    if (!state[abstractKey] && state.url) {
      $urlRouterProvider.when(state.url, ['$match', '$stateParams', function ($match, $stateParams) {
        if ($state.$current.navigable != state || !equalForKeys($match, $stateParams)) {
          $state.transitionTo(state, $match, { location: false });
        }
      }]);
    }

You might try decorating transitionTo when pre-rendering and forcing location: true in the options.

@nitinju
Copy link

nitinju commented Apr 10, 2018

@christopherthielen doing squashing in 0.2.15.
1- url: "/:categoryItemTypeId/:searchHHC/:city",
2- url: "/:categoryItemTypeId/:searchDental/:city".
As can be seen second argument is different, But some how its hitting both URL (even hitting all URL which are having all optional param with length 3).
My understanding angularjs is taking both URL as single URL and hitting one which is define first in configuration. Is It possible to achieve above scenario without making params mandatory??

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

No branches or pull requests