Temel kavramlar
Durum, Bağlam ve Servisler
Bileşenin durum bayrakları, reaktif durumu ve `setState`; uygulamaya (`context`) ve servislere erişim; `build` ve `dispose`.
Durum bayrakları
Bileşen yaşam döngüsünün neresinde olduğunu bayraklarla bildirir. Bayrakları MotifJS yönetir; kodunuz okur, yazmaz.
| Bayrak | Ne zaman true |
|---|---|
isInitialized |
Yapıcı bitince. |
isConfigured |
Yapılandırma evresi geçince: düz Component'te yapıcıda, sınıf bileşeninde kurulumun başında. |
isBuilt |
Bileşen kurulunca (build()). |
isVisible |
Görünürken. motif.hide() ile false olur. |
isWait |
Bileşen beklerken: x-wait ya da bindings.wait koşulu sürerken ve x-display koşulu false iken. Bekleyen bileşen kurulmaz. |
isDisposed |
Serbest bırakılınca. Bundan sonra bileşenin metotları bir şey yapmaz. |
x-display ile motif.hide() ayrı bayraklara dokunur: x-display koşulu false olan bileşen isWait'tir ve kurulmamış olabilir; motif.hide() ile gizlenen bileşen kurulmuştur, yalnız isVisible'ı false'tur (bkz. Görünürlük).
Bileşenin durumu
Bileşenin değişen verisi reaktif bir nesnede tutulur: sınıfta bir alan (state = reactive({ … })), fonksiyon bileşeninde yerel bir değişken, Options'ta data alanı. Bu nesneyi okuyan bağlar, nesne değişince kendiliğinden güncellenir (bkz. Bileşende durum). this.useModel(model), reactive(model) ile aynıdır.
Reaktif olmayan bir veri değiştiyse bağlar bunu kendiliğinden görmez. O durumda bağlar elle yeniden okutulur:
setState()önce çocukların, sonra bileşenin kendi bağlarını yeniden çalıştırır.reState()önce bileşenin kendi bağlarını, sonra çocuklarınkini yeniden çalıştırır.
İkisi de bileşeni yeniden kurmaz: view() yeniden çağrılmaz, childs yuvası değişmez, yalnız bağlar değerlerini yeniden okur.
Bağlam (context) — uygulamaya erişim
Her bileşen, ebeveyn zincirini tırmanarak uygulamaya context ile ulaşır (sınıfta ve Options'ta this.context, fonksiyon bileşeninde kök bileşenin context'i); ebeveyni henüz olmayan bileşen de (yapıcıda, onInitializing'de) çalışan uygulamaya ulaşır. context, çalışan Application'ın bileşene bağlı bir görünümüdür; üyeleri uygulamanınkidir: navigate, on, fire, onRouterChanged …
context üzerinden açılan on ve onRouterChanged abonelikleri bileşenin ömrüne bağlıdır; bileşen serbest bırakılınca kendiliğinden kalkar. onLifecycle için bu geçerli değildir; dönen iptal fonksiyonunu bileşenin motif.setDisposable(off)'u ile bağlayın (sınıfta ve Options'ta this.motif, fonksiyon bileşeninde kök bileşenin motif'i). Uygulama olayları için bkz. Olaylar.
Fonksiyon bileşeninde this yoktur. Aynı işler kökün ya da bir kancanın aldığı bileşenin context'iyle (sender.context) ya da useNavigation() ile yapılır; useNavigation().navigate(adres) context.navigate ile aynı gezintiyi yapar.
Servislere erişim
DI ile kayıtlı servislere iki yoldan ulaşılır. getService her bileşende vardır: sınıf ve Options bileşeninde this.getService(T), fonksiyon bileşeninde köke yazılan bir kancanın (örneğin onConfig) aldığı sender ile sender.getService(T). FromService(T) her yerde, fonksiyon bileşeninin gövdesinde de çalışır. Singleton serviste ikisi aynı örneği verir; scoped serviste getService bileşenin kendi sağlayıcısına bağlıdır, FromService uygulamanın güncel sağlayıcısından çözer (bkz. Dependency Injection).
Not listesinin başlığı klasörün adını ve not sayısını gösterir; "Yeni not" düğmesi servisten bir not yaratır ve uygulamayı o nota götürür:
import { Component } from "@motifx/core";
import { FOLDERS, NotesService, type Folder } from "../services/notes";
export class NotesHead extends Component<HTMLDivElement, { folder: Folder }> {
notes = this.getService(NotesService)!;
create() {
const note = this.notes.create();
this.context.navigate(`/notes/${note.id}`);
}
override onConfigured() {
this.class.add("list__head");
}
override view() {
const { folder } = this.props;
return (
<>
<h2>
{FOLDERS[folder].title} <span class="badge">{() => String(this.notes.count(folder))}</span>
</h2>
<button type="button" class="button--sm accent" onclick={() => this.create()}>
Yeni not
</button>
</>
);
}
}
import { Component, ComponentBase, motifComponent, motifFragment } from "@motifx/core";
import { FOLDERS, NotesService, type Folder } from "../services/notes";
export class NotesHead extends Component<HTMLDivElement, { folder: Folder }> {
static override elementTag = "div";
notes = this.getService(NotesService)!;
create() {
const note = this.notes.create();
this.context.navigate(`/notes/${note.id}`);
}
override onConfigured() {
this.class.add("list__head");
}
override view() {
const { folder } = this.props;
const title = motifComponent("h2", (h2: ComponentBase) => {
const badge = motifComponent("span", (span: ComponentBase) => {
span.class.add("badge");
span.bindings.text(() => String(this.notes.count(folder)));
});
h2.controls.add(motifComponent("text", (node: ComponentBase) => node.setText(`${FOLDERS[folder].title} `)), badge);
});
const create = motifComponent("button", (button: ComponentBase) => {
button.attr.add({ type: "button" });
button.class.add("button--sm accent");
button.motif.on("click", () => this.create());
button.setText("Yeni not");
});
return motifFragment({ childs: [title, create] });
}
}
import { FromService, useNavigation } from "@motifx/core";
import { FOLDERS, NotesService, type Folder } from "../services/notes";
export function NotesHead(props: { folder: Folder }) {
const { folder } = props;
const notes = FromService(NotesService)!;
const nav = useNavigation();
const create = () => {
const note = notes.create();
nav.navigate(`/notes/${note.id}`);
};
return (
<div class="list__head">
<h2>
{FOLDERS[folder].title} <span class="badge">{() => String(notes.count(folder))}</span>
</h2>
<button type="button" class="button--sm accent" onclick={create}>
Yeni not
</button>
</div>
);
}
import { ComponentBase, FromService, motifComponent, useNavigation } from "@motifx/core";
import { FOLDERS, NotesService, type Folder } from "../services/notes";
export function NotesHead(props: { folder: Folder }) {
const { folder } = props;
const notes = FromService(NotesService)!;
const nav = useNavigation();
const create = () => {
const note = notes.create();
nav.navigate(`/notes/${note.id}`);
};
return motifComponent("div", (head: ComponentBase) => {
head.class.add("list__head");
const title = motifComponent("h2", (h2: ComponentBase) => {
const badge = motifComponent("span", (span: ComponentBase) => {
span.class.add("badge");
span.bindings.text(() => String(notes.count(folder)));
});
h2.controls.add(motifComponent("text", (node: ComponentBase) => node.setText(`${FOLDERS[folder].title} `)), badge);
});
const button = motifComponent("button", (b: ComponentBase) => {
b.attr.add({ type: "button" });
b.class.add("button--sm accent");
b.motif.on("click", create);
b.setText("Yeni not");
});
head.controls.add(title, button);
});
}
import { motifOptions } from "@motifx/core";
import { FOLDERS, NotesService, type Folder } from "../services/notes";
export const NotesHead = (props: { folder: Folder }) => motifOptions({
el: "div",
get notes() {
return this.getService(NotesService)!;
},
create() {
const note = this.notes.create();
this.context.navigate(`/notes/${note.id}`);
},
onConfigured() {
this.class.add("list__head");
},
view() {
const { folder } = props;
return (
<>
<h2>
{FOLDERS[folder].title} <span class="badge">{() => String(this.notes.count(folder))}</span>
</h2>
<button type="button" class="button--sm accent" onclick={() => this.create()}>
Yeni not
</button>
</>
);
},
});
import { ComponentBase, motifComponent, motifFragment, motifOptions } from "@motifx/core";
import { FOLDERS, NotesService, type Folder } from "../services/notes";
export const NotesHead = (props: { folder: Folder }) => motifOptions({
el: "div",
get notes() {
return this.getService(NotesService)!;
},
create() {
const note = this.notes.create();
this.context.navigate(`/notes/${note.id}`);
},
onConfigured() {
this.class.add("list__head");
},
view() {
const { folder } = props;
const title = motifComponent("h2", (h2: ComponentBase) => {
const badge = motifComponent("span", (span: ComponentBase) => {
span.class.add("badge");
span.bindings.text(() => String(this.notes.count(folder)));
});
h2.controls.add(motifComponent("text", (node: ComponentBase) => node.setText(`${FOLDERS[folder].title} `)), badge);
});
const create = motifComponent("button", (button: ComponentBase) => {
button.attr.add({ type: "button" });
button.class.add("button--sm accent");
button.motif.on("click", () => this.create());
button.setText("Yeni not");
});
return motifFragment({ childs: [title, create] });
},
});
import { Component } from "@motifx/core";
import { FOLDERS, NotesService } from "../services/notes";
export class NotesHead extends Component {
static elementTag = "div";
notes = this.getService(NotesService);
create() {
const note = this.notes.create();
this.context.navigate(`/notes/${note.id}`);
}
onConfigured() {
this.class.add("list__head");
}
view() {
const { folder } = this.props;
return (
<>
<h2>
{FOLDERS[folder].title} <span class="badge">{() => String(this.notes.count(folder))}</span>
</h2>
<button type="button" class="button--sm accent" onclick={() => this.create()}>
Yeni not
</button>
</>
);
}
}
import { Component, motifComponent, motifFragment } from "@motifx/core";
import { FOLDERS, NotesService } from "../services/notes";
export class NotesHead extends Component {
static elementTag = "div";
notes = this.getService(NotesService);
create() {
const note = this.notes.create();
this.context.navigate(`/notes/${note.id}`);
}
onConfigured() {
this.class.add("list__head");
}
view() {
const { folder } = this.props;
const title = motifComponent("h2", (h2) => {
const badge = motifComponent("span", (span) => {
span.class.add("badge");
span.bindings.text(() => String(this.notes.count(folder)));
});
h2.controls.add(motifComponent("text", (node) => node.setText(`${FOLDERS[folder].title} `)), badge);
});
const create = motifComponent("button", (button) => {
button.attr.add({ type: "button" });
button.class.add("button--sm accent");
button.motif.on("click", () => this.create());
button.setText("Yeni not");
});
return motifFragment({ childs: [title, create] });
}
}
import { FromService, useNavigation } from "@motifx/core";
import { FOLDERS, NotesService } from "../services/notes";
export function NotesHead(props) {
const { folder } = props;
const notes = FromService(NotesService);
const nav = useNavigation();
const create = () => {
const note = notes.create();
nav.navigate(`/notes/${note.id}`);
};
return (
<div class="list__head">
<h2>
{FOLDERS[folder].title} <span class="badge">{() => String(notes.count(folder))}</span>
</h2>
<button type="button" class="button--sm accent" onclick={create}>
Yeni not
</button>
</div>
);
}
import { FromService, motifComponent, useNavigation } from "@motifx/core";
import { FOLDERS, NotesService } from "../services/notes";
export function NotesHead(props) {
const { folder } = props;
const notes = FromService(NotesService);
const nav = useNavigation();
const create = () => {
const note = notes.create();
nav.navigate(`/notes/${note.id}`);
};
return motifComponent("div", (head) => {
head.class.add("list__head");
const title = motifComponent("h2", (h2) => {
const badge = motifComponent("span", (span) => {
span.class.add("badge");
span.bindings.text(() => String(notes.count(folder)));
});
h2.controls.add(motifComponent("text", (node) => node.setText(`${FOLDERS[folder].title} `)), badge);
});
const button = motifComponent("button", (b) => {
b.attr.add({ type: "button" });
b.class.add("button--sm accent");
b.motif.on("click", create);
b.setText("Yeni not");
});
head.controls.add(title, button);
});
}
import { motifOptions } from "@motifx/core";
import { FOLDERS, NotesService } from "../services/notes";
export const NotesHead = (props) => motifOptions({
el: "div",
get notes() {
return this.getService(NotesService);
},
create() {
const note = this.notes.create();
this.context.navigate(`/notes/${note.id}`);
},
onConfigured() {
this.class.add("list__head");
},
view() {
const { folder } = props;
return (
<>
<h2>
{FOLDERS[folder].title} <span class="badge">{() => String(this.notes.count(folder))}</span>
</h2>
<button type="button" class="button--sm accent" onclick={() => this.create()}>
Yeni not
</button>
</>
);
},
});
import { motifComponent, motifFragment, motifOptions } from "@motifx/core";
import { FOLDERS, NotesService } from "../services/notes";
export const NotesHead = (props) => motifOptions({
el: "div",
get notes() {
return this.getService(NotesService);
},
create() {
const note = this.notes.create();
this.context.navigate(`/notes/${note.id}`);
},
onConfigured() {
this.class.add("list__head");
},
view() {
const { folder } = props;
const title = motifComponent("h2", (h2) => {
const badge = motifComponent("span", (span) => {
span.class.add("badge");
span.bindings.text(() => String(this.notes.count(folder)));
});
h2.controls.add(motifComponent("text", (node) => node.setText(`${FOLDERS[folder].title} `)), badge);
});
const create = motifComponent("button", (button) => {
button.attr.add({ type: "button" });
button.class.add("button--sm accent");
button.motif.on("click", () => this.create());
button.setText("Yeni not");
});
return motifFragment({ childs: [title, create] });
},
});
Not listesi başlığı <NotesHead folder={this.folder} /> ile yerleştirir.
Not listesi başlığı new NotesHead({ folder: this.folder }) ile yerleştirir.
Not listesi başlığı motifComponent(NotesHead, { folder: this.folder }) ile yerleştirir.
build ve dispose
- Bileşeni elle kurmak gerekmez: bileşen kurulmuş bir ebeveyne eklenince, kurulmamış bir ebeveynle birlikte ya da
app.runile kök yapılınca kurulur (bkz. Yaratılma ve kurulma).build()bir kez çalışır; kurulmuş, bekleyen ya da serbest bırakılmış bileşende bir şey yapmaz. dispose()bileşeni ağaçtan çıkarır ve alt ağacıyla birlikte serbest bırakır; çıkış geçişi varsa önce onu oynatır. Bitince çözülen bir söz döndürür; süren bir serbest bırakma sırasında ikinci çağrı aynı sözü verir. Bir ebeveynin çocuğunu kaldırmanın yolucontrols.remove(çocuk)'tur; o da serbest bırakır.- Serbest bırakılan bileşenin bağları,
motif.ondinleyicileri,contextabonelikleri vesetDisposablekayıtları kendiliğinden bırakılır. Serbest bırakıldıktan sonra gelen bir sözün sonucunu kullanmamak için using ve doWork vardır.
Sonraki adım
Serbest bırakıldıktan sonra gelen asenkron sonucu atmak için using ile Asenkron Sonuç bölümüne geçin.