-
Notifications
You must be signed in to change notification settings - Fork 3k
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
discussion: editing states and routes #126
Comments
I personally feel that it fits into the mission of ui-router. In my mind the mission is "The ultimate routing solution". |
I think being able to update $urlRouter at runtime might be useful, but even there I'm not entirely convinced; it amounts to saying that certain URL patterns in your application only exist once something else has happened in the application. To take your CMS example, You could have a state for viewing a page with a URL like '/page/:name'. Creating another page within the application doesn't mean the new page needs it's own state, just like you don't need to write new code inside the CMS app just to render that one particular new page: The code for rendering a page is generic. In the same way a state is generic and does not need to be redefined when data inside the application changes: you define a state that can display (or edit, or comment on, or ...) ANY page, not just a particular page. A similar argument goes for changing template versions at runtime: If they can change at runtime, don't hard-code them into the state, but make templateUrl a function, or use a templateProvider. (Or do use a hard-coded templateUrl, and wrap a decorator around $templateFactory that knows how to find the latest version.) |
@ksperling, thanks for thinking about this. For changing template versions at runtime, you need to bust the cache if you're using templateUrl (by changing its URL, which is stored in the state definition....) If your customer creates a blog and they want it to appear in the root of their site, or anywhere that the provider of the CMS system has not preordained as the "place for blogs," then how is that done? Or do you tell them to hit the refresh button because of a limitation in the software? (customers don't like to hear stuff like this and don't look too kindly on technology providers who say it, even if it is the right answer sometimes). Are you really specifically thinking about urlRouter, or that and the state service as well? (EDIT: I just saw the explanation for templateUrl, I'll look into that. But are you saying that it's not all the critical for the router not to work with URLs that customers want because they don't happen to be known while the CMS system is being developed? That's not the kind of CMS I'm thinking about...) |
I hope that didn't sound harsh. I'm just looking for a router that doesn't |
Stu, you are 100% positive that you can't build the dynamic solution you need by using the methods that Karsten mentions and that already exist? His explanation about the dynamic pages route seemed pretty feasible for you. Why do you need to use templateURL, why not templateProvider? And URLs can be fairly complex regex to match a very dynamic route. Just want to make sure before we add something that may not be needed. Or perhaps you COULD do it with current features but that's not how you'd like to do it, or you have restrictions preventing you from doing it that way. |
As I said, I'm going to look at templateFactory, that may solve cache On the routing, just to be clear, I'm not looking for on-the-fly dynamic As I understand Karsten's explanation, it is that I could have have urls Given the sense that runtime changes are not in scope and may in fact be I can patch it to make it possible to use the vast majority of it. So far, I'm sure that if you guys like what you see and want to merge efforts, it |
@stu-salsbury for all dynamic content sharing the same template, you wan't to use parameters for sure, e.g. In my own blog I simply have the route: A CMS or a Wiki would present a need to support nesting as well, which is not directly supported (AFAIK)... but indirectly through custom rules... You don't wan't to create routes in a dictionary for all pages in a Wiki or CMS... especially not if these becomes to large... instead again.. you wan't to use parameters IMO... As for adding features, you could get around that with parameters as well, but that isn't as clean as if we could add those routes with their parameters as the feature gets added... Reason this is a different case, is that if you add A blog feature to a CMS, it is likely that it would require a different template, a different controller etc... However, posts within that blog would share controller and templates, so again, no additional routes... Not to mention, if we solve this with parameters, then well... we only really need one global route... So IMO, if you are defining multiple routes with the same templates and controllers, that is just giving the system more unnecessary work. Different versions of a template is a different matter, I still consider those cases to fall into "it's the same template" category. Personally, avoiding the F5 refresh required in this situation would be a nice-to-have, after all it will be a rare case... Most cases will be Deploy the CMS, configure features (adding blog etc.) and never look back... That is going to be the life of many such systems i believe... I have had Wiki's, Blogs etc. live years on years without ever touching their configurations beyond what I did in the beginning. That is not to say that I don't think it wouldn't be great... But I can't think if a scenario where it's more than "nice-to-have"... That is also why I think it's more important to add an approach, that allows this to be different that "static javascript", e.g. the ability to get a JSON object from the server and directly use that in the configuration, without having to weave that into a .js file or something. (and making sure that that is executed to completion before the service is allowed to be used). |
I'm curious about what you mean when you say nesting isn't directly supported. I imagine you're not contradicting the docs https://github.com/angular-ui/ui-router/wiki/Nested-States so what kind of nesting isn't supported? Now onto whether or not the systems benefit from defining routes at runtime...so when a user of the CMS at an enterprise that has purchased the CMS system defines a blog feature and wants it to appear at How does the acme-today state appear the user who is defining the blog feature? Sheesh, I feel like people are trying to teach me web 101 here....perhaps because I used the example of a blog. I'm not working on developing a little angular-enable blogging thingy. I'm working on a more general purpose CMS to be suitable for use as the basis of enterprise-wide websites, so I'm going to sleep at night thinking about how best to allow arbitrary components to live in and communicate with the site at large, not whether or not users should be able to access a feature without refreshing their browser. If it was hard to make the route available dynamically, I'd give up and just say, 'well, customers will be told to refresh their browser at this stage of the workflow, and site owners will tell their visitors to refresh their page when a new sub-feature is available". But this isn't rocket science so why not do it without having to document In a non-AJAX world navigation gets updated when you go from page to page and this issue doesn't present itself. In an AJAX world, that's not the case. In AJAX, if the route to the feature isn't added at runtime, then you have to "re-install" the website by refreshing it. Granted, that's the easiest reinstallation process known to man, but it's still something that requires a communique, and so you can't publish anything that contains links to the feature until you've cajolled your internal and external users to get a new copy of the routing instructions. Put simply, just because all of this pushback is making me wonder whether I might be crazy... A (young) enterprise has a single-page website, the pages being named Page1. It's a great page and many people visit it many times per day. Being a saavy enterprise, they set the cache header for the page's template to expire in 2 years so that load/traffic is lower and users have a snappier experience when visiting Page1. Now the enterprise defines a new feature for their site, let's call it FeatureX. They want to put a link to it on Page1. a) Page1's template needs to change because it now needs to show a link to FeatureX. My impression is that you can cache templates for two years if you load them as html files. My impression is that you can use URL parameters to force new versions to be associated with those templates. As I said, maybe templateCache already does this, in which case, great. But the real issue is that you can't publish the link to FeatureX on Page1 until you update the routing (in all browsers and phone apps around the world) to know the route to FeatureX. Page1 is so popular that getting everyone to refresh their browsers or stop visiting the site for two years isn't practical. What's the solution? Mine is to both on-demand and periodically refresh the routing and navigation services. In the periodic case, which would happen on a configurable basis but might be every 5 minutes, 999 times out of a 1000 it's a cheap noop AJAX call that says, "no routing changes, no navigational UI changes". If it's done on-demand (as in demanded by some other part of the app), then it's probably not a noop because the user whose browser demands it is the one who cause the new route and/or navigational UI declaration to be necessary. If I'm missing something obvious then you all can enjoy knowing that I'll be slapping myself in San Diego :) edit 1: re-posted this comment because gmail and Github conspired against me in the formatting of the comment. I also didn't see some of the @jeme's comment, I either missed it or it was edited... in any case, I think maybe you're getting what I'm saying... but if you imagine this as something more than a personal blogging system you might reconsider whether a hundred lines of code are worth it. Essentially, I'm talking about getting a JSON object as you describe, plus being able to merge it into a running configuration. I should be able to get the first copy of the JSON up front and wire up the app in the callback so that deep linking works. edit 2: if Page1 is republished it will come with a routing timestamp that is newer than the one the browser has, therefore indicating to the browser that it should on-demand update the routing rather than waiting for the periodic update, thus when the user gets the new version of Page1 that has a link to FeatureX, the browser will know about the route. |
A couple of notes while I'm working on this:
I'm going to put up a placeholder repository soon so that this project doesn't need to be polluted with issues that are nice-to-haves for most projects and issues that are specific to what I'm building. Can we continue to discuss the possibilities of integrating and/or how the ui-router might be able to support this effort here? |
https://github.com/afterglowtech/angular-roadwork I welcome any and all to participate in discussion/raise issues/contribute/whatever. roadwork was going to be its own animal with or without the need to patch ui-router, so please don't think I'm taking my toys and leaving. I have a vested interest in ui-router and hope to collaborate! |
Obviously the nesting of pages within pages can go on for a Wiki, this isn't supported out of the box in terms of defining parameters etc, instead you need to resort creating a rule based on regular expressions that matches everything after "pages" even /'s... (Unless support for that has been added in some way.)
First of, I actually stated this as an area where it makes sense, but it is still nice to have... Chances are that your users won't stay on your website indefenetly anyways, so sooner or later they will reload the browser. And is it really crucial that they see the feature 5ms after it's added anyways? Besides, adding a feature that causes the routing of the user adding the feature to get reinitialized won't by it self fix it for the other 100 users being connected, it will only affect the one. Now there is a number of solutions to that, although in the end, a fallback will be polling or simply update the routes for each navigation. And a CMS would be an bad example in this case to be honest. Here an all-around solution that would use web-sockets if support and have a fallback if not would be great.
The problem you describe isn't really new to the business of web, and the problem doesn't get introduced with AJAX or angular, nor this routing solution. Even when you years back depended on a single JavaScript file, that could be cached if you told it could, so how do we solve that issue? This is actually very basic, instead of depending on But assuming your browser will treat the html files as any other files, you will need to do this anyways, because even if the user shuts down the system in between, he will still have the file cached, and have to actively flush that cache to get the new template if you don't add that bit of version information at the end.
That is caching definition on github for ya, every time I am in the issue list, go to an issue and tab back, i get an days old list of issues... And I do think I am getting what you wan't to do, and I don't think the overall capability should be left out, I think it's a neat idea... But I don't see it as a deal breaker, I do see it as a nice-to-have feature... Added to that, I really don't think it will have that big of an impact... |
I'm not deluding myself into any website that I publish will be so popular :) If it's a company's internal website they'll be on it a bunch.
No need for a socket, 5ms isn't going to kill anybody. I'm talking about minutes, not milliseconds. And it will indeed fix it for the 100 other users after the app refreshes its routes (perhaps every couple of minutes, configurable).
I'm confused -- you publish a new feature to the company's portal. Do you send out an email to tell everyone to refresh the portal and/or clear the cache in their browsers? It is very much related to caching and AJAX... in a non-AJAX world the user gets the navigation UI updates the next time they go to an expired page in the browser (and there is no such thing as client-side routing). You ensure that pages which need to be current do expire immediately and you're done. AJAX opens the possibility and challenge of more finely grained expirations. You don't need to expire or refresh your app root (index.html) because you haven't changed it. You can/need to change the routing one way or the other, though, unless your new features are delivered through a parameterized route. Waiting for cached files to expire is fine if cache time is short or non-existent. Refreshing browsers is another, but XHR can require clearing the cache.
With normal requests, they're supposed to honor the expiration header -- at the very least they should re-fetch if the header states that the content expires immediately. If they get it more often, then, well, there's not much that one can do. With XHR, cache control seems to be a grayer-area, but it seems that in most if not all cases changing a URL parameter on the file will force it to be refetched.
Well, some of your comments about caching and that there is no difference in an AJAX world make me think I'm not communicating the issue or imagining it, but more likely it's that you see it as a nice-to-have, as you state... which is just a matter of degree. This type of dynamism is one feature of what I'm trying to build, and I think when packaged with data-driven navigational UIs it will be a useful feature... but I'm not asking you to value it highly or at all, and I recognize that it may only be valuable in the context of the larger system, and that such a system is not universally needed or wanted. I'm not trying to solve war and poverty here, even though I hope that this feature helps to enable the overall value proposition of what I'm developing :) When you get right down to it, all of this is not something I'm asking ui-router to do... just to support editing the routes, which isn't such a big deal. Small enough that I'm not all that concerned about the patching -- unless the code goes in a new direction that makes it more difficult. |
@stu-salsbury The challenges existed before AJAX, but no one really used caching back then so they were unaware. A HTTP Request made through AJAX isn't that much different that a regular one. Except it is harder to control it from a user perspective (I think)... Imagine an old web application that has a script that does allot of fancy stuff on a website, like show and hide tabs etc... They load it with: Obviously, this has an easy fix both from the source and client perspective, the source can point to
I would NEVER cache the root file for a long time, that is your point of entry, if you cache that and change it down the road, then you will have to send out your mail... Instead use the ETag/Last-modified to reinstate it if it hasn't changed after it has gone invalid... Also I would never cache API calls, that doesn't make sense to me, so if you end up using a JSON object to generate your routing which you get from a service, then it shouldn't matter. Optionally cache it shortly and again use the ETag/Last-modified header. (to use the built in cache control rather than inventing your own). Basically, use this for all resources that may change, and which you can't force load with a version query.
Nope, I wait patiently... Because I doubt that they will keep a single browser window open on our company website for more than a day... Which would mean they would discover the new feature a day after it was added (I can live with that) or get an unwanted 404 from our system if we removed a feature. Again... that is ok because if they saved the link generating that 404, they could end up there anyway. How would they discover it, well due to having replaced the resources that I needed to, if it's a more static example, it would be the main application script as in the previous example. In your example, it could either be at the top, pointing to a new version of Obviously, that won't help for as long as the application is "live"... but that brings me back to that I don't expect users to have a browser window open on a single site for years... They will navigate away from it, by accident or will, or they will close the browser all together... So i am not so fearful for the situation. And if something behaves oddly on a page, users tend to hit refresh... (after all it may just have been a request that didn't succeed, can't shield against that) |
So given the response from the team, it seems like the ability to update the routing table isn't part of the mission of ui-router. I accept that. Thanks for taking the time to hash it through with me. My modifications to ui-router that enable it will probably ring in at about 20-30 lines of code when finished. Given the response from the team, I'll move forward under the assumption that these features are not within the scope of the vision for ui-router. The good news is that we can stop debating how caching works and whether or not 404s and "refresh your browser" trouble tickets with technical support, QA and Corporate Communications teams are ok! :) |
@stu-salsbury I'm not sure if I'm missing something in the use case you're trying to handle "cleanly", but I think you can overcome your major objection, namely needing to tell the user to press F5, simply by making the app do it itself. In your ideal scenario you would need to devise some bus to ferry notifications from the server to the clients about the change in routing/availability of features even if ui-router supported changing routes at runtime. So, use that same notification mechanism to instead trigger a reload of your app (at a time when it won't disturb your user) and you won't have those helpdesk tickets piling up! I've actually seen a long-runnning non-Angular single-page app that will occasionally inform the user that they need to "update", which then results in a simple reload of the page... and you promptly keep working. The reload itself, I think, is a non-issue to the user, and can be implemented as long as you set sane expiries on things like your index file and give yourself a mechanism to trigger it. My thought on this, however, is that you're spending a lot of effort on handling an extreme edge case. As others have said, the user who leaves the same browser tab open for days without ever navigating or doing anything that'd cause a page refresh is quite rare. The time you're devoting to this may be better spent making sure you don't have memory leaks or other problems faced by long-running in-browser apps, if long-running is indeed a high-value use case for you. (If you are doing those things and attempting to contribute this feature nonetheless, please be assured I mean no disrespect by the suggestion above.) "It is so awesome that it never needs to reload" just isn't a feature your enterprise client is specifically requesting; it is a nice-to-have achievement. Is there a functional shortcoming of the app-initiated reload approach? That said, dynamic routing in general does seem to me to be useful, at first glance, if only to allow defining routes based on the result of some asynchronous call (i.e.: after the config phase of your module). That is, if your requirements are for your ng app to run unmodified and for it to add routes to components based on configuration data retrieved from some server data source. The argument that there are work-arounds doesn't negate the usefulness of the feature, so discussion shouldn't really end on that point. However, on the other hand, the caveats raised in your initial pull request's discussion do remain--opening the door to magically (dis)appearing routes will likely lead to many more poorly-designed apps than the number of apps it will help achieve awesomeness. However, it isn't the role of any framework to preemptively be condescending to developers who use it, so this isn't a good reason to close the door on the dynamic routing feature. Getting back to whether this is a feature to pursue in ui-router (specifically, not dynamic routing but state-machine re-init capability), shouldn't the test be whether the feature's benefit (vs. a potentially cumbersome or flawed work-around) outweighs its cost in processing time and as dead-weight in those situations where it will go unused? This is a judgement call to be made by the powers that be. For my two cents', I'd lean towards a slightly conservative approach and instead put dynamic routing on a low-priority wish-list, only adding it if there were a truly elegant solution proposed that's light and has minimal cost. |
@azarzycki I agree, I think the complexity is quite high and most users of ui-router probably won't care about dynamically changing either states or routes. I'm also intending to factor out some things that are currently baked into $state into their own services (specifically the 'resolve' mechanism and the interface with ui-view), so there should be a lot of components that can be reused by somebody who wants to build a fully dynamic router. One use case that I can think of for "late" state configuration (in the sense of adding more states, rather than changing / replacing existing ones) would be a truly huge application that's split into dynamically loaded modules. However even in that case one could probably get away with loading all routes and states up front, and only loading controllers and templates dynamically. |
Thanks guys. It's clearly out of scope for ui-router. I'm going to try to develop a router that supports lazy-loading states/route and getting definitions/updates from the server at runtime. When the server knows what routes are available, I'd like it to be able to tell the client (at the time those routes become available). Interpersonal workflow and collaboration is a big deal in my plan -- not looking for any "isn't it awesome that I don't need to reload," just looking for people to be able to say, "hey check out "http://xyz.com/thisNewFeature" and not have their coworker get a 404 (or an older version of the template associated with it) -- hopefully they won't even think about whether or not it's neat that they don't need to refresh to all be "on the same page." I understand now that it's not one of ui-rotuer's goals, so I won't bug you about it anymore. I've started refactoring the state provider to make it relatively efficient to edit states and to have it own the rules that relate to its urls so that it can manage them, too. If a route is not found, when normally the fallback/otherwise route would immediately kick in, a round-trip to the server will test whether or not there is a newly available route (or a lazy one) that satisfies the URL. Deletes and updates can be requested for inclusion with that request, and client apps will be able to instigate updates/deletes manually at any time they choose. At least that's the plan as of now. |
@stu-salsbury, correct me if I'm wrong, but wouldn't any link that a user clicks, whether that be in an email or through IM, open in a new window/tab? The server will be asked for "http://xyz.com/previouslyNonExistentURI" and it will reply with your app, therefore the refresh will in effect already have happened by the time the app is loaded. That is, unless your app also includes an IM client, email client, etc. where the link being clicked on is actually processed by the running instance of your app. If this is the case then you're left to either make a dynamic router or force a reload in your otherwise() handler after verifying with the DB that the requested URI is indeed new. Assuming your app is resistant to reloads (as I suspect) there should be little interrpution to the user's workflow, certainly no server-404 or app-route-not-found. As you previously said, there already is a workable way to ensure your template is up-to-date by using a factory, so that's not a related issue. Point being that your needs as you state them can be met with the existing framework. The use case of "hey check this out < link >" is handled. Only the implied requirement of "must not reload" isn't. I like @ksperling's approach of factoring components out of $state. It sounds like the most prudent use of everyone's development effort and will result in an even more flexible set of building blocks. Perhaps you should coordinate your efforts, and you can provide the reference implementation of a fully dynamic router based on the separated components? That way we all benefit. |
Yes, I'm planning to do a lazy-load attempt for unhandled urls prior to falling back to the otherwise handler. I guess I wasn't clear -- in the abstract, anytime a link shows up on the site to a route that was not defined when the app was loaded, then the feature I'm describing will prevent a 404, both through lazy-loading and through (optional) proactive fetching of updates/deletes/additions. Examples:
Yadah, yadah, yadah. Since the routes are server-defined in the architecture, it makes sense to be able to define them through AJAX/JSON rather than Javascript. If you think of everything (pages, templates, data, routes) as entities in a database or derived from the database, and you imagine a disconnected team collaborating on the things in the database, then hopefully you can envision that it would be quite useful to be able to put things in the database (with the appropriate metadata) and have them become navigatable-to. Getting the routing to cooperate is step one. (Having it automatically show up in the navigational UI is step two and is most certainly not the job of the router.) I didn't realize the feature would be so negatively received, otherwise I would have either spent more time explaining use cases or not bothered! I'd hoped to build this feature into ui-router, but it looks like our interests are in different places... and that's ok! :) I'll keep an eye on refactoring in ui-router as it happens. It sounds orthogonal to what I'm discussing (breaking out resolve and directive code), but I'll hope to leverage some or all of it, just as ui-router might grab some of what I do if it works out. |
I'm closing this as an issue but we can keep talking about it if anyone wants. In preparation for being able to merge updates from JSON, I'm presently working on a StatesTree (the evolution of the states object) that can manage itself. It doesn't feel too complex, yet -- thought it's also not working, yet! :) I'll post something here when/if there's something to see in case anyone wants to peek. |
Let's discuss the possibility and challenges of being able to edit states and routes. I think it would be a great feature.
Please note: previous conversations have included talk of putting functions of the ui-router providers into the ui-router services. This is not/is no longer what I'm proposing due to concerns about accidental misuse.
Some use cases include:
What would need to happen?
I'm not the expert here, but from what I've seen there a few challenges are immediately apparent:
It's unlikely that these are the only challenges, but they're a start.
Of course, there are also questions as to whether its useful and/or safe and/or part of the mission of ui-router to do this.
Would it be useful? Well, I it would to me and I think others -- as long as website/webapp owners care about their URLs and want their techies or tech suppliers to give them/sell them dynamic, multiuser websites that are easy to use and are manageable by people who are not programmers.
Would it be safe? Well, it's easy enough to misconfigure your routing table at configuration time.
Is it part of the mission of ui router or can it fit into the mission? Not my question to answer, but I hope so! The universe can only handle so many routers in the angular ecosphere. It would be nice to concentrate the community's efforts on making one that can serve all comers.
I guess it mostly boils down to treating the states/routes as living things as opposed to a set of rules that are defined once and then left alone. Most apps can just set them and forget them, and those apps could continue to do so... not a line of app code would need to be changed.
I'd be happy to work on making states/routes editable within the scope of ui-router if the feature is welcome -- I hope so. But if @ksperling and others think this really doesn't fit the mission, then I will gracefully accept it and do it in a fork or something.
In either case, I hope this can be chewed on as a healthy discussion here. It was ui-router that convinced me to move to Angular, if that's any indication of how much I value it.
The text was updated successfully, but these errors were encountered: