Yönlendirme
RouterView
Rota bileşenlerinin yerleştiği RouterView, iç içe ve adlandırılmış çıktılar, rota bileşeninin kurulması; JSX ile ve JSX'siz.
RouterView — rota çıktısı yerleştirme
Rota bileşenlerinin ekranda gösterileceği yer RouterView'dir. Layout içine yerleştirin; JSX'siz yazımda motifComponent(RouterView) ile kurulur. Layout rotasının bileşeni, çocuk rotaların yerleşeceği yere bir RouterView koyar. Örnekteki layout kendi içeriğinin yanına (main.detail) bir RouterView yerleştirir ve rotanın meta verisini useNavigation() ile okur:
import { Component, RouterView, useNavigation } from "@motifx/core";
import { FOLDERS, NotesService, type Folder } from "../services/notes";
export default class NotesLayout extends Component {
notes = this.getService(NotesService)!;
nav = useNavigation();
get folder(): Folder {
return this.nav.meta.folder as Folder;
}
override view() {
return (
<div class="notes">
<section class="list">
<h2>{() => FOLDERS[this.folder].title}</h2>
<div class="rows">
{this.notes.list(this.folder).map((note) => (
<a key={note.id} rel="router" href={`/${this.folder}/${note.id}`}>{note.title}</a>
))}
</div>
</section>
<main class="detail">
<RouterView />
</main>
</div>
);
}
}
import { FromService, RouterView, useNavigation } from "@motifx/core";
import { FOLDERS, NotesService, type Folder } from "../services/notes";
export default function NotesLayout() {
const notes = FromService(NotesService)!;
const nav = useNavigation();
const folder = () => nav.meta.folder as Folder;
return (
<div class="notes">
<section class="list">
<h2>{() => FOLDERS[folder()].title}</h2>
<div class="rows">
{notes.list(folder()).map((note) => (
<a key={note.id} rel="router" href={`/${folder()}/${note.id}`}>{note.title}</a>
))}
</div>
</section>
<main class="detail">
<RouterView />
</main>
</div>
);
}
import { FromService, motifOptions, RouterView, useNavigation } from "@motifx/core";
import { FOLDERS, NotesService, type Folder } from "../services/notes";
const NotesLayout = () => motifOptions({
el: "div",
nav: useNavigation(),
get folder(): Folder {
return this.nav.meta.folder as Folder;
},
onConfigured() {
this.class.add("notes");
},
view() {
const notes = FromService(NotesService)!;
return (
<>
<section class="list">
<h2>{() => FOLDERS[this.folder].title}</h2>
<div class="rows">
{notes.list(this.folder).map((note) => (
<a key={note.id} rel="router" href={`/${this.folder}/${note.id}`}>{note.title}</a>
))}
</div>
</section>
<main class="detail">
<RouterView />
</main>
</>
);
},
});
export default NotesLayout;
import { Component, ComponentBase, motifComponent, RouterView, useNavigation } from "@motifx/core";
import { FOLDERS, NotesService, type Folder, type Note } from "../services/notes";
export default class NotesLayout extends Component {
notes = this.getService(NotesService)!;
nav = useNavigation();
get folder(): Folder {
return this.nav.meta.folder as Folder;
}
override view() {
return motifComponent("div", (root: ComponentBase) => {
root.class.add("notes");
const list = motifComponent("section", (section: ComponentBase) => {
section.class.add("list");
const title = motifComponent("h2", (h2: ComponentBase) => h2.controls.add(motifComponent("text", (node: ComponentBase) => node.bindings.text(() => FOLDERS[this.folder].title))));
const rows = motifComponent("div", (div: ComponentBase) => {
div.class.add("rows");
div.controls.add(motifComponent({
initializeComponent: (holder: ComponentBase) => holder.bindings.list(() => this.notes.list(this.folder), (note: Note) => motifComponent("a", {
indexkey: () => note.id,
initializeComponent: (a: ComponentBase) => {
a.attr.add({ rel: "router", href: () => `/${this.folder}/${note.id}` });
a.controls.add(motifComponent("text", (node: ComponentBase) => node.bindings.add("textContent", note, "title")));
},
})),
}));
});
section.controls.add(title, rows);
});
const detail = motifComponent("main", (main: ComponentBase) => {
main.class.add("detail");
main.controls.add(motifComponent(RouterView));
});
root.controls.add(list, detail);
});
}
}
import { ComponentBase, FromService, motifComponent, RouterView, useNavigation } from "@motifx/core";
import { FOLDERS, NotesService, type Folder, type Note } from "../services/notes";
export default function NotesLayout() {
const notes = FromService(NotesService)!;
const nav = useNavigation();
const folder = () => nav.meta.folder as Folder;
return motifComponent("div", (root: ComponentBase) => {
root.class.add("notes");
const list = motifComponent("section", (section: ComponentBase) => {
section.class.add("list");
const title = motifComponent("h2", (h2: ComponentBase) => h2.controls.add(motifComponent("text", (node: ComponentBase) => node.bindings.text(() => FOLDERS[folder()].title))));
const rows = motifComponent("div", (div: ComponentBase) => {
div.class.add("rows");
div.controls.add(motifComponent({
initializeComponent: (holder: ComponentBase) => holder.bindings.list(() => notes.list(folder()), (note: Note) => motifComponent("a", {
indexkey: () => note.id,
initializeComponent: (a: ComponentBase) => {
a.attr.add({ rel: "router", href: () => `/${folder()}/${note.id}` });
a.controls.add(motifComponent("text", (node: ComponentBase) => node.bindings.add("textContent", note, "title")));
},
})),
}));
});
section.controls.add(title, rows);
});
const detail = motifComponent("main", (main: ComponentBase) => {
main.class.add("detail");
main.controls.add(motifComponent(RouterView));
});
root.controls.add(list, detail);
});
}
import { ComponentBase, FromService, motifComponent, motifOptions, RouterView, useNavigation } from "@motifx/core";
import { FOLDERS, NotesService, type Folder, type Note } from "../services/notes";
const NotesLayout = () => motifOptions({
el: "div",
nav: useNavigation(),
get folder(): Folder {
return this.nav.meta.folder as Folder;
},
onConfigured() {
this.class.add("notes");
},
view() {
const notes = FromService(NotesService)!;
const list = motifComponent("section", (section: ComponentBase) => {
section.class.add("list");
const title = motifComponent("h2", (h2: ComponentBase) => h2.controls.add(motifComponent("text", (node: ComponentBase) => node.bindings.text(() => FOLDERS[this.folder].title))));
const rows = motifComponent("div", (div: ComponentBase) => {
div.class.add("rows");
div.controls.add(motifComponent({
initializeComponent: (holder: ComponentBase) => holder.bindings.list(() => notes.list(this.folder), (note: Note) => motifComponent("a", {
indexkey: () => note.id,
initializeComponent: (a: ComponentBase) => {
a.attr.add({ rel: "router", href: () => `/${this.folder}/${note.id}` });
a.controls.add(motifComponent("text", (node: ComponentBase) => node.bindings.add("textContent", note, "title")));
},
})),
}));
});
section.controls.add(title, rows);
});
const detail = motifComponent("main", (main: ComponentBase) => {
main.class.add("detail");
main.controls.add(motifComponent(RouterView));
});
return [list, detail];
},
});
export default NotesLayout;
import { Component, RouterView, useNavigation } from "@motifx/core";
import { FOLDERS, NotesService } from "../services/notes";
export default class NotesLayout extends Component {
notes = this.getService(NotesService);
nav = useNavigation();
get folder() {
return this.nav.meta.folder;
}
view() {
return (
<div class="notes">
<section class="list">
<h2>{() => FOLDERS[this.folder].title}</h2>
<div class="rows">
{this.notes.list(this.folder).map((note) => (
<a key={note.id} rel="router" href={`/${this.folder}/${note.id}`}>{note.title}</a>
))}
</div>
</section>
<main class="detail">
<RouterView />
</main>
</div>
);
}
}
import { FromService, RouterView, useNavigation } from "@motifx/core";
import { FOLDERS, NotesService } from "../services/notes";
export default function NotesLayout() {
const notes = FromService(NotesService);
const nav = useNavigation();
const folder = () => nav.meta.folder;
return (
<div class="notes">
<section class="list">
<h2>{() => FOLDERS[folder()].title}</h2>
<div class="rows">
{notes.list(folder()).map((note) => (
<a key={note.id} rel="router" href={`/${folder()}/${note.id}`}>{note.title}</a>
))}
</div>
</section>
<main class="detail">
<RouterView />
</main>
</div>
);
}
import { FromService, motifOptions, RouterView, useNavigation } from "@motifx/core";
import { FOLDERS, NotesService } from "../services/notes";
const NotesLayout = () => motifOptions({
el: "div",
nav: useNavigation(),
get folder() {
return this.nav.meta.folder;
},
onConfigured() {
this.class.add("notes");
},
view() {
const notes = FromService(NotesService);
return (
<>
<section class="list">
<h2>{() => FOLDERS[this.folder].title}</h2>
<div class="rows">
{notes.list(this.folder).map((note) => (
<a key={note.id} rel="router" href={`/${this.folder}/${note.id}`}>{note.title}</a>
))}
</div>
</section>
<main class="detail">
<RouterView />
</main>
</>
);
},
});
export default NotesLayout;
import { Component, motifComponent, RouterView, useNavigation } from "@motifx/core";
import { FOLDERS, NotesService } from "../services/notes";
export default class NotesLayout extends Component {
notes = this.getService(NotesService);
nav = useNavigation();
get folder() {
return this.nav.meta.folder;
}
view() {
return motifComponent("div", (root) => {
root.class.add("notes");
const list = motifComponent("section", (section) => {
section.class.add("list");
const title = motifComponent("h2", (h2) => h2.controls.add(motifComponent("text", (node) => node.bindings.text(() => FOLDERS[this.folder].title))));
const rows = motifComponent("div", (div) => {
div.class.add("rows");
div.controls.add(motifComponent({
initializeComponent: (holder) => holder.bindings.list(() => this.notes.list(this.folder), (note) => motifComponent("a", {
indexkey: () => note.id,
initializeComponent: (a) => {
a.attr.add({ rel: "router", href: () => `/${this.folder}/${note.id}` });
a.controls.add(motifComponent("text", (node) => node.bindings.add("textContent", note, "title")));
},
})),
}));
});
section.controls.add(title, rows);
});
const detail = motifComponent("main", (main) => {
main.class.add("detail");
main.controls.add(motifComponent(RouterView));
});
root.controls.add(list, detail);
});
}
}
import { FromService, motifComponent, RouterView, useNavigation } from "@motifx/core";
import { FOLDERS, NotesService } from "../services/notes";
export default function NotesLayout() {
const notes = FromService(NotesService);
const nav = useNavigation();
const folder = () => nav.meta.folder;
return motifComponent("div", (root) => {
root.class.add("notes");
const list = motifComponent("section", (section) => {
section.class.add("list");
const title = motifComponent("h2", (h2) => h2.controls.add(motifComponent("text", (node) => node.bindings.text(() => FOLDERS[folder()].title))));
const rows = motifComponent("div", (div) => {
div.class.add("rows");
div.controls.add(motifComponent({
initializeComponent: (holder) => holder.bindings.list(() => notes.list(folder()), (note) => motifComponent("a", {
indexkey: () => note.id,
initializeComponent: (a) => {
a.attr.add({ rel: "router", href: () => `/${folder()}/${note.id}` });
a.controls.add(motifComponent("text", (node) => node.bindings.add("textContent", note, "title")));
},
})),
}));
});
section.controls.add(title, rows);
});
const detail = motifComponent("main", (main) => {
main.class.add("detail");
main.controls.add(motifComponent(RouterView));
});
root.controls.add(list, detail);
});
}
import { FromService, motifComponent, motifOptions, RouterView, useNavigation } from "@motifx/core";
import { FOLDERS, NotesService } from "../services/notes";
const NotesLayout = () => motifOptions({
el: "div",
nav: useNavigation(),
get folder() {
return this.nav.meta.folder;
},
onConfigured() {
this.class.add("notes");
},
view() {
const notes = FromService(NotesService);
const list = motifComponent("section", (section) => {
section.class.add("list");
const title = motifComponent("h2", (h2) => h2.controls.add(motifComponent("text", (node) => node.bindings.text(() => FOLDERS[this.folder].title))));
const rows = motifComponent("div", (div) => {
div.class.add("rows");
div.controls.add(motifComponent({
initializeComponent: (holder) => holder.bindings.list(() => notes.list(this.folder), (note) => motifComponent("a", {
indexkey: () => note.id,
initializeComponent: (a) => {
a.attr.add({ rel: "router", href: () => `/${this.folder}/${note.id}` });
a.controls.add(motifComponent("text", (node) => node.bindings.add("textContent", note, "title")));
},
})),
}));
});
section.controls.add(title, rows);
});
const detail = motifComponent("main", (main) => {
main.class.add("detail");
main.controls.add(motifComponent(RouterView));
});
return [list, detail];
},
});
export default NotesLayout;
Gezinme aynı layout rotasında kalırsa layout yerinde kalır, yalnızca RouterView'daki bileşen değişir; başka bir rotaya geçişte layout da yeniden kurulur (bkz. Bir adres ekrana nasıl gelir).
RouterView'ın kendi elemanı yoktur (elemansız, fragment bileşen): rota bileşeni doğrudan RouterView'ın durduğu yere, örnekte main.detail'in içine yerleşir. Etikete yazılan class, style gibi öznitelikler yazılacak eleman olmadığı için DOM'a yansımaz; çıktının çevresinde bir kap gerekiyorsa RouterView'ı bir elemanın içine koyun (bkz. Kök eleman).
Rota bileşeninin kurulması
control: () => import('./pages/SettingsPage') ile yüklenen modülün default dışa aktarımı rota bileşeni olarak kullanılır; default dışa aktarımı olmayan modülde sayfa kurulamaz, MJX304 raporlanır (nedeni MJX127) ve hata rotası gösterilir. Sınıf new Sınıf(app), fonksiyon fn(app) ile kurulur: ilk argüman çalışan Application'dır, rota bileşeni etiketten prop almaz. Options API nesnesi döndüren fonksiyon (ya da nesnenin kendisi) da rota bileşeni olur: fonksiyon yine fn(app) ile çağrılır, dönen nesne JSX'teki gibi bileşene çevrilir ve ctor prop almaz (undefined). Lazy ile yüklenen default da bu biçimlerden biri olabilir; Lazy onu argümansız kurar.
Bileşen DI'da kayıtlıysa (@Injectable sınıf ya da services.addTransient / addScoped / addSingleton kaydı) örnek sağlayıcıdan çözülür ve kaydın yaşam süresi sayfanın ömrünü belirler:
| Yaşam süresi | Sayfa |
|---|---|
transient (@Injectable'ın varsayılanı) |
Her ziyarette yeni sayfa kurulur; ayrılınca serbest bırakılır. |
scoped |
Her gezinmenin kapsamında bir sayfa kurulur; ayrılınca serbest bırakılır. keepAlive: true ile önbellekteki sayfa kendi gezinme kapsamından uzun yaşar. |
singleton |
Uygulama boyunca tek sayfadır: hazır örnek verilmiş rota gibi ayrılınca serbest bırakılmaz, outlet'ten ayrılır (onDeactivated), dönüşte aynı örnek geri bağlanır (onActivated). app.dispose() ile serbest bırakılır. |
Adlandırılmış RouterView
Bir rotanın bileşeni varsayılan olarak üst rotanın default adlı RouterView'ına yerleşir; başka bir çıktıya yerleşmesi için rotada extend.targetOutlet verin. Bir layout birden çok RouterView taşıyabilir; adı verilmeyen çıktı default'tur. Örnekte ikinci çıktının adı page:
<RouterView />
<RouterView name="page" />
const folders = motifComponent(RouterView);
const page = motifComponent(RouterView, { name: "page" });
<RouterView />
<RouterView name="page" />
const folders = motifComponent(RouterView);
const page = motifComponent(RouterView, { name: "page" });
const settings: RouteItem = { path: "/settings", control: () => import("./pages/SettingsPage"), extend: { targetOutlet: "page" } };
const settings = { path: "/settings", control: () => import("./pages/SettingsPage"), extend: { targetOutlet: "page" } };
Her rota tek bir çıktıya yerleşir; aynı düzeyde bir anda tek rota gösterildiği için /settings açıkken varsayılan çıktı boş kalır, bir klasöre dönülünce page çıktısı boşalır.
app.run'a kök bileşen vermezseniz, MotifJS köke otomatik olarak name: 'default' bir RouterView yerleştirir; rota tablosunun ilk halkası oraya gelir.
Layout'un RouterView'ı henüz kurulmamışsa (örneğin x-wait ile bekleyen bir bölümün içindeyse) çocuk rota bekler ve RouterView kurulduğu anda yerine bağlanır. Geliştirme kipinde 3000 ms içinde kurulmazsa MJX301 uyarısı yazılır; uyarı çoğunlukla layout'un RouterView koymayı unuttuğunu gösterir.
Gezinme bittikten sonra kurulan RouterView de aynı şekilde dolar: layout çıktısını Frame ile değiştirdiğinde ya da {koşul && <RouterView />} ile kapatıp açtığında ekrandaki rotanın bileşeni yeni RouterView'a yerleşir. Bunun için gezinme olmaz; guard'lar, onEntering ve onEnter çalışmaz, onShow çalışır. Eski RouterView ile serbest bırakılan sayfa yeniden kurulur, eski çıktının çıkış geçişi varsa yeni sayfa onu beklemez. keepAlive, hazır örnek, singleton ve scoped sayfalar serbest bırakılmaz; aynı örnek yeni RouterView'a taşınır (onDeactivated, ardından onActivated). RouterView yerine yenisi konmadan kaldırılırsa bu sayfalar bekler, uyarı yazılmaz. Sayfa kurulamazsa MJX304 raporlanır ve hata rotası gösterilir.
Ayrılan sayfa
Rota sayfalarının giriş ve çıkış geçişleri gezinmede de oynar: ayrılan sayfa çıkış geçişini bitirir, sonra gelen sayfa yerleşip girer (bkz. Stil ve Animasyon).
Yeni sayfa, ayrılan sayfanın serbest bırakılmasını beklemez. Router ayrılan sayfayı ekrandan kaldırır, yeni sayfayı yerleştirir ve ayrılan sayfayı yeni sayfa ekrana geldikten sonra serbest bırakır:
- Sayfa ayrılır ayrılmaz bağları ve bileşenlere bağlı
context.on/context.onRouterChangedabonelikleri durur;TransportToile bir yuvaya gönderdiği içerik yuvadan kalkar. Kendi çıkış geçişi olan sayfanın elemanı geçiş bitince, olmayanınki hemen kaldırılır. onDisposing,onDisposedvemotif.setDisposableile bağlanan temizlik işleri (kütüphane bileşenlerinindestroy()çağrısı gibi) yeni sayfa ekrana geldikten sonra çalışır ve gidilen rotayı görür. Uzun süren bir temizlik yeni sayfanın gelişini geciktirmez.- Ayrılan sayfa, kendi gezinmesinin
scopedörneklerini serbest bırakılması bitene kadar kullanır; bu örnekler sayfadan sonra serbest bırakılır (bkz. Yaşam Süreleri ve Kapsamlar). keepAlive, hazır örnek vesingletonsayfalar ile yığında tutulan sayfalar serbest bırakılmaz, outlet'ten ayrılır.- Bir gezinme bitmeden yenisi başlarsa, yarıda kalan gezinmenin kurup gösteremediği sayfa serbest bırakılır; o rotaya sonra gidildiğinde sayfa yeniden kurulur.
keepAlive, hazır örnek vesingletonsayfalar korunur. app.navigate'in sonucu veonRouterChanged, ayrılan sayfa serbest bırakılmadan gelir.app.dispose()serbest bırakılmayı bekleyen sayfaları da serbest bırakır.
Sonraki adım
Sayfalar arasında bağlantı kurmak için Bağlantılar bölümüne geçin.