Coder Social home page Coder Social logo

Nested routes about svelte-pathfinder HOT 14 OPEN

sveltetools avatar sveltetools commented on July 21, 2024
Nested routes

from svelte-pathfinder.

Comments (14)

PaulMaly avatar PaulMaly commented on July 21, 2024 1

Yep, your case is clear and seems work as expected. I would even say - works as it coded.

pathfinder allows you to have your own route ranking system - just place your conditions in the specific order for the specific place of the code. It's provides a minimal learning curve because you don't need to know the ranking rules. You work with plain state and write the conditions you need in very obvious way.

So, I don't think that pathfider needs to have some kind of route ranking system, because it has no routes at all ))) If you'll try don't perceive navigation as a set of pre-defined routes, but just try to work with it as a state that is no different from any other state of your application, then everything will fall into place. After all, you don't have any ranking system for other types of states, do you?

Probably, in future, I'll implement supporting of custom regexp patterns to match paths. When, you will be able to solve your issue in such particular way:

{:else if params = $pattern(/task/(?<taskId>\d+)/)}
  <TaskView params={params} />
{:else if params = $pattern('/task/edit')}

from svelte-pathfinder.

PaulMaly avatar PaulMaly commented on July 21, 2024

Hi, in general pathfinder is very unopinionated how exactly you’ll use it in your application and provides only very low-level instruments to manipulating routing state.

This particular example could be implemented in several different ways. Your example looks like a regular config-based routing implementation, but in pathfinder you probably don't need to create such configs at all. You can check and subscribe to any part of your router state at any moment and modify your application and markup in any ways.

If you would like to use config-based approach to describing a routes in your application you probably will need to write some wrapper around pathfinder to handle configuration and apply it to the router state.

Your example is not tricky enough to show something interesting with layouting, but I can imagine more cunning example with multiple layouts:

// routes.js

export const routes = [{
         path: '/product',
         component: ProductsLayout,
         children: [{
                path: '/:id',
                component: ProductPage,
         }, {
                path: '/',
                component: ProductsList,
         }],
}, {
         path: '/my',
         component: ProfileLayout,
         children: [ ... ]
}];

// AppShell.svelte

