-
Notifications
You must be signed in to change notification settings - Fork 3k
Simplify Redirects (Alias URLs?) #185
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
Comments
on simple redirects without concern for parameters... on whether to reduce the number of providers/services to one... on the complexity of supporting parameterized aliases... Right now, there is some interplay within $state(Provider) and $urlRouter(Provider) -- witness this code in the $stateProvider:
So $state leverages the functionality of $urlRouter to handle url-instigated transitions. $urlRouter ends up with a "when" rule that has a function which calls $state.transitionTo(). And here's how $urlRouterProvider assembles its rules:
So $urlRouter is bearing an array of rules that it examines one at a time when the $location changes. $state doesn't really care about the order in which states are defined, because in and of itself everything has specific name and to get to a state you specify the name in the transitionTo() function. $urlRouter, on the other hand, tests things one at a time to find a match. If I understand it correctly, this comment in the source mentions the possibility of down the road snipping the decision process once a particular pattern is disqualified.
That comment begins to introduce the problem (at least I think of it as a problem) of using an array to store the rules -- namely that you have no way of skipping rules that certainly won't match because you don't known how many indices in the array to advance to get to the next rule which has a possibility of matching. When I began to float the idea of editing states ex post facto, one of the complexities cited was (to paraphrase) "how do you manage the rules"... and given that $stateProvider farms out the url handling to $urlRouter and that its rules are in an array (but not a "disarray" ;) ), this was a completely valid challenge. I'm not going down the path of suggesting that ui-router support editing in this comment, so please bear with me... this is about aliases. I think I cracked a few nuts that relate to aliases/otherwise, and into merging the functionality of $urlRouter and $state in one service provider in $detour, and I'd be happy to help with the retirement of $urlRouter and the addition of aliases and the ...and I defer to the ui-router team as to whether that's the way to go with $state. I started to go into greater detail about how $detour works and realized I was getting ahead of myself. I guess my point is that aliases aren't that hard if you merge $urlRouter with $state, but I acknowledge that doing that merging is real work. |
The reason for keeping $urlRouter and $state separate is that
I don't think the complexities of alias support are unmanageable, or really related to $state and $urlRouter being separate. Essentially what will have to happen looks something like this in pseudo code:
I do wonder why you'd use lots of aliases in pratice though -- if you get asked to implement 'fancy' URLs, wouldn't you just change the primary URLs of the state to be fancy instead of adding aliases? |
"fancy" may be a misnomer... I'd call them "marketing" urls...usually simpler and smaller than the ones that make the app actually work. It's just a way to do a redirect when you get right down to it. The url does matter for relative paths in the html (as I suspected might be what's tripping up the bootstrap modal in #180), so you may not want to rearrange your hierarchy to support the marketing urls. |
Well... once @nateabele finishes the sref directive, you shouldn't really need to hard-code state URLs in HTML anymore. The state definition would then be the only place containing the actual URL pattern, so you'd be free to change it according to marketing whims ;-) |
I don't understand how @nateabele's changes are going to have an impact on the flyer the marketing team hands out at conferences or puts in a TV ad. There may be one or two links from the outside world into your app if you're lucky! I'm not sure it's time to retire url's altogether... when you have relative paths in your html to things that aren't states, the url hierarchy matters. Redirects haven't lost their usefulness... If the $urlRouter
...though I could be mistaken. |
If there's only 1 or 2 external URLs into the average app, I'm not quite sure why aliases are important then? In either case, I think $state should only handle URLs that either directly or indirectly (alias / redirect) refer to a state. Lumping everything together in one monolithic service is one of the main design mistakes of the existing $route service in my mind. |
$stateProvider.redirect(stateNameOrUrl, stateNameOrUrl); |
I don't really think alias is needed, because I think it promotes the use of redirects a bit too much. It indirectly tells the dev that every state could/should have some alias urls. Instead if we use a method like |
Maybe by using the term redirect I'm making things confusing. When I've said redirect I was referring to the aliad feature which would be within state definitions. I wasn't indicating that I perceive the need to a separate redirect feature to go between states. |
But you need to redirect from an existing state -- which defeats the |
Hm, one easy way to side-step the whole "combination of parent aliases" problem would be to require alias URLs to be absolute (which in most reasonable use-cases they should be anyway). So all $state needs to do is validate that the alias has all the parameters the state needs and set up the redirect. |
I'm 👎 on this. Each state should have one and only one canonical URL. If you want to have short URLs, create an empty state with only |
My $0.02... @nateabele, I'm glad you're keeping the topic alive, even if we seem to be on opposite sides of the thinking... :) Is it the feature's value the question (does it complicate the ui, is it going to be used, does it fit in the mission, how many lines of code and bugs saved in client apps, etc.)? Or the feature's cost? Or some combination of both? I ask because you're indicating that the user should go through some hoops to do this -- nothing too complicated, but work all the same -- and the alternative is that the library goes through those hoops. I don't see it as painting too far out of the lines to hope that you could deep link extra externally-shared links to your states. Why not do this right in the library, rather than try to document use of onEnter() and transitionTo() for that use case and then have people using a bunch of deviants of the sample? If done in the library, it's really not much more complicated than the "otherwise" redirect which should be in $state if $urlRouter isn't retired. If everyone's supposed to use $urlRouterProvider to deal in urls, then aren't a lot of people going to end up using both and wonder how they're related? If it's how the thing would be implemented -- of course all links within the app should be using hierarchical state names rather than URLs... those are the true CNs.... even the "url" isn't as canonical as the state name and its parameters. The "url" is is the second-level canon... and the one that ends up being current when you go to a state... but aliases are proven to be valuable all over the place (DNS, OOP, yadah, yadah). I see no reason why aliases in the sense we're talking about should be treated as workarounds or relegated to some deeper level of the tech stack, such as the web server, proxy, or load distributor. Seems to me supporting aliases lets you stop publicizing $urlRouter and have no need to keep it as part of the public api... which might help a little with adoption. |
@nateabele I agree that states should have one true canonical URL. However being able to easily set up a few redirects doesn't contradict that basic premise. And redirects should happen on the URL level; I strongly disagree with using 'dummy' / transition states that do another transition in onEnter. @stu-salsbury I don't quite understand what you dislike about $urlRouter being part of the public API. It's true that it's a lower-level API, but it's there for when people want to do lower-level things. In fact I think that blurring the distinction between state handling and routing by shoving things like .otherwise onto $state makes it harder for people to understand that conceptually a state and a route (aka urlRouter rule) are different things. |
This. |
Cool with me! I think states and routing are deeply enough entwined to manage them together as one service from an application's perspective. To me it feels like making two things out of one. Since detour is meant to be a way to let you define both urls and states with one api, perhaps I'm biased. I wouldn't want to require people to use an extra service and provider just to support otherwise, and it's a hop skip and jump from there to aliases, However, for ui-router, moving aliases/otherwise to $state is just an idea for how to make it easier for app developers -- if it's clearer for them if separate providers and services are exposed -- so that everyone understands their different functions -- then it's certainly worth the bytes to expose both separately. Usability is key. No worries! |
I'm more on @stu-salsbury side of this debate. It feels like states and routes belong together enough that adding sugar to stateProvider doesn't feel odd to me. I do think it will reduce confusion for newer users as I am still a new user and it makes sense to me. However I won't cry too much if I have to bust out urlRouter to do my URL based functions. |
Not to pile on because I've said enough, but I'm still feeling like something isn't clicking. The concept of two providers and services just seems ludicrous to me. I must not be getting it and curiosity is driving me forward. If $state relies on $urlRouter to do its url handling, then how separate are they? $state can't stand on its own without $urlRouter, and yet $urlRouter really isn't useful other than to do "otherwise" redirects to a "404-ish" state and to handle when redirects that could be better managed with aliases... and yet they should be seen as separate things? Every state with a url is leaning on $urlRouter. $state configures a $urlRouter "when" rule to get itself squared away with urls, and the $urlRouter relies on $state to clean up the urls when transitionTo is called, and it's hanging onto transitionTo functions in its when handlers.... they couldn't be more tightly linked without a circular dependency which is avoided due to the eventing mechanism of route changes. Advantages to combining:
Advantages to keeping them separate:
I must be missing something. As @timkindberg says, I won't cry if they're kept separate. If I don't understand the logic, no hard feelings either way. Is it at all possible that $urlRouter is separate today because of an early impulse to support something that resembles $routeProvider? If $routeProvider goes away, what good is left? This paragraph is sponsored by those who weren't around when this project first took flight... Either way, ui-router is a heckuvah great solution to routing and state management. Don't let my pushback convince you I'm not grateful for it. |
Yeah I'm on board with @stu-salsbury. This certainly isn't the most important thing we need, and I do see the concern of merging them too much that devs become confused or more importantly, don't learn to fully understand the power of each provider in its own right. So I think if we create an otherwise/redirect method that redirects to a state as opposed to a url, then its a totally legal addition to $state in the same vein as having a url property in the state config. So how about this? $stateProvider.redirect(url, stateName); Now this method is totally in "state" world. There could be no confusion here as to this being a url specific method. It redirects from a url to a state. |
But it can. You can wire up a whole state-driven application without ever using routing. This has nothing to do with footprint or API surface area. This has to do with architecture design decisions that are conceptually right vs. those that are conceptually wrong. For some background, I suggest you read up on the Single Responsibility Principle. |
@nateabele, curious if you think my idea for $stateProvider.redirect(url, stateName) would fall outside of the state responsibility. |
@timkindberg It would. You're asking |
So setting up a url is ok in the config, but dealing with them afterward in a method is not ok? If we really were splitting things out into true SRP objects, wouldn't we leave URL out of state completely and then have an additional service called something like URLStateConnector which simply connected urls to states? |
$stateProvider does not stand on its own. Every state with a url is creating a $urlRouterProvider when rule. You concern yourself with url's because they are the addresses to your whole system that the outside world can use to find and keep track of things. |
This doesn't address parameters and is unwieldy because it lives away from the state to which it applies. I was asked why I have something against urlRouter... it's because aliases in the state keep all of the code that relates to addressing that state in one place (with the exception of an otherwise/fallback state, which is inherently its own animal). |
I'd say SRP is fine and all, but at present each state's UrlMatcher gets lost in an array in a different service. It's called ui-router for a reason. I fold. |
$urlRouter is also there for people that want to have URLs that do other things that don't map to $state. Or somebody that doesn't like how $state works write their own version and still re-use $urlRouter, $resolve (about to commit that one in the next few days), $view (TBD), ... It's perfectly possible to use $state without $uR either if you don't reflect state in the URL, or you could write your own $location processing that ends up calling $state.transitionTo(). Lumping everything into one services makes all these things impossible. SRP isn't only about the use cases we can think of now, it's also about making it easier to reuse pieces of code in other use cases and contexts that we haven't thought of. That said, having a state.urlAliases property that is an array of absolute URLs and instructs $state to set up a $uRP.redirect to the primary URL of that state is simple and should cater for 95% of the simple cases where people don't understand why they need to use $uR separately. It also won't couple $state and $uR significantly tighter than they are now; it's the same kind of optional interaction as configured by state.url. |
Fine by me, as long as one of the aliases can be "" or "/". Sent from my iPad On Jun 18, 2013, at 6:00 PM, Karsten Sperling [email protected] wrote:
|
@timkindberg: gosh I hope you're kidding :) Long story -- not meant for github -- but I needed that. I agree $urlRouter provider's functionality is worth having access to, and I'd be psyched if aliases/otherwise (and @timkindberg's "/"<->"" feature (j/k: I imagine we all do it)) were accessible to the majority of apps that are just going to use the otherwise feature and never concern themselves with aliases. |
@hugsbrugs i updated it to work with new version.. give me some days, i think i will put it on github, but i don't have time to write tutorials :D http://naslovnice.si it's working example (but it's still in development, so maybe when u check it, it will not work ;) |
Thank you very much @vizo for your work and your link ! I can't get it working at the time writing : no errors but routes does not load content ... I will investigate further before asking you questions ! |
I've had a big problem with nested states. For example, app.locale.[state].[substate] were unavailable. We've fixed it by changing the following line in @vizo's code: For this one: As soon as I finish my routes config file, I'll post it in here. |
Hmm what is difference? so it creates totally new object ... tell me if it works for you too? |
I finally created https://github.com/vizo/angular-ui-router-i18n . So please let's move there for issues and anything else related. Thanks. |
The problem we faced with the simple extend is the urlMatcher.params prototype, this.params would come with an empty one. Missing functions such as $$equals |
In some cases I get a "undefined function" when I call $state.go by using this new line or even that old. It only works properly when I use that way I said before. Actually I'm trying to figure out the reason why. |
Now i see it, i wasn't using latest ui-router ... UrlMatcher is becoming larger and larger and it's becoming very hard to maintain. It would be so much easier if ui-router would support alias urls for same state. |
Hi guys,
No view is being loaded when I visit '/' route
Visiting '/home/ in this case results redirecting to '/'. It looks like the '/' route doest really work. Does anyone of you encounter this problem before ? |
anyone ? |
I have the same problem. I always go to $urlRouterProvider.otherwise when I try to not use the :locale prefix. But, if I change the state using $state.go, it works properly. I'm kinda stuck in this issue also @mkoczorowski. |
Yeah, so close.. Let us know if you managed to figure this on out @gerisztein |
So do you, @mkoczorowski |
@vizo I agree with you that multilingual apps are complicated with ui-router as it stands. I totally agree with you that 'It would be so much easier if ui-router would support alias urls for same state'. Your solution is possible (thankyou for your work), but it comes with several 'musts' before you can start to use it. I suppose that there are good reasons why there must only be one URL per state, but actually a cow is a cow whether you call it 'cow' in english or 'vache' in french. And the URL '/cow' and the URL '/vache' is the same URL for those who have to deal with SEO in multilingual apps, they both use exactly the same state. |
I tried your solution for hours, no go in my case. ui-sref worked nicely, but I couldn't get the view to show 'TypeError: state.ownParams.$$equals is not a function' kept appearing. Presumably some mistake of mine. I'll have to go back to my original, complicated solution for now. |
Yeah. I have the same achievement using ui-sref but when I try to access the page through the URL it is redirected to the otherwise state. I don't really know what to do, I've tried many different approaches but... |
Hi Lucas, Il 03/02/15 17:45, Lucas Gerisztein ha scritto:
|
I had another brilliant idea (hack I suppose). See the following code:
Since at the moment I have to create a state for every language URL I could put the language in front of the state name, such as 'it-chiantiClassico' and 'en-chiantiClassico' and then change the ui-sref on the fly when I click to change languages. ui-sref="{{currentLang}}-chiantiClassico" But ui-sref doesn't listen for changes so that can't work. |
Actually this is exactly the approach I'm working on. Tomorrow I'm going to work on a custom directive to emulate ui-sref behavior. Se riesco a finirlo te lo faccio sapere ;) |
Thanks Lucas for your reply. I suppose another idea along the same lines would be a state url with params: This would need only one url per state, so it might fit in with ui-router just as it is, but slashPlusLang is not particularly elegant, although it might to be the only way if there is a root lang which doesn't appear, |
Thanks for all your thoughts! I was also trying a lot of other approaches before i came to this, but it seems that all others are more complicated on the end. You will figure it out when your app gets bigger. |
@vizo any idea about the bug we are descibing above ? We would appreciate your help |
I've reached a simple solution to the multi-language problem which is suitable for my needs. It is not completely DRY: templateURLs are still repeated, but nested states are still possible. Ok, my site is not going to be complicated so I haven't tried this with more esoteric combinations, but it looks like I have stayed within normal ui-router semantics. There is an abstract state for each locale at the root, and in my example there are two locales, 'en' and 'it'. The root abstract states have urls like '/it' - or, in the case of english, a blank string '' - because on my site 'en' locale has no url prefix. Now in the urls I can put what I like for SEO, (see the chiantiClassico states)
Now, there's the problem of ui-sref. On a multi-lingual site it's basically unusable because it won't respond to an change of the language - e.g. from 'en.home' to 'it.home'. So, to solve the href problem I considered a couple of solutions.
Code for making the map when the language changes;
|
Hi! I'm using another method as I already have lodash running on my project I've decided to create the states dynamically using an object to set the templateurl, controllers and other settings for each state and the forIn function. I've reduced 200 lines in my code and I think it's kinda easy to maintain. I already have some configurations for the routes for each state and an object dynamically generated that shows me which locales are available to use. In addition to this, I have something like this: the object: var states = {
'home': {
'controller': 'HomeCtrl',
'templateUrl': 'home.html',
'params': {}
}
} the function _.forIn(states, function(prop, state) {
_.forIn(available, function(props, locale) {
$stateProvider.state(stateName, {
data: {
analytics: analytics,
title: title
},
params: prop.params,
resolve: resolve,
url: url,
controller: prop.controller,
templateUrl: prop.templateUrl,
});
});
}); It solved my problem. It's not the prettiest solution but works like a charm. |
any updates on this @vizo @gerisztein @mwarren2 ? |
See @ksperling comment in #174 (comment):
I like the idea of aliases, but I'd be okay with just adding a new redirect method to $stateProvider. Like this:
$stateProvider.redirect("/fancypants", stateNameOrURL)
If we did allow urlAlias then I'm not too worried about the various parent/child alias combinations. If that's what the user wants, they should get it.
This also brings up the point of too many providers, see #184
The text was updated successfully, but these errors were encountered: