Dynamische breadcrumbs bouwen vanaf een URL-pad

Hoe de waarde van url.path in een Joystick-component te nemen en deze om te zetten in een dynamische breadcrumb-gebruikersinterface.

Aan de slag

Voor deze tutorial gaan we het full-stack JavaScript-framework van CheatCode, Joystick, gebruiken. Joystick brengt een front-end UI-framework samen met een Node.js-back-end voor het bouwen van apps.

Om te beginnen willen we Joystick via NPM installeren. Zorg ervoor dat u Node.js 16+ gebruikt voordat u installeert om compatibiliteit te garanderen (lees deze tutorial eerst als u wilt leren hoe u Node.js installeert of meerdere versies op uw computer uitvoert):

Terminal

npm i -g @joystick.js/cli

Hiermee wordt Joystick wereldwijd op uw computer geïnstalleerd. Na de installatie gaan we een nieuw project maken:

Terminal

joystick create app

Na een paar seconden ziet u een bericht dat u bent uitgelogd op cd in uw nieuwe project en voer joystick start . uit :

Terminal

cd app && joystick start

Hierna zou je app moeten werken en zijn we klaar om aan de slag te gaan.

Geneste routes toevoegen

Om een ​​breadcrumb-gebruikersinterface te demonstreren, hebben we een set geneste routes nodig waarmee we kunnen werken. Om het simpel te houden, laten we beginnen met het openen van de index.server.js bestand in de root van het project dat we zojuist hebben gemaakt en voeg een paar routes toe:

Terminal

import node from "@joystick.js/node";
import api from "./api";

node.app({
  api,
  routes: {
    "/": (req, res) => {
      res.render("ui/pages/index/index.js", {
        layout: "ui/layouts/app/index.js",
      });
    },
    "/nested": (req, res) => {
      res.render("ui/pages/index/index.js", {
        layout: "ui/layouts/app/index.js",
      });
    },
    "/nested/path": (req, res) => {
      res.render("ui/pages/index/index.js", {
        layout: "ui/layouts/app/index.js",
      });
    },
    "/nested/path/to": (req, res) => {
      res.render("ui/pages/index/index.js", {
        layout: "ui/layouts/app/index.js",
      });
    },
    "/nested/path/to/:thing": (req, res) => {
      res.render("ui/pages/index/index.js", {
        layout: "ui/layouts/app/index.js",
      });
    },
    "*": (req, res) => {
      res.render("ui/pages/error/index.js", {
        layout: "ui/layouts/app/index.js",
        props: {
          statusCode: 404,
        },
      });
    },
  },
});

In de app die we zojuist hebben gemaakt, de index.server.js bestand is het belangrijkste "startpunt" voor de server van onze applicatie. Binnen bellen we naar de node.app() functie van de @joystick.js/node pakket om onze server op te starten, de API door te geven die we willen dat deze wordt geladen en de routes die we beschikbaar willen hebben in onze app.

Waar we ons hier op willen concentreren, zijn de routes , en in het bijzonder alle routes die we hebben toegevoegd, beginnend met /nested . Hier maken we een pseudo-genesteld URL-patroon dat we kunnen gebruiken om onze code voor het genereren van broodkruimels te testen.

Voor elke /nested route, doen we precies hetzelfde:render de index paginacomponent (we hebben zojuist de inhoud van de / gekopieerd en geplakt de terugbelfunctie van de route voor elke /nested route). Het verschil tussen elk is het pad zelf. Merk op dat we voor elke route die we hebben toegevoegd een extra niveau dieper gaan:

  • /nested
  • /nested/path
  • /nested/path/to
  • /nested/path/to/:thing

Het einddoel is dat we met deze structuur nu een geneste set routes hebben die we gemakkelijk kunnen weergeven als broodkruimels.

Vervolgens willen we de /ui/pages/index/index.js . wijzigen bestand dat we hier weergeven om onze breadcrumbs-gebruikersinterface uit te bouwen.

Een dynamische broodkruimelgenerator toevoegen

Toen we onze app maakten met joystick create app eerder kregen we ook een voorbeeldpaginacomponent op /ui/pages/index/index.js . Laten we dat nu eens openen en de bestaande inhoud vervangen door een skeletcomponent die we kunnen gebruiken om onze breadcrumb-gebruikersinterface te bouwen.

/ui/pages/index/index.js

import ui from '@joystick.js/ui';

const Index = ui.component({
  render: () => {
    return `
      <div>
      </div>
    `;
  },
});

export default Index;

Als dat op zijn plaats is, is het eerste dat we willen doen de daadwerkelijke creatie van onze broodkruimels aansluiten en ons vervolgens concentreren op het weergeven ervan op de pagina. Om dit te doen, gaan we vertrouwen op de methods eigenschap van een joystickcomponent.

/ui/pages/index/index.js

import ui from '@joystick.js/ui';

const Index = ui.component({
  methods: {
    getBreadcrumbs: (component) => {
      // We'll build our breadcrumbs array here...
    },
  },
  render: () => {
    return `
      <div>
      </div>
    `;
  },
});

export default Index;

In een Joystick-component bevat de eigenschap Methods een object met diverse methoden (een andere naam voor functies die in JavaScript voor een object zijn gedefinieerd) die verband houden met onze component. Wat we nu willen doen is een functie definiëren getBreadcrumbs() die het zware werk zal doen om ons URL-pad om te zetten in een reeks objecten die elke broodkruimel beschrijven die we willen weergeven.

/ui/pages/index/index.js

import ui from '@joystick.js/ui';

const Index = ui.component({
  methods: {
    getBreadcrumbs: (component) => {
      const pathParts = component?.url?.path?.split('/').filter((part) => part?.trim() !== '');
      return pathParts?.map((part, partIndex) => {
        const previousParts = pathParts.slice(0, partIndex);
        return {
          label: part,
          href: previousParts?.length > 0 ? `/${previousParts?.join('/')}/${part}` : `/${part}`,
        };
      }) || [];
    },
  },
  render: () => {
    return `
      <div>
      </div>
    `;
  },
});

export default Index;

We hebben de hele code hier voor de duidelijkheid gedumpt, dus laten we er doorheen gaan. Ten eerste is ons doel om deze functie getBreadcrumbs . te kunnen aanroepen en laat het ons een reeks objecten retourneren waarbij elk object een van onze broodkruimels beschrijft.

Om daar te komen, moeten we het huidige pad krijgen waar onze gebruiker naar kijkt. We hebben hiervoor twee opties in onze app, beide even gemakkelijk. Ten eerste kunnen we in een webbrowser altijd het huidige pad krijgen via de window.location.pathname globale waarde (location.pathname in het kort). Omdat we met een Joystick-app werken, gaan we hier de url.path . gebruiken waarde (die identiek is aan location.pathname ) beschikbaar op onze componentinstantie.

U zult merken dat bij het definiëren van een methode op een Joystick-component, als er geen argumenten worden doorgegeven aan die functie wanneer we deze aanroepen, Joystick automatisch het laatst mogelijke argument toewijst aan de componentinstantie. Als we bijvoorbeeld methods.getBreadcrumbs('something') . hebben gebeld , zou de functiehandtekening hierboven veranderen in getBreadcrumbs: (someValue, component) => { ... } .

Binnenkant van onze functie, van de component we verkrijgen bijvoorbeeld het huidige pad met component.url.path als een koord. Om bij een array te komen, moeten we eerst ons pad in delen splitsen. Om dat te doen, moeten we de .split() . gebruiken functie beschikbaar op alle strings in JavaScript. Naar .split() , we kunnen een teken doorgeven dat we willen splitsen op . Omdat we te maken hebben met een pad als /nested/path/to/123 we willen splitsen op de / schuine streep naar voren. Het eindresultaat is een array als deze:

['', 'nested', 'path', 'to', '123']

Dit brengt ons het meest van de weg, maar merk op dat er hier een lege string is. Dat komt omdat toen we een .split('/') . deden , de eerste schuine streep is geteld, maar omdat er niets aan voorafgaat, krijgen we gewoon een lege waarde.

Om dit aan te pakken, merk op dat de volledige regel hier is:

const pathParts = component?.url?.path?.split('/').filter((part) => part?.trim() !== '');

Wat dit zegt is "neem de url.path waarde als een string, splits het in een array met behulp van de / schuine streep naar voren als scheidingsteken en filter vervolgens elk deel in de resulterende array uit als het bijsnijden van alle witruimte resulteert in een lege tekenreeks."

Het eindresultaat? We krijgen een schone array om mee te werken, zoals ['nested', 'path', 'to', '123'] in onze pathParts variabel.

Met deze array hebben we wat we nodig hebben om onze broodkruimels uit te bouwen. Vervolgens moeten we deze array in kaart brengen. Voor elke iteratie willen we het werk doen dat nodig is om ons breadcrumb-object te bouwen. Elke breadcrumb heeft twee eigenschappen:label wat de weergegeven naam is die gebruikers zullen zien in de broodkruimelketen en href wat de URL is waarnaar de breadcrumb wordt gelinkt.

Voor de label , ons werk is eenvoudig:we hergebruiken gewoon de naam van het pad part we zijn momenteel aan het rondlopen. href is wat lastiger. Hier moeten we ervoor zorgen dat elke volgende broodkruimel weet wat er voor kwam, dus als we erop klikken, verwijzen we naar de juiste URL.

Om dit te doen, hebben we net binnen onze kaart een nieuwe variabele toegevoegd previousParts waarvoor onze pathParts . nodig is array en roept de .slice() . aan methode erop, zeggende "geef me alles van het eerste element in de array tot de index van het huidige deel." Met andere woorden, dit levert ons een nieuwe op array met alles wat vóór de huidige part . kwam .