{#if Route}
<Route.Layout>
     <Route.Page />
</Route.Layout>
{:else}
   <NotFound />
{/if}

<script>
    import { pattern } from 'svelte-pathfinder';
    import { routes } from './routes.js';
    
    import NotFound from './pages/NotFound.svelte';
    
    let Route = null;
    $: {
        const layout = routes.find(route => $pattern(route.path, { loose: true }));
        if (layout) {
             const page = layout.children.find(child => $pattern(`${layout.path}${child.path}`));
             if (page) {
                Route = {
                     Layout: layout.component,
                     Page: page.component,
                };  
             } else {
                Route = null;
             }
        } else {
           Route = null;
        }
    }
</script>

This example is not well tested but I believe the idea is clear. And, of course, you can think up any configuration structure you want and made traverse of this structure in any ways you need.

Hope, this would help. Feel free to ask additional questions. Good luck!

from svelte-pathfinder.

eakl avatar eakl commented on July 21, 2024

Thank you for the detailed answer.

I have several questions.

  1. Adding <svelte:window on:click={click} /> in your markup makes all <a> (except the cases highlighted in the documentation) act as router link?

  2. Using the click helper, path-finder doesn't update the url when navigating?

  3. What this snippet supposed to do? Isn't it supposed to navigate to /about?

<script>
  const goToAbout = () => {
    $path = '/about'
  }
</script>

<button on:click={goToAbout}>Go to About</button>

{#if params = $pattern('/about')}
  <About />
{/if}
  1. How does a link is able to match the correct route without <Router> ... </Router>

Home.svelte

{#if params = $pattern('/')}
  <Home />
{:else if params = $pattern('/products')}
  <ProductList />
{:else if params = $pattern('/product/:id')}
  <ProductView productId={params.id} />
{:else if params = $pattern('/about')}
  <About />
{/if}

ProductList.svelte

<div>
  <ul class="menu">
    <li><a href="/product/1">Product 1</a></li>
    <li><a href="/product/2">Product 2</a></li>
    <li><a href="/product/3">Product 3</a></li>
  </ul>
</div>

ProductView.svelte

<main>
    <div>
        <a href="/about">Go to About</a>
    <div>

    <div>
        Product {productId}
    </div>
</main>

When I click on Go to About in ProductView.svelte how is path-finder able to match this route in Home.svelte?

{:else if params = $pattern('/about')}
  <About />
{/if}

Because there is no <Router> ... </Router> component, how path-finder knows I want to match the route defined in Home.svelte instead of a similar route defined somewhere else?

from svelte-pathfinder.

PaulMaly avatar PaulMaly commented on July 21, 2024

Hi,

Let me extend and swap your questions a little to reveal the logic of solutions and instruments provided by pathfinder.

What this snippet supposed to do? Isn't it supposed to navigate to /about?

Yes, it'll perform navigate to /about. As I noted before, pathfinder has no opinion on how you should apply it in your application, so absolutely all the things are optional and created only for convenience. The general idea is to change URL parts directly via corresponding stores ($path, $query, $fragment). So, you can write any handler of any event in your application and change any part of the URL in any way you want:

  const goToAbout = () => {
    $path = '/about';
  };
  // OR
  const changeFirstPathSegment = () => {
     $path[0] = 'about';
  };
  // OR
  const addQueryParameter = () => {
      $query.page = 1;
  };

An important thing is that navigation in the context of a pathfinder is primarily a state change, and then an optional URL change. Actually, URL change is just optional side-effect from router state change and can we disabled automatically or manually over configuration depending on your goals.

Adding <svelte:window on:click={click} /> in your markup makes all (except the cases highlighted in the documentation) act as router link?

In some cases a little convenient is to set router state in bulk, eg. by full URL including all parts. What's why was added 3 main helpers:

goto() - to set all stores in bulk using URL as string or URL object.
redirect() - the same thing, but without creating new history record.
back() - basically just a wrapper around history.back() with additional fallback.

goto('/about');

// equalent to 

$path = '/about';
$query = '';
$fragment = '';

goto('/about#contacts');

// equalent to 

$path = '/about';
$query = '';
$fragment = 'contacts';

Another common task is to listen a click on regular html link elements (a-tags) and performing navigation to specific URL in its href-attribute. You able to implement your own handler or just use built-in click helper which is just handling click event, checking some conditions and performing navigation for you. click helper works using JS event bubbling mechanism, so you able to use it anywhere you need, not only on window object:

<!-- will handle all well-defined links in you app -->
<svelte:window on:click={click} />

<!-- will handle only links inside nav-tag -->
<nav on:click={click}>
   <a href="...">...</a>
   <a href="...">...</a>
   <a href="...">...</a>
</nav>

Also pathfinder provide submit helper for advanced users to handle html GET-forms in your application.

If you prefer to have special router link component you can create it by yourself, for example with some specific styles:

// RouterLink.svelte

<a {href} {...$$restProps} class="..." on:click|preventDefault={() => goto(href)}>
   <slot />
</a>

<script>
    import { goto } from 'svelte-pathfinder';
    export let href = '';
</script>

<style>
  ...
</style>

Using the click helper, path-finder doesn't update the url when navigating?

It depends on your configuration and environment, but yes, in general case, click helper will update URL as well as router state. Actually, click helper just utilize goto helper inside to set router state in bulk.

How does a link is able to match the correct route without ...

The main idea of pathfinder is that actually you don't need to learn something specially related only to that router. If you know how regular svelte stores works then you know how to deal with pathfinder. So, it's just a Svelte's stores and you can use them exactly the same as any others in you application!

Because there is no ... component, how path-finder knows I want to match the route defined in Home.svelte instead of a similar route defined somewhere else?

pathfinder have no idea about anything except you'll tell him, because in context of pathfinder we don't have a built-in route-concept. It's just a pure state. You wrote these lines, this condition and when state has changed this condition will be triggered to sync with new state:

// somewhere in you code
<button on:click={() => $path = '/about'}>...</button>
// it will triggering path-state change and all related expressions
// 👇
{:else if params = $pattern('/about')}
  <About />
{/if}

In fact, in any router, you end up working with a certain state. Usually, it's just hidden from you by the concepts wrapped around this router. It dictates how exactly you should interact with this state, how to change it, how to react to it and so on.

pathfinder gives you low-level access to pure state of the routing and will perform optional side-effect to URL for you if needed. Usually, using pathfinder you don't need to limit yourself by concepts like route/page and so on. Also, you don't need to perceive navigation as rendering a particular component over a particular route pattern as you did in config-based routers:

// App.svelte

<svelte:window on:click={click}/>
<main>
    <MainNav>
    {#if $path.length > 1}
        <SecondaryNav />
    {/if}
    </MainNav>
    <BreadCrumbs crumbs={$path} />
    ...
    {#if $path[2]}
        <SidePanel contentType={$path[2]} />
    {/if}
</main>

{#if $fragment}
   <Modal>
      ...
   </Modal>
{/if}

Hope this would help, feel free to ask additional questions.

from svelte-pathfinder.

eakl avatar eakl commented on July 21, 2024

First, thank you for taking the time to write long and detail answers. Every question I had is clearer now.

I'll play a bit with the library.

But maybe a last question. How do you intend to develop the library (if your plan is to go beyond its current development)

EDIT

Could you also document the ParseParamsOptions interface like you did for Prefs and what each option means?

from svelte-pathfinder.

PaulMaly avatar PaulMaly commented on July 21, 2024

First, thank you for taking the time to write long and detail answers. Every question I had is clearer now.
I'll play a bit with the library.

Glad to hear it and good luck.

Could you also document the ParseParamsOptions interface like you did for Prefs and what each option means?

I think it's a good idea. I'll add its description in nearest future. In general, I understand that documentation and the lack of sufficient examples are the weakest side of my libraries. Some of them, for example svelte-asyncable, in my opinion, are must-have in any project essentially (at least I use them everywhere).

But maybe a last question. How do you intend to develop the library (if your plan is to go beyond its current development)

Currently, I've some plans regarding documentation improvements for current version. Also, I working on some other packages where pathfinder has a key role.

Also, at this moment, I feel that a critical mass of questions, very similar to those you asked in this thread, have accumulated already. And probably, in the next major version, I'll include an optional pack of add-ons for each basic implementation of most familiar router models on-top of pathfinder, such as component-based, config-based router models, etc. To just give the ideas how pathfinder can be applied to the projects in more familiar way.

At the same time, the use of these ready-to-use add-ons won't exclude the possibility of using pathfinder as it designed initially. So, we'll be able to use the best of both options.

from svelte-pathfinder.

eakl avatar eakl commented on July 21, 2024

And probably, in the next major version, I'll include an optional pack of add-ons for each basic implementation of most familiar router models on-top of pathfinder, such as component-based, config-based router models, etc. To just give the ideas how pathfinder can be applied to the projects in more familiar way.

That's exactly what I was hoping for. I like the tiny, yet powerful, router implementation. But some examples on how to use the library would be most welcome.

Svelte stores are not complex to understand but applying pathfinder to a real case scenario is still quite obscure for me, especially coming from React Router where everything is abstracted behind a nice API.

I like touching the bare bone of the library and feel I can customize everything as my knowledge grows but I need some common use cases guidance as well.

I tend to prefer to centralize my routes in a single location to have more control over it rather than modifying the markup in the component directly.

Thank you for your answers and I'll follow the development of this library closely.

from svelte-pathfinder.

PaulMaly avatar PaulMaly commented on July 21, 2024

especially coming from React Router where everything is abstracted behind a nice API.

That's why I've so avoiding to specify how exactly to use a pathfinder in projects ))) For me, API of React Router as ugly as possible, but I understand it's just a my opinion and I don't want to impose how exactly router API should look like.

I tend to prefer to centralize my routes in a single location to have more control over it rather than modifying the markup in the component directly.

Because you perceive routing as a predefined set of the routes (usually even a set of the paths only), but it's just a one way to work with. pathfinder propose to perceive navigation as the ever-changing state of your application, not a set of the routes and the components which are resolved according these routes. It's unusual, but allows you to build more flexible and complex UIs without limiting your self.

In my experience with other types of the routers (I tried all of them no doubt), every time in each project I was faced with inflexibility and the inability to implement something in the way that I would like and how it would be most convenient. That's why I created pathfinder, no doubt, it's the easiest way to work with routing and change routing state in applications that I've ever work with.

from svelte-pathfinder.

PaulMaly avatar PaulMaly commented on July 21, 2024

Could you also document the ParseParamsOptions interface like you did for Prefs and what each option means?

FYI: https://github.com/sveltetools/svelte-pathfinder#parseparamsoptions

from svelte-pathfinder.

eakl avatar eakl commented on July 21, 2024

Thank you for the documentation.

pathfinder propose to perceive navigation as the ever-changing state of your application, not a set of the routes and the components which are resolved according these routes.

Interesting

Is typing a URL can modify the stores as well?

<ul on:click={click}>
  <li>
    <a href="/">
      Home
    </a>
  </li>
  <li>
    <a href="/tasks">
      All Tasks
    </a>
  </li>
  <li>
    <a href="/task/1">
      Single Task
    </a>
  </li>
</ul>

{#if params = $pattern('/') }
  <div>Home</div>
{/if}

{#if params = $pattern('/tasks') }
  <div>Tasks</div>
{/if}

{#if params = $pattern('/task/:id') }
  <div>Task id: {$path[1]}</div>
{/if}
  • Typing /tasks in the URL works fine
  • Typing /task in the URL show me the menu only as intended because the route doesn't exist
  • Typing /task/1 in the URL leads to an error
// Chrome
Failed to load resource: the server responded with a status of 404 (Not Found)

from svelte-pathfinder.

PaulMaly avatar PaulMaly commented on July 21, 2024

If you type something in browser's address bar and press enter to apply these changes to, it's will perform full page reload unrelated to specific JS-powered router. I suppose that you web server is not configured according minimal SPA requirements. Basically, to work in SPA mode, web server should respond with index.html to all requests except requests to real resources, eg. static assets and so on. For example, if you use something like sirv to serve your application, you need to enable single option in its config.

But you can play with pathfinder in that manner just emulating browser's address bar in you app: https://svelte.dev/repl/a67233d4e5754264a1e4922c7012345c?version=3.57.0

from svelte-pathfinder.

eakl avatar eakl commented on July 21, 2024

I have played with pathfinder and ran into this case

{#if params = $pattern('/')}
  <Home />
{:else if params = $pattern('/tasks')}
  <TasksList />
{:else if params = $pattern('/task/:taskId')}
  <TaskView params={params} />
{:else if params = $pattern('/task/edit')}
  <EditTask />
{:else if params = $pattern('/task/edit/sub1')}
  <EditSub1 />
{:else if params = $pattern('/task/edit/sub2')}
  <EditSub2 />
{:else if params = $pattern('/task/edit/sub3')}
  <EditSub3 />
{:else if params = $pattern('*')}
  <div>Not Found</div>
{/if}

All routes work except when:

  • I'm on /task/1 and I click on /task/edit
  • I'm on /task/edit and I click on /task/1

In those cases $path changes but the component is not rendered. And when I'm on a different route (like /), clicking on /task/edit renders /task/:taskId.

Swapping the order make it work and this is normal

{:else if params = $pattern('/task/edit')}
  <EditTask />
{:else if params = $pattern('/task/:taskId')}
  <TaskView params={params} />

I know pathfinder is a low level routing system and you may not want to add too many opinionated features. However, some routing library implement a route ranking system that allow the developer to add routes in any order.

Is it something you consider adding to pathfinder?

from svelte-pathfinder.

PaulMaly avatar PaulMaly commented on July 21, 2024

v4.7.0 version now support custom RegExp patterns in $pattern(). Short how-to is added to this section of README.

from svelte-pathfinder.

eakl avatar eakl commented on July 21, 2024

Thanks @PaulMaly I'll try that

from svelte-pathfinder.

Related Issues (14)

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.