Skip to content
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

catch urls like /team/:teamID and /team/:teamID* (wildcard) #15

Closed
frederikhors opened this issue Jun 4, 2019 · 14 comments
Closed

catch urls like /team/:teamID and /team/:teamID* (wildcard) #15

frederikhors opened this issue Jun 4, 2019 · 14 comments

Comments

@frederikhors
Copy link

frederikhors commented Jun 4, 2019

Coming from this example: https://codesandbox.io/s/reverent-tesla-c0kq0 I would like to understnad if this is possible in Navaid now:

const router = Navaid()
    .on("/", () => draw(PageHome))
    .on("/teams", () => draw(PageTeamList))
    .on("/team/:teamID/*", obj => draw(Team, obj))
    .listen();
  • using .on("/team/:teamID/*", obj => draw(Team, obj)) doesn't catch urls like "/team/1"
  • using .on("/team/*", obj => draw(Team, obj)) catch urls like "/team/1" but as params in Team component I need the :teamID parameter which is wild in this case; the problem is if the URL is eg. like "/team/1/2/56/8" wild is 1/2/56/8 which is a wrong var in my Team component code.

UPDATE:

I need also something like this to catch: /team/1/shirt/5.

Is there a way to catch both using something like /team/:teamID* where * here is a wildcard after :teamID?

I hope I was clear. :)

@lukeed
Copy link
Owner

lukeed commented Jun 4, 2019

You can use team/:teamID?, yes. And I believe it should be fine too with a wild card to follow that optional param.