Beneden op het object dat we teruggeven van onze .map() we gebruiken een ternaire operator om de waarde van href . in te stellen afhankelijk van de lengte van de previousParts reeks. Als de lengte 0 . is , we staan ​​aan het begin van ons pad en er zijn dus geen eerdere delen om te renderen. Als dit het geval is, retourneren we gewoon de href als /${part} .

Als er zijn previousParts , we gebruiken de .join() methode op die array om de array weer om te zetten in een string, waarbij de resulterende string wordt samengevoegd met de naam van de huidige part . Het eindresultaat? Voor elke iteratie krijgen we zoiets als dit:

{ label: 'nested', href: '/nested' }
{ label: 'path', href: '/nested/path' }
{ label: 'to', href: '/nested/path/to' }
{ label: '123', href: '/nested/path/to/123' }

Dat is het voor krijgen onze broodkruimels. Laten we ze nu op de pagina weergeven.

/ui/pages/index/index.js

import ui from '@joystick.js/ui';

const Index = ui.component({
  methods: {
    getBreadcrumbs: (component) => { ... },
  },
  css: `
    .breadcrumbs {
      display: flex;
    }

    .breadcrumbs li {
      list-style: none;
    }

    .breadcrumbs li:before {
      content: "/";
      display: inline-flex;
      margin-right: 10px;
    }

    .breadcrumbs li:not(:last-child) {
      margin-right: 10px;
    }
  `,
  render: ({ when, each, methods }) => {
    const breadcrumbs = methods.getBreadcrumbs();

    return `
      <div>
        ${when(breadcrumbs?.length > 0, `
          <ul class="breadcrumbs">
            ${each(breadcrumbs, (breadcrumb) => {
              return `
                <li><a href="${breadcrumb?.href}">${breadcrumb?.label}</a></li>
              `;
            })}
          </ul>
        `)}
      </div>
    `;
  },
});

export default Index;

Het deel waar we aandacht aan willen besteden staat in de render() functie. Hier hebben we de weergave van onze lege <div></div> . verwisseld met onze broodkruimels.

Naar onze render() functie, verwachten we dat Joystick ons ​​een object zal doorgeven dat de huidige componentinstantie vertegenwoordigt. In plaats van render: (component) => {} te schrijven hier gebruiken we JavaScript-destructuring om de specifieke variabelen die we van dat object willen, te "plukken". Dus, in plaats van component.when . te schrijven , component.each , enz., kunnen we gewoon when . schrijven , each , en methods (verwijzend naar dezelfde eigenschappen met steno).

De methods . gebruiken eigendom hiervan, net binnen render() , bellen we naar methods.getBreadcrumbs() het resultaat (onze reeks broodkruimelobjecten) opslaan in een variabele breadcrumbs . Met deze array gebruiken we vervolgens de when() renderfunctie in Joystick waarmee we wat HTML voorwaardelijk kunnen renderen wanneer de eerste waarde die we aan de functie doorgeven true is .

Hier willen we een HTML-tekenreeks retourneren die een <ul></ul> . weergeeft (die onze lijst met broodkruimels vertegenwoordigt). Binnen in die <ul></ul> om elke broodkruimel weer te geven, gebruiken we de each() render-functie om te zeggen, gegeven de array die als eerste argument is doorgegeven, roept u voor elk item in die array de functie aan die als tweede argument is doorgegeven.

Voor die functie verwachten we elk item te ontvangen in de array die we hebben doorgegeven aan each , of, een van onze breadcrumb voorwerpen. Binnen de functie verwacht Joystick dat we een reeks HTML retourneren voor elke iteratie van de breadcrumbs reeks. Omdat we in een <ul></ul> zitten tag, voor elke broodkruimel willen we een <li></li> . weergeven tag met een <a></a> label erin. Van daaruit gebruiken we gewone JavaScript-interpolatie om de waarde van onze href door te geven en label van de huidige breadcrumb voorwerp.

Dat is het! Bovenaan hebben we een css . toegevoegd eigendom met een aantal eenvoudige styling om dingen op te ruimen. Als we een browser openen en schakelen tussen onze geneste routes, zouden we onze breadcrumbs dynamisch moeten zien bijwerken.

Afsluiten

In deze zelfstudie hebben we geleerd hoe u enkele geneste routes in een Joystick-app kunt instellen. Vervolgens leerden we hoe we een Joystick-component konden maken die het huidige pad volgde en deze converteerde naar een reeks broodkruimelobjecten die we konden gebruiken voor weergave in onze gebruikersinterface. Ten slotte hebben we geleerd hoe we onze broodkruimels voorwaardelijk kunnen weergeven in onze gebruikersinterface, met behulp van Joystick's when en each renderfuncties.