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

Proposal: Routing Guide (Work In Progress) #122

Open
justinbmeyer opened this Issue Nov 2, 2017 · 3 comments

Comments

Projects
None yet
3 participants
@justinbmeyer
Contributor

justinbmeyer commented Nov 2, 2017

tldr; We propose a guide that teaches how to setup can-route to handle common use cases. We will survey common problems and design a guide or a series of small examples addressing them.

This was discussed on a recent live stream (35:49)

Problems

The following lists different problems we would like to see addressed:

  • Values with delimiters (ex: / )
  • Conflicting routes
  • Nested routing
  • Testing Routing Rules
  • Preventing history changes
  • Mounting pushState
  • Alternate routing (turning off routing, 404s, etc)
  • Switching between "page components" using convention
  • Trailing slash routing
  • Validating routes
  • Using can.route.data vs passing down your data.
  • AppVM data that shouldn't be on the route.

Please let us know in the comments below which are most important to you or others you would like to see.

Values with delimiters (ex: /)

Sometimes, people want values with / to show up as / in the url.

What you want:

route("{page}/{id}")
route.param({page: "edit", id:"c2/a1"}) //-> "edit/c2%2Fa1" NOT "edit/c2/a1"

This is also related to nested routing where you would want something like:

route.deparam("folder1/folder2/folder3/fileA/meta") //-> {path: "folder1/folder2/folder3/fileA", action: "meta"}

Conflicted routes:

Sometimes one routes rules might "swallow" another routing rule. For example:

route("{page}/{id}");
route("items/{itemId}");

route.deparam("items/5") -> {page: "items", id: "5", route: "{page}/{id}"}

I've seen the following rules also desired:

top -> {category: "all", sort: "top"}
sports -> {category: "sports", sort: "top"}
sports/latest -> {category: "sports", sort: "latest"}

The guide should cover how to handle these situations.

Nested routing and/or composite routing

Is there a good pattern to take multiple "little" apps, each with their own routing rules, and compose them?

var loginRules = [
  "{user}/{action}"
];
var recipeRules = [
  "{recipe}/{id}"
]

route("login/{user}/{action}");
route("recipe/{recipe}/{id}");

Testing Routing Rules

Lets detail how you can test your routing rules to make sure they work.

// routes.js
import route from "can-route";
route( ... )
route( ... )

// test.js 
import route from "can-route";
import "./routes"

assert.deepEqual( route.deparam("foo/bar"), {page: "foo", action: "bar"})
assert.deepEqual( route.param({page: "foo", action: "bar"}, "foo/bar" );

Preventing History Changes

With pushstate, it's possible to prevent a history entry when someone navigates to recipe/5/view to recipe/6/edit. Using replaceStateOn, replaceStateOnce, replaceStateOff. We should explain how.

Mounting pushState

can-route-pushstate defaults its mounting to /. It's possible to change this to /where/my/app/belongs. So only routes within /where/my/app/belongs will use pushState. Lets show how to do this.

Alternate Routing Scenarios

There are a few scenarios that would be good to demonstrate:

  • When the application is loading with the url /user/secrets, but the user is not logged in, we might want to show them the login page, but keep the url /user/secrets.
  • If the user goes to a page that is unavailable or broken, how do we send them a 404?
  • How do we let people go to a page that would normally be pushState-d? For example, a route like {page}, but we want the special checkout page to be loaded from the server.

Switching between "page components" using convention

Its very common to have some sort of convention to align a page property to similarly named component. For example, Bitballs does this. We should show how to do this. Bonus points if we can also show how to animate it.

Trailing slash routing

Often folks want /foo and /foo/ to route to the same page. Lets show how to do that.

Validating routes

If a user is on /user/2 and wants to go to /user/3/secrets with:

route.data.update({
  page: "user",
  id: 3,
  action: "secrets"
})

How can you check that this is an ok page transition?

Using can.route.data vs passing down your data

For connivence, many components use the global route.data to interact with the route.

For example:

VM = {
  saveAndReturnToView(){
    this.data.save().then(() => {
      route.data.page = "view";
    })
  }
}

Alternatively, the route's data can be passed to the component:

<my-component page:bind="appViewModel.page"/>
VM = {
  saveAndReturnToView(){
    this.data.save().then(() => {
      this.page = "view";
    })
  }
}

Which pattern should you use and why? If you do use the first pattern, how can you test it without breaking your tests. How can you make demo pages?

Preventing AppVM data from being on your route

When connecting a VM to the route, it's common for the VM to have properties that should not be serialized for the route.

DefineMap.extend({
  someData: {serialize: false} //-> will not be sent to the url
})

@justinbmeyer justinbmeyer referenced this issue Nov 2, 2017

Closed

Epoch 1 Survey Questions #77

24 of 24 tasks complete
@bgando

This comment has been minimized.

Contributor

bgando commented Nov 3, 2017

Thoughts:

  1. Is there a recommended way to implement a back button?
  2. When I use update() with two variables, I expect #!/page/section but I get #!page/section. How do I add a slash in there?
  3. The if/else logic tree that is in bitcentive for routing makes it really easy to have bugs so tips on how to avoid that would be useful.
  4. Is there a good way to test can-route? What are some strategies?
@justinbmeyer

This comment has been minimized.

Contributor

justinbmeyer commented Nov 3, 2017

@bgando

Is there a recommended way to implement a back button?

Does history.back() work? https://developer.mozilla.org/en-US/docs/Web/API/Window/history

When I use update() with two variables, I expect #!/page/section but I get #!page/section. How do I add a slash in there?

Can you provide an example?

The if/else logic tree that is in bitcentive

Do you mean this if/else logic? Or you might mean the bitballs if/else logic which is even more complex.

What kind of bugs are you having?

So the thing to know about CanJS's routing is that it's usually like:

URL -> State 
State + Session Info -> Component Name

The URL gets translated to some stateful representation. Then, the if/else logic usually takes that information and combines it with the current session to figure out which page should be drawn.

We might be able to make this a bit more declaratively, or w/ something like Streamy VMs make it a bit easier to test. But I'm not sure this logic can go away and still maintain the STATE -> URL ability of can-route.

One idea might be to have a convention like:

{
  'games/{gameId}': "game-details",
  'tournaments/{tournamentId}': "tournament-details"
}

But it still would be tricky in include the session in this convention.

Is there a good way to test can-route?

I show some up above. Thoughts about those?

@justinbmeyer

This comment has been minimized.

Contributor

justinbmeyer commented Mar 29, 2018

Update route with state from outside the route.

I'm not sure how to summarize this, but @mjstahl was trying to keep the URL consistent with what page the user was on given their session state.

For example, if a user went to /purchase, but was not logged in, he wanted the url to say /login. Then it would be able to replaceState back to /purchase.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment