Bağımlılık Enjeksiyonu

Bağımlılık Enjeksiyonuna Giriş

Servislerin kaydedilip bileşenlerden çözümlenmesi; kayıttan kullanıma tam örnek ve DI hata kodları; JSX ile ve JSX'siz.

MotifJS bütünleşik bir bağımlılık enjeksiyonu (DI) sistemi içerir. Servisleri uygulama başlarken kaydeder, sonra bileşenlerden veya başka servislerden çözümlersiniz (resolve). Servis, bileşenler arasında paylaşılan durumu ve işi tutan bir sınıf ya da değerdir. Bileşen servisi kendisi kurmaz, sağlayıcıdan (ServiceProvider) ister; sağlayıcı kaydın yaşam süresine göre ya aynı örneği ya da yeni bir örnek verir.

Kayıttan kullanıma

Servisler Application.CreateBuilder()'ın services koleksiyonuna, build()'den önce kaydedilir. addSingleton(token, { useClass }) sınıfı tek örnekli kaydeder; startup: true verilen singleton bir bileşen istemeden app.run() başında oluşturulur. Örnekteki iki kayıt (Bileşenler sayfasındaki NotesService ve SettingsService):

import { Application } from "@motifx/core";
import { NotesService } from "./services/notes";
import { SettingsService } from "./services/settings";

const builder = Application.CreateBuilder();
builder.services.addSingleton(NotesService, { useClass: NotesService });
builder.services.addSingleton(SettingsService, { useClass: SettingsService, startup: true });
const app = builder.build();
import { Application } from "@motifx/core";
import { NotesService } from "./services/notes";
import { SettingsService } from "./services/settings";

const builder = Application.CreateBuilder();
builder.services.addSingleton(NotesService, { useClass: NotesService });
builder.services.addSingleton(SettingsService, { useClass: SettingsService, startup: true });
const app = builder.build();

Bileşen kayıtlı bir servisi sağlayıcıdan alır: sınıf bileşeni ve Options nesnesi this.getService(token), fonksiyon bileşeni FromService(token) kullanır. Servisin reaktif durumu bir getter içinde okunduğunda bağ, durum değiştikçe yenilenir. Örnekteki FolderBadge bir klasördeki not sayısını gösterir:

import { Component } from "@motifx/core";
import { NotesService, type Folder } from "../services/notes";

export class FolderBadge extends Component<HTMLSpanElement, { folder: Folder }> {
  notes = this.getService(NotesService)!;

  override onConfigured() {
    this.class.add("badge");
  }

  override view() {
    return <>{() => this.notes.count(this.props.folder)}</>;
  }
}
import { FromService } from "@motifx/core";
import { NotesService, type Folder } from "../services/notes";

export function FolderBadge(props: { folder: Folder }) {
  const notes = FromService(NotesService)!;
  return <span class="badge">{() => notes.count(props.folder)}</span>;
}
import { motifOptions } from "@motifx/core";
import { NotesService, type Folder } from "../services/notes";

export const FolderBadge = (props: { folder: Folder }) => motifOptions({
  el: "span",

  get notes() {
    return this.getService(NotesService)!;
  },

  onConfigured() {
    this.class.add("badge");
  },

  view() {
    return <>{() => this.notes.count(props.folder)}</>;
  },
});
import { Component, ComponentBase, motifComponent } from "@motifx/core";
import { NotesService, type Folder } from "../services/notes";

export class FolderBadge extends Component<HTMLSpanElement, { folder: Folder }> {
  static override elementTag = "span";
  notes = this.getService(NotesService)!;

  override onConfigured() {
    this.class.add("badge");
  }

  override view() {
    return motifComponent("text", (node: ComponentBase) => node.bindings.text(() => this.notes.count(this.props.folder)));
  }
}
import { ComponentBase, FromService, motifComponent } from "@motifx/core";
import { NotesService, type Folder } from "../services/notes";

export function FolderBadge(props: { folder: Folder }) {
  const notes = FromService(NotesService)!;
  return motifComponent("span", (badge: ComponentBase) => {
    badge.class.add("badge");
    badge.controls.add(motifComponent("text", (node: ComponentBase) => node.bindings.text(() => notes.count(props.folder))));
  });
}
import { ComponentBase, motifComponent, motifOptions } from "@motifx/core";
import { NotesService, type Folder } from "../services/notes";

export const FolderBadge = (props: { folder: Folder }) => motifOptions({
  el: "span",

  get notes() {
    return this.getService(NotesService)!;
  },

  onConfigured() {
    this.class.add("badge");
  },

  view() {
    return motifComponent("text", (node: ComponentBase) => node.bindings.text(() => this.notes.count(props.folder)));
  },
});
import { Component } from "@motifx/core";
import { NotesService } from "../services/notes";

export class FolderBadge extends Component {
  static elementTag = "span";
  notes = this.getService(NotesService);

  onConfigured() {
    this.class.add("badge");
  }

  view() {
    return <>{() => this.notes.count(this.props.folder)}</>;
  }
}
import { FromService } from "@motifx/core";
import { NotesService } from "../services/notes";

