angular / router

The Angular 1 Component Router
MIT License
665 stars 135 forks source link

use case: Routes without a Path #121

Open martinmcwhorter opened 9 years ago

martinmcwhorter commented 9 years ago

There is a use case where some pages, or states with uiRouter, should not have a path/url.

There is a valid story where we don't want a login page to be bookmarked or deep-linked to. This is easily implemented in uiRouter.

Does the new router support routes without a path?

0x-r4bbit commented 9 years ago

@martinmcwhorter So does that mean in ui-router you'd transition to a state without updating the browser's URL?

tobidelius commented 9 years ago

@PascalPrecht I think what he means is that the parent state is abstract with a path and the child is the root of the parent. @martinmcwhorter right?

martinmcwhorter commented 9 years ago

@PascalPrecht, Yes, I mean ui-router states do not need to have a url, so if you transition to a state without a url, the url will not be updated.

Some states do not need a URL, so the path does not change when we go to those states.

Another use case would also be a pageNotFound state. We don't want to show /pagenotfound in the browser URL. We want to show /some/page/the/user/entered

0x-r4bbit commented 9 years ago

@martinmcwhorter okay, so regarding the 404 page, I think this issue might be interesting for you: https://github.com/angular/router/issues/110

When having components that aren't mapped to any routes, that'd mean they also aren't reachable per deeplink. Still don't really see the use case for that.

A login form component that is instantiated without a corresponding route, for me is just a component. So I don't really see where a router would come into play here. I haven't used abstract states in ui-router, but maybe @martinmcwhorter you can elaborate a bit on your scenario.

My point is, isn't that what a router is for? Resolving routes to load components and navigate between them?

martinmcwhorter commented 9 years ago

@PascalPrecht I agree 100%, "[A] router is for.. [r]esolving routes to load components and navigate between them."

My only caveat is that a SPA is statefull (or can be) and does not have a requirement for components to be bound to paths. Serverside MVC and API frameworks should be stateless. Therefor any resource needs to have a unique URL. An SPA does not have the same restriction.

I am not sure the abstract states are part of this.

I think you are right about login not being such a great use case. Let me elaborate on a better use case: User Register Workflow State Machine

  1. We want to allow a user to Register, we want to limit bots by forcing a user to click signup within the angular app rather than have a '/register.
  2. Once the user has submitted the registration form, we want to bring them to the validate email form.
  3. When the user has validated the email address we want to collect payment details to allow them to make purchases.

This is a scaled down over simplistic workflow. At the success of each step we can progress through the workflow with $state.go(nextStep). The user can only go in 1 -> 2 -> 3 at any point -- only moving forward.

Having url paths does not break this pattern if we add additional logic to prevent the user from going back -- but in a statemachine workflow like this a path doesn't serve any purpose as it is not a location the user can navigate back to at any point.

All that said, I appreciate this may not be part of the scope of the new router. Defining pathless pages would be configuration heavy -- and that seems to be something the new router is trying to avoid.

0x-r4bbit commented 9 years ago

@martinmcwhorter thank you for providing a more detailed user scenario. I think I get the idea now. A checkout workflow would probably be the same kind of thing (depending on what you want, but you could also do it without updating paths/routes). And this is indeed something were ui-router really shines, since you can just say "hey, please transition to state X" no matter if there's a route configured or not.

This is currently not supported by the new router. I also haven't seen any plans on supporting such a feature yet (which doesn't mean it won't be). Maybe it makes even sense to think about a "state" component on it's own?

While I understand the use cases of transitioning to a state without path/route, I'm not entirely sure if this is the scope of the new router.

@btford would love to hear your thoughts/plans on this.

martinmcwhorter commented 9 years ago

After thinking about this a little more it seems something like could be convention based. For instance a child router could be lightly-configured to create pathless "routes" based on convention.

Ill do a little bit of poking around and see if this can be implemented (as a non-default option) without breaking the existing API in a way that fits in with the new paradigm of the new router.

martinmcwhorter commented 9 years ago

@PascalPrecht, @btford

I have worked up a proof of concept: https://github.com/martinmcwhorter/router/commit/6333558cf24c5a17f9edf880424a34abdd82d750

This does not break the existing karma and protractor tests. I have created a copy of the phoneKitten module, phoneKittenPathless: http://localhost:8000/examples/angular-1/phone-kitten-pathless/index.html#/phones

When you click on a phone you are brought to the details page, but the url does not change. The back button will not bring you to the previous page (this is a feature, literally), so a refresh will the bring you to the master view list.

Details

I have added a method to router: go(link: string, linkParams: any): Promise; This in many ways is a duplication of navigate(url: string): Promise; but instead changing components based on url, changes based on component name and params.

navigate() and go() should probably be consolidated into a single method. Developers shouldn't need to think about if they are going to a routed or pathless component.

In order to achieve this an attribute is dynamically added to the link: ng-link-params="{paramName: \"paramValue\"}". This seems a bit of a hack -- this should go away.

The component params are not being validated in pathless components. Components with paths can have params validated with the defined path: "/component/:someParam". We know that 'someParam' is a valid parameter. So for pathless components we would need a config:

$router.config([
    { path: '/' , redirectTo: '/phones'  },
    { path: '/phones', component: 'phoneList' }
    { component: 'phoneDetail',  params: ['phoneId'] }
  ]);

What doesn't Work Plenty. I am sure it will be very easy to break this. It is very much a proof of concept.

