Bağımlılık Enjeksiyonu
Yaşam Süreleri ve Kapsamlar
singleton, scoped ve transient; gezinme kapsamı ve scoped handle'lar.
Yaşam süreleri (lifetimes)
| Yaşam süresi | Davranış |
|---|---|
singleton |
Uygulama boyunca tek örnek. |
scoped |
Gezinme başına bir örnek. Aynı gezinmede ekranda olan her şey (layout, sayfa, alt bileşenler, FromService, singleton bağımlılıkları) aynı örneği görür; ayrıntı aşağıda. Router yoksa kök sağlayıcıda tek örnektir. |
transient |
Her çözümlemede yeni örnek (varsayılan). |
Yaşam süresi kayıtta verilir (addSingleton / addScoped / addTransient ya da @Injectable({ lifetime }), bkz. Servis Kaydı). Bir rota bileşeninin kendisi DI'da kayıtlıysa kaydın yaşam süresi sayfanın ömrünü belirler (bkz. Rota bileşeninin kurulması).
scoped: gezinme başına tek örnek
Router her gezinmede bir kapsam açar. scoped bir servisi isteyen herkes (getService, inject, deps, FromService) servisin kendisini değil, o servise giden bir handle alır. Handle servis gibi kullanılır (alanlar, metotlar, #private alanlar, instanceof) ve her erişimde doğru gezinmenin örneğine gider. Örnekte layout ile sayfa aynı scoped seçimi kullanır:
import { Injectable, reactive } from "@motifx/core";
@Injectable({ lifetime: "scoped" })
export class Selection {
ids = reactive<number[]>([]);
toggle(id: number) {
const i = this.ids.indexOf(id);
if (i < 0) this.ids.push(id);
else this.ids.splice(i, 1);
}
}
import { Injectable, reactive } from "@motifx/core";
@Injectable({ lifetime: "scoped" })
export class Selection {
ids = reactive([]);
toggle(id) {
const i = this.ids.indexOf(id);
if (i < 0) this.ids.push(id);
else this.ids.splice(i, 1);
}
}
Layout'taki sayaç ve not sayfasındaki düğme aynı servisi ister:
import { Component } from "@motifx/core";
import { Selection } from "../services/selection";
import type { Note } from "../services/notes";
export class SelectedCount extends Component {
selection = this.getService(Selection)!;
override view() {
return <span class="badge">{() => `${this.selection.ids.length} seçili`}</span>;
}
}
export class SelectButton extends Component<any, { note: Note }> {
selection = this.getService(Selection)!;
override view() {
const { note } = this.props;
return (
<button type="button" class="button--sm ghost" onclick={() => this.selection.toggle(note.id)}>
Seç
</button>
);
}
}
import { Component, ComponentBase, motifComponent } from "@motifx/core";
import { Selection } from "../services/selection";
import type { Note } from "../services/notes";
export class SelectedCount extends Component {
selection = this.getService(Selection)!;
override view() {
return motifComponent("span", (span: ComponentBase) => {
span.class.add("badge");
span.bindings.text(() => `${this.selection.ids.length} seçili`);
});
}
}
export class SelectButton extends Component<any, { note: Note }> {
selection = this.getService(Selection)!;
override view() {
const { note } = this.props;
return motifComponent("button", (button: ComponentBase) => {
button.attr.add({ type: "button" });
button.class.add("button--sm ghost");
button.motif.on("click", () => this.selection.toggle(note.id));
button.setText("Seç");
});
}
}
import { FromService } from "@motifx/core";
import { Selection } from "../services/selection";
import type { Note } from "../services/notes";
export function SelectedCount() {
const selection = FromService(Selection)!;
return <span class="badge">{() => `${selection.ids.length} seçili`}</span>;
}
export function SelectButton(props: { note: Note }) {
const selection = FromService(Selection)!;
const { note } = props;
return (
<button type="button" class="button--sm ghost" onclick={() => selection.toggle(note.id)}>
Seç
</button>
);
}
import { ComponentBase, FromService, motifComponent } from "@motifx/core";
import { Selection } from "../services/selection";
import type { Note } from "../services/notes";
export function SelectedCount() {
const selection = FromService(Selection)!;
return motifComponent("span", (span: ComponentBase) => {
span.class.add("badge");
span.bindings.text(() => `${selection.ids.length} seçili`);
});
}
export function SelectButton(props: { note: Note }) {
const selection = FromService(Selection)!;
const { note } = props;
return motifComponent("button", (button: ComponentBase) => {
button.attr.add({ type: "button" });
button.class.add("button--sm ghost");
button.motif.on("click", () => selection.toggle(note.id));
button.setText("Seç");
});
}
import { motifOptions } from "@motifx/core";
import { Selection } from "../services/selection";
import type { Note } from "../services/notes";
export const SelectedCount = () => motifOptions({
el: null,
view() {
const selection = this.getService(Selection)!;
return <span class="badge">{() => `${selection.ids.length} seçili`}</span>;
},
});
export const SelectButton = (props: { note: Note }) => motifOptions({
el: null,
view() {
const selection = this.getService(Selection)!;
const { note } = props;
return (
<button type="button" class="button--sm ghost" onclick={() => selection.toggle(note.id)}>
Seç
</button>
);
},
});
import { ComponentBase, motifComponent, motifOptions } from "@motifx/core";
import { Selection } from "../services/selection";
import type { Note } from "../services/notes";
export const SelectedCount = () => motifOptions({
el: null,
view() {
const selection = this.getService(Selection)!;
return motifComponent("span", (span: ComponentBase) => {
span.class.add("badge");
span.bindings.text(() => `${selection.ids.length} seçili`);
});
},
});
export const SelectButton = (props: { note: Note }) => motifOptions({
el: null,
view() {
const selection = this.getService(Selection)!;
const { note } = props;
return motifComponent("button", (button: ComponentBase) => {
button.attr.add({ type: "button" });
button.class.add("button--sm ghost");
button.motif.on("click", () => selection.toggle(note.id));
button.setText("Seç");
});
},
});
import { Component } from "@motifx/core";
import { Selection } from "../services/selection";
export class SelectedCount extends Component {
selection = this.getService(Selection);
view() {
return <span class="badge">{() => `${this.selection.ids.length} seçili`}</span>;
}
}
export class SelectButton extends Component {
selection = this.getService(Selection);
view() {
const { note } = this.props;
return (
<button type="button" class="button--sm ghost" onclick={() => this.selection.toggle(note.id)}>
Seç
</button>
);
}
}
import { Component, motifComponent } from "@motifx/core";
import { Selection } from "../services/selection";
export class SelectedCount extends Component {
selection = this.getService(Selection);
view() {
return motifComponent("span", (span) => {
span.class.add("badge");
span.bindings.text(() => `${this.selection.ids.length} seçili`);
});
}
}
export class SelectButton extends Component {
selection = this.getService(Selection);
view() {
const { note } = this.props;
return motifComponent("button", (button) => {
button.attr.add({ type: "button" });
button.class.add("button--sm ghost");
button.motif.on("click", () => this.selection.toggle(note.id));
button.setText("Seç");
});
}
}
import { FromService } from "@motifx/core";
import { Selection } from "../services/selection";
export function SelectedCount() {
const selection = FromService(Selection);
return <span class="badge">{() => `${selection.ids.length} seçili`}</span>;
}
export function SelectButton(props) {
const selection = FromService(Selection);
const { note } = props;
return (
<button type="button" class="button--sm ghost" onclick={() => selection.toggle(note.id)}>
Seç
</button>
);
}
import { FromService, motifComponent } from "@motifx/core";
import { Selection } from "../services/selection";
export function SelectedCount() {
const selection = FromService(Selection);
return motifComponent("span", (span) => {
span.class.add("badge");
span.bindings.text(() => `${selection.ids.length} seçili`);
});
}
export function SelectButton(props) {
const selection = FromService(Selection);
const { note } = props;
return motifComponent("button", (button) => {
button.attr.add({ type: "button" });
button.class.add("button--sm ghost");
button.motif.on("click", () => selection.toggle(note.id));
button.setText("Seç");
});
}
import { motifOptions } from "@motifx/core";
import { Selection } from "../services/selection";
export const SelectedCount = () => motifOptions({
el: null,
view() {
const selection = this.getService(Selection);
return <span class="badge">{() => `${selection.ids.length} seçili`}</span>;
},
});
export const SelectButton = (props) => motifOptions({
el: null,
view() {
const selection = this.getService(Selection);
const { note } = props;
return (
<button type="button" class="button--sm ghost" onclick={() => selection.toggle(note.id)}>
Seç
</button>
);
},
});
import { motifComponent, motifOptions } from "@motifx/core";
import { Selection } from "../services/selection";
export const SelectedCount = () => motifOptions({
el: null,
view() {
const selection = this.getService(Selection);
return motifComponent("span", (span) => {
span.class.add("badge");
span.bindings.text(() => `${selection.ids.length} seçili`);
});
},
});
export const SelectButton = (props) => motifOptions({
el: null,
view() {
const selection = this.getService(Selection);
const { note } = props;
return motifComponent("button", (button) => {
button.attr.add({ type: "button" });
button.class.add("button--sm ghost");
button.motif.on("click", () => selection.toggle(note.id));
button.setText("Seç");
});
},
});
Layout sayacı <SelectedCount />, not sayfası düğmeyi <SelectButton note={note} /> ile yerleştirir.
Layout sayacı new SelectedCount(), not sayfası düğmeyi new SelectButton({ note }) ile yerleştirir.
Layout sayacı motifComponent(SelectedCount), not sayfası düğmeyi motifComponent(SelectButton, { note }) ile yerleştirir.
- Sayfanın yaptığı değişikliği layout aynı anda görür; ikisi aynı gezinmenin örneğindedir.
- Başka bir sayfaya geçildiğinde layout'un handle'ı kendiliğinden yeni gezinmenin örneğine geçer ve sayacın bağı yenilenir. Önceki gezinmenin örneği, ayrılan sayfa serbest bırakıldıktan sonra serbest bırakılır (
dispose()). - Ayrılan sayfa önceki gezinmenin örneğine bağlı kalır; örnek, sayfa serbest bırakılırken (
onDisposing,onDisposed) de canlıdır. Sayfa ayrıldıktan sonra tamamlanan bir iş (awaitsonrası) yeni sayfanın servisine değil, kendi gezinmesinin örneğine yazar. Aynı rotada parametre değişimi (/notes/1→/notes/2) de yeni bir gezinmedir. keepAlivebir sayfa önbellekte beklerken kendi gezinmesinin örneğini yaşatır; yeniden gösterildiğinde,onActivatedçalışmadan önce güncel gezinmenin örneğine geçer, bu yüzdenonActivatediçinde servise yazılan değer güncel örnekte kalır. Mobil yığında bekleyen sayfalar da aynı kurala uyar. Servisteki durumun gezinmeler arasında korunması isteniyorsa servissingletonolmalıdır.- Geri ve ileri gezinme de yeni bir gezinmedir, yeni örnek açar.
- Gezinme gerçekleşmezse (guard iptali, sayfa kurulum hatası) güncel örnek değişmez; açılan kapsam serbest bırakılır.
- Sayfa kurulurken (yapıcıda ya da yapıcının başlattığı işte) yeni gezinmenin örneği kullanılır.
app.provider.createScope()ile koddan açılan bir kapsam bu düzenin dışındadır: servisin kendisini verir ve yalnız o kapsamda yaşar. Böyle bir kapsamda üretilen transient örneklerin kapsamla birlikte serbest bırakılması için bkz. Servislerin Serbest Bırakılması.- Handle bileşen başına ayrı bir nesnedir: iki bileşenin aldığı handle'lar aynı örneğe gitse de
===ile eşit değildir; servisleri içerikleriyle karşılaştırın.
Serbest bırakılan bir bileşenin alanları temizlendiği için, uzun süren bir işte bileşenin isDisposed alanını kontrol etmek yine iyi bir alışkanlıktır.
Sonraki adım
Servislerin nereden ve nasıl alındığını öğrenmek için Servis Çözümleme bölümüne geçin.