Subject

Subject הוא סוג של Observable שמאפשר תקשורת בין קומפוננטות. נניח שיש לנו שתי קומפוננטות בעמוד. שתי הקומפוננטות לא מקושרות, אין ביניהם יחסים של אב ובן. אנחנו רוצים שכשהמשתמש יכניס ערך בקומפוננטה אחת, המידע יעבור לקומפוננטה השנייה.

אפשר להשתמש ב-services ואפשר להשתמש בטריגר של אירועים.

בניית הקומפוננטות להעברת המידע

קובץ comp1.html

<div class="mb-5">
  <input class="form-control mb-3" type="text">
  <button class="btn btn-primary">Send</button>
</div>

קובץ comp2.html

<div>
  <h3>You Entered <i>Content</i></h3>
</div>

אנחנו רוצים להכניס ערך בתיבת הטקסט של comp1 ולראות את הערך הזה ב-comp2 במקום הכיתוב content.

ניצור משתנה ב-comp2 שיכיל את ערך הטקסט:

קובץ comp2.ts

export class Comp2Component {
  inputText: string;
}

את ערך המשתנה הזה נציג על המסך.

קובץ comp2.html

<div>
  <h3>You Entered <i>{{ inputText }}</i></h3>
</div>

ב-comp1 ניצור משתנה שיכיל את מה שמכניסים בתיבת הטקסט ונחבר אותו לטופס עם two way binding.

קובץ comp1.ts

export class Comp1Component {
  enteredText: string;
}

קובץ comp1.html

<div class="mb-5">
  <input
    class="form-control mb-3"
    type="text"
    [(ngModel)]="enteredText"
  >
  <button class="btn btn-primary">Send</button>
</div>

כדי להשתמש ב-ngModel צריך לייבא ל-app.module את ספריית FormsModule.

נחבר פונקציה ללחיצה על הכפתור. כרגע היא רק שולחת לקונסול את מה שיש בתיבת הטקסט.

קובץ comp1.html

<button class="btn btn-primary" (click)="onBtnClick()">Send</button>

קובץ comp1.ts

onBtnClick(){
    console.log(this.enteredText);
}

המטרה שלנו היא להעביר את הערך הזה מ-comp1 ל-comp2. כדי לעשות את זה ניצור servuce חדש.

ng g s services/data

בתוך ה-service נגדיר אירוע. האירוע הזה יקפיץ לנו אירוע מסוג string.

שימוש באירוע להעברת מידע

קובץ data.service.ts

dataEmitter = new EventEmitter<string>();

הצעד הבא הוא ליצור פונקציה שתקפיץ את האירוע הזה. עכשיו יש לנו אירוע ופונקציה שמקפיצה אותו.

export class DataService {
  dataEmitter = new EventEmitter<string>();

  raiseDataEmitterEvent(data: string){
    this.dataEmitter.emit(data);
  }
}

נזריק את ה-service ל-comp1 ובמקום להדפיס את התוכן של תיבת הטקסט, נחבר את הערך לפונקציה שמייצרת את האירוע ב-service. כשתהיה לחיצה על הכפתור, תהיה הקפצה של האירוע עם הערך שהכנסנו בתיבת הטקסט.

קובץ comp1.ts

export class Comp1Component {
  enteredText: string;

  constructor(private dataService: DataService){}

  onBtnClick(){
    this.dataService.raiseDataEmitterEvent(this.enteredText);
  }
}

עכשיו נלך ל-comp2 ונרשם לקבלת עדכונים מהאירוע הזה. לפונקציה subscribe יש פונקציית callback בשם next. והיא מקבלת את הערך שה-observable הקפיץ. נחבר אותו למשתנה שיש לנו ב-comp2.

קובץ comp2.ts

export class Comp2Component {
  inputText: string;

  constructor(private dataService: DataService){}

  ngOnInit(){
    this.dataService.dataEmitter.subscribe((value) => {
      this.inputText = value;
    });
  }
}

עכשיו כשנכניס ערך בתיבת הטקסט של comp1 ונלחץ על אישור, נקבל את הערך מודפס ב-comp2.

שימוש ב-subject להעברת מידע

אפשר להשיג את אותה התוצאה על ידי שימוש ב-subject.

ניצור subject חדש ב-service שלנו. ה-subject מחזיר observable, נשים אותו בתוך משתנה. במקום הפונקציה emit שהשתמשנו בה לאירוע, אנחנו צריכים להשתמש בפונקציה next. עוד משהו שצריך להוסיף זה סוג המידע שה-observable מחזיר לנו כשיש טריגר של ה-subject. במקרה שלנו זה string.

קובץ data.service.ts

export class DataService {
  //dataEmitter = new EventEmitter<string>();
  dataEmitter = new Subject<string>();

  raiseDataEmitterEvent(data: string){
    this.dataEmitter.next(data);
  }
}

נראה שהעמוד שלנו עובד כמו שצריך והפונקציונליות עדיין עובדת.

כאן העברנו את המידע לקומפוננטה אחת, אבל אפשר לרשום עוד קומפוננטות שמקבלות את המידע כשהוא מגיע.