mbenadda commented 9 years ago

While I'm not entirely sure I agree with the stateful use case, I think @martinmcwhorter really stumbled upon an issue with the way navigate is currently implemented.

In an internationalized app for example, we absolutely need to be able to navigate to a route based on its name, and not its URL. I don't think consumers of this method throughout an app should have to worry about the way URLs are constructed.

The proposed go(link: string, linkParams: any) function exposes just the same API as the one in ui-router, which I think is a good thing, if only for migration purposes. I guess I'm not the only one to use that feature as the basis to implement internationalized URLs

Once you implement a method to get to components based on the route's name, you basically have an API to deal with pathless routes too.

As an app developer relying on ui-router today, the lack of this feature would really be a show-stopper to any migration effort.

martinmcwhorter commented 9 years ago

I plan on cleaning up this proof-of-concept into something that could potentially be merged into master.

I would like to merge go() and navigate() into a single directive that passes the link microsytax. Something like navigate(link: string). Example: navigate("user({id:4566})"). Then the router-link directive would call this for all routes, regardless if there is a path.

The config for the router will need to be extended to allow for pathless parameters. This may require a wrapper/adapter for RouteRecognizer that adds the additional configs options. I would be interested in @btford 's feedback on this regarding if RouteRecognizer is going to go away / or is an adapter/wrapper would be the correct approach to adding additional config options.

nareshbhatia commented 9 years ago

Another use case for routes without a path:

On a stock trading site, I have various tabs to see my portfolio, transactions, setting etc.

I go to the portfolio page using the route /portfolio.

This page has two panels, one showing a chart view and another showing a tabular view of my chart.

I am able to change the contents of the chart view and the tabular view independently by clicking some buttons in those views, e.g. a bar chart vs. a pie chart. Changing the views at this level should not change the URL path, but just the state of those views - bar chart replaced with pie chart etc.

Using the new router I can think of putting a router.config at the highest level to navigate through my tabs. But I don't know if I can add a nested router inside the portfolio tab to achieve the state changes for the chart and table views. I really don't need any routes here, just two views and a way to manage their states. Or is this a use case for creating two directives at this level - one for chart and another for table?

jeffwhelpley commented 9 years ago

My primary use case is when I have a sidebar which changes content based of user actions, not the URL. I want the URL to remain the same for SEO reasons. The primary content on the page is the same and if a user shares the URL I want them to share the URL for the main content on the page. The sidebar content is not as important and never needs deep linking. For example, I have a sidebar with related search results by default and then the user can click on a search box and start typing which changes the state of the right sidebar into user search mode and shows their search results. This doesn't affect the main content however.

The only other alternative I could think about besides having a state without a URL is to perhaps use URL params so that the sidebar state changes would modify the URL params but leave the main URL alone. This could maybe work and I will try out, but it would be nice to at least have the flexibility to have states without URLs.

evillemez commented 9 years ago

Just lending my voice to the need for pathless component routing. Here's a use case:

We have an app for administering online exams built w/ Angular and websockets. When an examinee connects there's a whole complicated security process that happens both for authenticating the connection as well as transmitting the exam content, and the user's state if it exists. Then, if they were previously in the exam, we know what their state was and restore them to their previous state, because we're always keeping track of that server-side.

Throughout the entire exam process the URL never changes, and this is on purpose. We actually don't want deep linking, it serves no purpose for us. There is an initial URL to the exam its self, but nothing else.

I realize this is a highly specific use-case - but assumptions by the router shouldn't be the limiting factor in enabling us to upgrade to Angular 2. A lot of devs won't be building "sites" in the traditional sense with Angular2, but native apps on desktop/mobile where browser urls/history aren't a relevant concern.

It really seems to me that path needs to be an optional property. Do transitions to components however you do them, and if there's an associated path config, then manage the browser url/history.

Birowsky commented 8 years ago

Why is the label use case? Does that mean there is still no viable usecase for this?? I hope it's something else, but if that's the situation, here's mine:

https://test.restaurantacasa.cat

  1. Click on a restaurant to open it (url updates)
  2. Click on an item to add it to the cart
  3. Click on the cart to open it

From now on, you are navigating through the purchase path without touching the url.

If we would change the url for every cart state, we would lose visual context coming from the background. Just ask the material guys of how important context is.

nmai commented 8 years ago

Is this still an issue? I currently have the same use case as @jeffwhelpley where I'd like to use the router for a sidebar component, but I don't want the URL to update based on sidebar state because it's an auxiliary view and not necessarily even related to the main content.

irissoftworks commented 8 years ago

+1 for this. I was wondering exactly the same here when looking at the component router. There are definitely many valid use cases where business might not want to have urls associated to states. To anyone asking or in doubt, yes we do change states without modifying the url, and this is supported by ui-router. Any news about this issue ?

andrewkhan1 commented 8 years ago

+1 for this. I have an built a 'REST data viewer' component, which is complex enough to be an app of it's own. This app should only be concerned with itself and not the outer application. That allows me to keep the component application agnostic and embed it throughout my projects.

If the component became concerned about the URL of the application it would no longer be application agnostic, and I wouldn't be able to easily embed it into any application.

0xphilipp commented 7 years ago

+1 Any news on this? How are you currently doing it?

lokesh06061994 commented 7 years ago

+1 Any updates on this issue?

timschwallie commented 6 years ago

use https://ui-router.github.io/ it should be able to handle this use case and others.