export function FolderBadge(props) {
  const notes = FromService(NotesService);
  return <span class="badge">{() => notes.count(props.folder)}</span>;
}
import { motifOptions } from "@motifx/core";
import { NotesService } from "../services/notes";

export const FolderBadge = (props) => motifOptions({
  el: "span",

  get notes() {
    return this.getService(NotesService);
  },

  onConfigured() {
    this.class.add("badge");
  },

  view() {
    return <>{() => this.notes.count(props.folder)}</>;
  },
});
import { Component, motifComponent } from "@motifx/core";
import { NotesService } from "../services/notes";

export class FolderBadge extends Component {
  static elementTag = "span";
  notes = this.getService(NotesService);

  onConfigured() {
    this.class.add("badge");
  }

  view() {
    return motifComponent("text", (node) => node.bindings.text(() => this.notes.count(this.props.folder)));
  }
}
import { FromService, motifComponent } from "@motifx/core";
import { NotesService } from "../services/notes";

export function FolderBadge(props) {
  const notes = FromService(NotesService);
  return motifComponent("span", (badge) => {
    badge.class.add("badge");
    badge.controls.add(motifComponent("text", (node) => node.bindings.text(() => notes.count(props.folder))));
  });
}
import { motifComponent, motifOptions } from "@motifx/core";
import { NotesService } from "../services/notes";

export const FolderBadge = (props) => motifOptions({
  el: "span",

  get notes() {
    return this.getService(NotesService);
  },

  onConfigured() {
    this.class.add("badge");
  },

  view() {
    return motifComponent("text", (node) => node.bindings.text(() => this.notes.count(props.folder)));
  },
});

Bileşen etiketiyle kullanılır:

<RouterLink to="/notes" activeClass="active">Tüm notlar <FolderBadge folder="notes" /></RouterLink>
const all = motifComponent(RouterLink, {
  to: "/notes",
  activeClass: "active",
  childs: [motifComponent("text", (node: ComponentBase) => node.setText("Tüm notlar ")), motifComponent(FolderBadge, { folder: "notes" })],
});
<RouterLink to="/notes" activeClass="active">Tüm notlar <FolderBadge folder="notes" /></RouterLink>
const all = motifComponent(RouterLink, {
  to: "/notes",
  activeClass: "active",
  childs: [motifComponent("text", (node) => node.setText("Tüm notlar ")), motifComponent(FolderBadge, { folder: "notes" })],
});

Singleton kaydedilmiş bir servisi isteyen her bileşen aynı örneği alır. Servis durumunu reactive bir nesnede tutuyorsa bir bileşenin yaptığı değişiklik (örneğin notes.create()) o durumu okuyan bütün bağları yeniler; ayrıca bir olay ya da bildirim gerekmez.

Fonksiyon bileşeninin this'i olmadığı için FromService servisi uygulamanın sağlayıcısından alır; scoped bir servis için bu da güncel gezinmenin handle'ını verir. Ayrıntılar Servis Çözümleme, kaydın diğer biçimleri ve @Injectable ile otomatik kayıt Servis Kaydı, bir servisin ne kadar yaşadığı Yaşam Süreleri ve Kapsamlar sayfasındadır.

DI hata kodları

Fırlatılan kodlar çağrıyı yapan satırda hata olarak gelir; uyarılar yalnız geliştirme kipinde konsola ve DevTools uyarı listesine yazılır, errorHandler dinleyicilerine gitmez (bkz. Hata Yönetimi). Bütün kodlar Hata ve Uyarı Kodları sayfasındadır:

Kod Ne zaman
MJX401 Kayıtlı olmayan bir token get, getAsync ya da inject ile istendi; fırlatılır.
MJX402 Async fabrika ya da Promise ile kaydedilmiş bir servis get ile istendi (startup servis dahil); fırlatılır, getAsync kullanılır.
MJX403 Kayıt tanımı geçersiz; fırlatılır.
MJX404 Döngüsel bağımlılık; fırlatılır.
MJX405 İkinci bir ApplicationBuilder kuruldu; fırlatılır.
MJX406 app.run seçiciyle verilen host elemanını bulamadı; fırlatılır.
MJX407 getService bileşen için bir sağlayıcı bulamadı; geliştirme kipinde uyarı, null döner.
MJX408 getService çözümlemesi hata verdi; geliştirme kipinde uyarı, null döner.
MJX409 inject bir servis kurulumunun dışında çağrıldı; fırlatılır.
MJX410 Bir kayıtta birden çok kaynak verildi; geliştirme kipinde uyarı, öncelik useValue > useFactory > useClass.
MJX411 deps bir dizi değil; geliştirme kipinde uyarı, geçersiz deps yok sayılır.
MJX413 Kurulamayan bir fonksiyon (ok fonksiyonu, async fonksiyon, nesne metodu) sınıf olarak kaydedildi; kayıt satırında fırlatılır.
MJX414 FromService çözümleyemedi; geliştirme kipinde uyarı, null döner.
MJX415 startup singleton olmayan bir kayda verildi; geliştirme kipinde uyarı, servis ilk çözümlemede oluşturulur.

Sonraki adım

Servislerin nasıl kaydedildiğini öğrenmek için Servis Kaydı bölümüne geçin.