How do I make a component that is aware of the current url in Sapper with Svelte?
Asked Answered
D

3

16

I have a page that has a nav bar with a "Quarters" link. Under the Quarters link, when the user is on the /quarters route, a list of quarters will be shown, like 2019Q2 etc. The url will be /quarters/2019q2.

I want to make a component that show shows a hyperlink that will have the selected class if the current url matches the href of the link. Here's the closest I can get:

<script>
  import { afterUpdate } from 'svelte';
  export let segment;
  export let text = 'text here';
  export let link;
  let isCurrentPath = false;
  console.log(segment, link, text);
  afterUpdate(() => {
    if (window.location.pathname.includes(link)) {
      isCurrentPath = true;
      debugger;
    }
    console.log('HL afterUpdate ', window.location);
  });
</script>

<style>
  /* omitted */
</style>

<a class:selected={segment && isCurrentPath} href={link}>{text}</a>

That works fine for the first load, but when the user navigates to a different data page the selection is not updated. How do I get some code to only run on the client-side? If I access the window object outside of afterUpdate I will get an null ref error from the server-side code.

ETA: Tried this too:

  let isCurrentPath = false;
  let path = typeof window === 'undefined' ? '' : window.location.pathname;
  $: if (path) isCurrentPath = window.location.pathname.includes(link);

That code doesn't fire when the user clicks one of the data links. Tried onMount as well with no positive result.

Doggone answered 5/9, 2019 at 21:34 Comment(0)
D
12

The trick is to create a reactive statement based on a value in the page store.

<!--
This is used to have a link on the page that will show highlighted if the url meets the criteria.
You might want to adjust the logic on line 19.
usage: 
<HighlightedLink bind:segment highlight="faq" rel="prefetch" link="/faq" text="FAQ" />
--> 

<script>
  import { stores } from '@sapper/app';
  const { page } = stores();
  export let highlight;
  export let segment;
  export let text = 'text here';
  export let link;
  export let target;
  let highlightPath = false;
  $: highlightPath =
    $page.path && highlight && ($page.path.includes(highlight) || $page.path.includes(link));
</script>

<style>
  .selected {
    position: relative;
    display: inline-block;
  }
  .selected::after {
    position: absolute;
    content: '';
    width: calc(100% - 1em);
    height: 2px;
    background-color: rgb(255, 62, 0);
    display: block;
    bottom: -1px;
  }
  a {
    padding-left: 10px;
  }
</style>


<a class:selected={highlightPath} href={link}>{text}</a>
Doggone answered 11/9, 2019 at 18:51 Comment(0)
P
13

For people using SvelteKit, the given answers still apply. Take a look at the docs for the page store: https://kit.svelte.dev/docs/modules#$app-stores-page

EDIT: There were breaking changes in a new SvelteKit update. You still access the current url from the page store like this:

<script>
  import { page } from '$app/stores';
</script>

<h1>{$page.url.pathname}</h1>
Phosphoroscope answered 29/7, 2021 at 15:32 Comment(2)
can you provide example? the docs are useless.Laborer
@Laborer Actually I think they're pretty good. But here you go: Import the store this way: import { page } from '$app/stores'; and then use it like this: {#if $page.path == '/some/route'}Phosphoroscope
D
12

The trick is to create a reactive statement based on a value in the page store.

<!--
This is used to have a link on the page that will show highlighted if the url meets the criteria.
You might want to adjust the logic on line 19.
usage: 
<HighlightedLink bind:segment highlight="faq" rel="prefetch" link="/faq" text="FAQ" />
--> 

<script>
  import { stores } from '@sapper/app';
  const { page } = stores();
  export let highlight;
  export let segment;
  export let text = 'text here';
  export let link;
  export let target;
  let highlightPath = false;
  $: highlightPath =
    $page.path && highlight && ($page.path.includes(highlight) || $page.path.includes(link));
</script>

<style>
  .selected {
    position: relative;
    display: inline-block;
  }
  .selected::after {
    position: absolute;
    content: '';
    width: calc(100% - 1em);
    height: 2px;
    background-color: rgb(255, 62, 0);
    display: block;
    bottom: -1px;
  }
  a {
    padding-left: 10px;
  }
</style>


<a class:selected={highlightPath} href={link}>{text}</a>
Doggone answered 11/9, 2019 at 18:51 Comment(0)
G
1

instead of using afterUpdate I believe you should look in to writing something with onMount and use reactive statements https://svelte.dev/examples#reactive-statements to let svelte know that you want to recalculate isCurrentPath whenever location changes.

Groundmass answered 6/9, 2019 at 17:22 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.