However, you should be aware that /team/:teamID?/* is effectively the same thing as team/* so it might run on more matches than you anticipate

@frederikhors
Copy link
Author

Sorry I forgot I need also something like this to catch: /team/1/shirt/5.

So i can't use team/:teamID? nor /team/:teamID?/*.

@frederikhors
Copy link
Author

frederikhors commented Jun 4, 2019

For now I'm using this code:

let teamID = params.wild.indexOf("/");
if (teamID === -1) teamID = params.wild;
else teamID = params.wild.substr(0, teamID);

@lukeed
Copy link
Owner

lukeed commented Jun 4, 2019

Well, those are separate routes. By definition

If you choose to be doing some nesting or whatever instead, that's your choice. As mentioned before Navaid isn't meant for nesting behavior since that's a view concern.

You could do something like this though:

sub = (
  navaid()
    .on('/shirts/:shirtID', ...)
    // Others
)

main.on('/team/:teamID?/*', obj => {
  let { wild, teamID } = obj;
  
  // Wild has remainder
  //  which can be routed against
  sub.run(wild);
})

But I have no idea what your full sitemap is. If that's all there is, then I'd do team/:teamID?/:shirtID? but that limits you from having non-shirt subpaths.

@frederikhors
Copy link
Author

You genius!

team/:teamID?/:shirtID? works amazing!

What do you mean with: but that limits you from having non-shirt subpaths?

@lukeed
Copy link
Owner

lukeed commented Jun 4, 2019

Haha. It means that if you ever wanted to add team shorts your URL structure will have to change.

/team/1/2 is not as flexible as /team/1/shirts/1

@frederikhors
Copy link
Author

frederikhors commented Jun 4, 2019

Oh. I'm still trying to understand what you mean.

Do you mean if I have an URL like: /team/1/2/3/4/5 is better (more flexible) than an URL like /team/1/shirt/2/goal/3/moment/4?

And why? Because I can't use optional parameters with the latter?

@lukeed
Copy link
Owner

lukeed commented Jun 4, 2019

The other way around.

/team/1/2 will always be shirt=2

So if you wanted to add pants or shorts down the road, that's going to be a problem.

/team/1/2 will still always be a shirt, not shorts or pants.

Even if you do /team/1/pants/3, it will still parse shirtID = pants

It's a long way to say that you've reserved the 2nd path segment after "/team" to always be a shirt ID

@frederikhors
Copy link
Author

Ok, ok. I now. Thank Luke. God bless you.

@ravilution
Copy link

Well, those are separate routes. By definition

If you choose to be doing some nesting or whatever instead, that's your choice. As mentioned before Navaid isn't meant for nesting behavior since that's a view concern.

You could do something like this though:

sub = (
  navaid()
    .on('/shirts/:shirtID', ...)
    // Others
)

main.on('/team/:teamID?/*', obj => {
  let { wild, teamID } = obj;
  
  // Wild has remainder
  //  which can be routed against
  sub.run(wild);
})

But I have no idea what your full sitemap is. If that's all there is, then I'd do team/:teamID?/:shirtID? but that limits you from having non-shirt subpaths.

@lukeed Firstly I love your demo. It is very very very helpful. I have a nested route situation. Not uncommon. Let me know your thoughts based on your answer above. I use Svelte. My page has two components. In the top I have parent component and in the bottom I have child1 or child2 component. When the user goes from /parent/child1 to /parent/child2, I don't want the top parent component to refresh. I only want the bottom child component to change. I have done this in the past with react-router with nested routes. How do I do it in navaid with svelte?

 navaid()
  .on('/parent/child1' => console.log('parent+child1'))
  .on('/parent/child2' => console.log('parent+child2'))

@lukeed
Copy link
Owner

lukeed commented Apr 20, 2020

@ravilution thank you :)
I think #10 (comment) is along the lines of what you'd want. Parent would contain its own Navaid instance, which has paths for the children. This Parent would sync listen/unlisten according to its own lifecycle.

The only missing part is that your Parent would have to define / control where Children get rendered. This is the same as App in my demo

@ravilution
Copy link

@lukeed Thank you. You are awesome. Below is the quick example I came up with. It works. Can you review and let know your thoughts/improvements? Thanks in advance.

App.svelte

<script>
  import Router from "./Router.svelte";

  const routes = {
    "/parent/*": import("./Parent.svelte")
  };
</script>

<main>
  <h1>App</h1>
  <a href="/parent/child1">/parent/child1</a>
  <a href="/parent/child2">/parent/child2</a>
  <Router {routes} />
</main>

Parent.svelte

<script>
  import Router from "./Router.svelte";

  export let wild;

  const routes = {
    child1: import("./Child1.svelte"),
    child2: import("./Child2.svelte")
  };
</script>

<div>
  <h2>Parent</h2>
  <Router {routes} {wild} />
</div>

Child1.svelte

<h3>Child1</h3>

Child2.svelte

<h3>Child2</h3>

Router.svelte

<script>
  import { onDestroy } from "svelte";
  import Navaid from "navaid";

  export let wild;
  export let routes;

  let Route, params;

  const draw = (m, obj) => {
    params = obj || {};
    if (m.preload) {
      m.preload({ params }).then(() => {
        Route = m.default;
      });
    } else {
      Route = m.default;
    }
  };

  const router = Navaid("/");
  $: {
    for (let path in routes) {
      const thunk = routes[path];
      router.on(path, p => thunk.then(m => draw(m, p)));
    }

    if (wild) {
      router.run(wild);
    } else {
      router.listen();
    }
  }

  onDestroy(router.unlisten);
</script>

<svelte:component this={Route} {...params} />

@lukeed
Copy link
Owner

lukeed commented Apr 20, 2020

My first thought is that all your routes will load the chunks immediately. They'll still be code-split but that doesn't help on its own if you're loading everything upfront anyway.

My second thought is that I, personally, wouldn't reach to make an abstraction if you're only using this twice (main + 1 sub resource).

I can get to a computer later tonight and look at anything else in particular you'd like me to

@ravilution
Copy link

@lukeed you are right. I fixed the loading of chunks using below code. I am going to use it in so many places and not just twice. So I needed the abstraction. In the example, I am only using twice.

App.svelte

<script>
  import Router from "./Router.svelte";

  const routes = {
    "/parent/*": () => import("./Parent.svelte")
  };
</script>

<main>
  <h1>App</h1>
  <a href="/parent/child1">/parent/child1</a>
  <a href="/parent/child2">/parent/child2</a>
  <Router {routes} />
</main>

Parent.svelte

<script>
  import Router from "./Router.svelte";

  export let wild;

  const routes = {
    child1: () => import("./Child1.svelte"),
    child2: () => import("./Child2.svelte")
  };
</script>

<div>
  <h2>Parent</h2>
  <Router {routes} {wild} />
</div>

Child1.svelte

<h3>Child1</h3>

Child2.svelte

<h3>Child2</h3>

Router.svelte

<script>
  import { onDestroy } from "svelte";
  import Navaid from "navaid";

  export let wild;
  export let routes;

  let Route, params;

  const draw = (m, obj) => {
    params = obj || {};
    if (m.preload) {
      m.preload({ params }).then(() => {
        Route = m.default;
      });
    } else {
      Route = m.default;
    }
  };

  const router = Navaid("/");
  $: {
    for (let path in routes) {
      const getThunk = routes[path];
      router.on(path, p => getThunk().then(m => draw(m, p)));
    }

    if (wild) {
      router.run(wild);
    } else {
      router.listen();
    }
  }

  onDestroy(router.unlisten);
</script>

<svelte:component this={Route} {...params} />

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

No branches or pull requests

3 participants