Skip to content

Routing ​

Składanie tras ​

Plik src/router/routes.js importuje trasy każdego modułu i skleja je w jedną tablicę:

js
// src/router/routes.js
const routes = [
  ...authRoutes,
  ...userRoutes,
  ...mediaRoutes,
  // ... pozostałe moduły

  // --place where generator will inject new routes
  // coreRoutes should be the last one
  ...coreRoutes,
];

Tablica coreRoutes musi pozostać ostatnia

Plik modules/core/routes/public.js zawiera trasę /:catchAll(.*)* obsługującą stronę 404. Wszystko, co zostanie dopisane poniżej niej, nigdy się nie dopasuje.

Wewnątrz modułu trasy poszczególnych modeli agreguje plik routes/index.js:

js
// src/modules/blog/routes/index.js
import articleRoutes from "modules/blog/routes/articles";
import categoryRoutes from "modules/blog/routes/categories.js";

const routes = [
  articleRoutes,
  categoryRoutes,

  // --place where generator will inject new routes
];

export default routes;

Kształt trasy modelu ​

Każdy model deklaruje własny rekord najwyższego poziomu, zawierający layout i flagę secured. Właściwe strony są jego dziećmi:

js
// src/modules/blog/routes/articles.js
const articleRouteName = "blog.articles";

export default {
  path: "/blog/articles",
  component: () => import("layouts/MasterLayout.vue"),
  meta: { secured: true },
  children: [
    {
      path: "",
      name: `${articleRouteName}.index`,
      meta: { access: `${articleRouteName}.index` },
      component: () => import("modules/blog/pages/articles/ArticleIndexPage.vue"),
    },
    {
      path: "create",
      name: `${articleRouteName}.create`,
      meta: { access: `${articleRouteName}.store` },
      component: () => import("modules/blog/pages/articles/ArticleCreatePage.vue"),
    },
    {
      path: ":id",
      name: `${articleRouteName}.edit`,
      meta: { access: `${articleRouteName}.show` },
      component: () => import("modules/blog/pages/articles/ArticleEditPage.vue"),
    },
  ],
};

Akcja w nazwie trasy nie musi odpowiadać nazwie uprawnienia:

Nazwa trasymeta.access
blog.articles.indexblog.articles.index
blog.articles.createblog.articles.store
blog.articles.editblog.articles.show

Uprawnienia nazwane są tak jak metody kontrolera po stronie backendu, natomiast trasy — tak jak ekrany w panelu.

Konwencja <moduł>.<modele>.<akcja> ​

Z nazwy trasy korzysta pięć mechanizmów naraz — jej format nie jest więc kwestią estetyki. Zmiana nazwy psuje wszystkie pięć jednocześnie:

MechanizmWyliczana wartośćPlik
Guard nawigacjiuprawnienie (meta.access albo to.name)modules/core/helpers/navigation-guards.js
useCoreTabletrasa edycji ${alias}.editmodules/core/composables/table.js
useFormSubmittrasa powrotu ${alias}.index i trasa po zapisie ${alias}.editmodules/core/composables/form.js
useCoreBreadcrumbsklucze i18n powstałe z rozbicia nazwy po kropcemodules/core/composables/breadcrumbs.js
useNestedRoutesaktywna pozycja menu, na podstawie dwóch pierwszych segmentówsrc/composables/useNestedRoutes.js

W praktyce do komponentów przekazywany jest wyłącznie alias:

js
// src/modules/blog/components/BlogArticlesTable.vue
useCoreTable({ resource: "/blog/articles", alias: "blog.articles" });

Pozostałe trasy wyliczane są automatycznie.

Odstępstwo od konwencji wymaga jawnej konfiguracji

Jeżeli trasa edycji nie nazywa się ${alias}.edit, należy podać ją bezpośrednio — w useCoreTable poprzez własną definicję akcji edit, a w useFormSubmit poprzez opcje onSaveRoute i redirectRoute.

Zabezpieczanie tras ​

Za kontrolę dostępu odpowiadają dwa pola w obiekcie meta:

PoleUmiejscowienieZnaczenie
secured: truerekord nadrzędny, ten z layoutemTrasa wymaga zalogowania. Guard sprawdza to.matched.some(r => r.meta.secured), wystarczy więc zadeklarować je raz, na rodzicu
access: "..."rekord dzieckaKlucz uprawnienia. Domyślnie przyjmowana jest wartość to.name

Działanie samego guarda opisuje Autentykacja.

Dodawanie trasy niestandardowej ​

Dopisz kolejne dziecko do tablicy children. Poniżej ekran zmiany kolejności kategorii w module FAQ:

js
// src/modules/faq/routes/categories.js
{
  path: "reorder",
  name: `${categoriesRouteName}.reorder`,
  meta: { access: `${categoriesRouteName}.update` },
  component: () => import("modules/faq/pages/categories/CategoryReorderPage.vue"),
},

Kolejność dzieci ma znaczenie

Trasy reorder i create muszą zostać zadeklarowane przed :id, w przeciwnym razie trasa parametryczna przechwyci je jako identyfikator.

Uzupełnij następnie klucz faq.categories.title.reorder w tłumaczeniach modułu — bez niego breadcrumbs wyświetlą surowy klucz.

js
// src/modules/blog/pages/articles/ArticleEditPage.vue
const breadcrumbs = useCoreBreadcrumbs("blog.articles.edit");

Composable useCoreBreadcrumbs(key, data?) rozbija klucz na <moduł>.<modele>.<akcja> i buduje ścieżkę zgodnie z konwencją i18n.

Dla akcji index powstają dwa elementy:

t("blog.name", 2)  →  t("blog.articles.title.index")

Dla pozostałych akcji powstają trzy, przy czym element środkowy jest odnośnikiem:

t("blog.name", 2)  →  t("blog.articles.title.index")  →  t("blog.articles.title.edit")
                       { name: "blog.articles.index" }

Zwracana jest wartość typu computed, przekazywana bezpośrednio do strony:

vue
<!-- src/modules/blog/pages/articles/ArticleIndexPage.vue -->
<base-card-page :title="$t('blog.articles.title.index')" :breadcrumbs="breadcrumbs">

Własne breadcrumbs ​

Konwencja nie wystarcza, gdy breadcrumbs zależą od danych rekordu — na przykład gdy ostatni element ma prezentować tytuł artykułu. Utwórz wtedy w module plik <moduł>.breadcrumbs.js; import.meta.glob wykryje go automatycznie.

Plik eksportuje funkcję przyjmującą t i zwracającą mapę kluczy "<modele>.<akcja>":

js
// src/modules/blog/blog.breadcrumbs.js
export default (t) => ({
  "articles.edit": (data) => [
    { label: t("blog.name", 2) },
    { label: t("blog.articles.title.index"), to: { name: "blog.articles.index" } },
    { label: data?.title ?? t("blog.articles.title.edit") },
  ],
});

Dane przekazywane są drugim argumentem composable:

js
// src/modules/blog/pages/articles/ArticleEditPage.vue
const breadcrumbs = useCoreBreadcrumbs("blog.articles.edit", () => query.data.value);

Klucz nieodnaleziony w mapie oznacza powrót do zachowania konwencyjnego — nadpisywać można więc wyłącznie wybrane ekrany, bez opisywania pozostałych.

TIP

Pojedynczy element breadcrumbs jest obiektem { label, to? }. Pominięcie to sprawia, że element renderowany jest jako zwykły tekst.

Layouty w trasach ​

LayoutZastosowanie
layouts/MasterLayout.vueWszystkie ekrany panelu, wraz z nagłówkiem i menu bocznym
layouts/AuthLayout.vueLogowanie i reset hasła
layouts/BlankLayout.vueStrona 404 oraz inne ekrany bez elementów nawigacyjnych

Szczegóły opisują Layouty i strony.

Made by Moonbite