Warum wird mein BehaviorSubject aktualisiert, wenn ich den neuen Wert nicht an die next()-Methode übergebe?
P粉321584263
P粉321584263 2024-03-21 22:58:22
0
1
437

Ich versuche, mit RXJS eine einfache TODO-Anwendung zu erstellen. Ich habe eine JSON-Server-Mock-Datenbank mit TODO-Aufgaben.

Also bin ich bei diesem TasksService gelandet:

@Injectable({
  providedIn: 'root'
})
export class TasksService
{
  private _tasks : ITask[] = [];
  private _tasks$: BehaviorSubject<ITask[]> = new BehaviorSubject<ITask[]>([]);
  constructor (private _http: HttpClient) { }

  public getTasks()
  {
    this.getTasksObservableFromDb().pipe(
      tap(
        (tasks) => 
        {
          this._tasks = tasks;
          this._tasks$.next(tasks);
        }
      )
    ).subscribe();

    return this._tasks$;
  }

  public addTask(task: ITask)
  {
    this._tasks.push(task);
    this._tasks$.next(this._tasks);
  }

  private getTasksObservableFromDb(): Observable<any>
  {
    return this._http.get<any>('http://127.0.0.1:3000/tasks');
  }

Wenn ich Aufgaben hinzufüge, möchte ich sie nicht sofort auf dem Server veröffentlichen. Wenn ich also Aufgaben vom Server erhalte, speichere ich sie in der Eigenschaft _tasks und übergebe sie dann an die Methode next() meines _tasks$:BehaviorSubject . Denn später möchte ich meine Aufgaben stapelweise auf dem Server veröffentlichen und jetzt möchte ich nur noch, dass sie in Angular korrekt angezeigt werden.

In meiner AppComponent erhalte ich die Aufgaben und ordne sie meiner Aufgabeneigenschaft zu.

export class AppComponent implements OnInit
{
  public tasks!:BehaviorSubject<ITask[]>;
  constructor (private _tasksService: TasksService)
  {}
  ngOnInit(): void
  {
    console.log('OnInit');
    this.tasks = this._tasksService.getTasks();
  }

  public addTask()
  {
    this._tasksService.addTask(
      {
        id: crypto.randomUUID(),
        isImportant: true,
        text: 'Added task'
      }
    );
  }
}

In meiner HTML-Vorlage verwende ich asynchrone Pipe als Aufgabenattribut und zeige meine Aufgabe an:

<ng-container *ngFor="let task of tasks | async">
    {{task.text}}
    {{task.id}}
</ng-container>
<button type="button" (click)="addTask()">Add Task</button>

Aber dann habe ich versehentlich diese Zeile in meinem TaskService gelöscht: this._tasks$.next(this._tasks);

Meine Methode sieht jetzt also so aus:

public addTask(task: ITask)
  {
    this._tasks.push(task);
  }

Aber das Hinzufügen von Aufgaben funktioniert immer noch! Auch wenn ich kein Array neuer Aufgaben an mein BehaviorSubject übergebe, zeigt Angular die neu hinzugefügten Aufgaben an.

Also habe ich beschlossen, die Werte in meinen Aufgaben aufzuzeichnen! : BehaviorSubject<ITask[]>-Eigenschaft in meiner AppComponent-Klasse:

public addTask()
  {
    this._tasksService.addTask(
      {
        id: crypto.randomUUID(),
        isImportant: true,
        text: 'Added task'
      }
    );

    this.tasks.pipe(tap((value) => console.log(value)
    )).subscribe();
  }

und Aufgaben werden wie erwartet hinzugefügt – jedes Mal, wenn Sie ein Array erhalten, das eine Aufgabe enthält:

Array(3) [ {…}, {…}, {…} ] <- Add task button is clicked 
Array(4) [ {…}, {…}, {…}, {…} ] <- Add task button is clicked 
Array(5) [ {…}, {…}, {…}, {…}, {…} ] <- Add task button is clicked

Aber wenn ich diese Zeile an die addTask-Methode in TaskService zurückgebe: this._tasks$.next(this._tasks);

Ich habe diese Protokolle erhalten:

Array(3) [ {…}, {…}, {…} ] <- Add task button is clicked -> one task is added
Array(4) [ {…}, {…}, {…}, {…} ] <- Add task button is clicked -> one task is added
Array(4) [ {…}, {…}, {…}, {…} ] <- I get the same array 
Array(5) [ {…}, {…}, {…}, {…}, {…} ] <- Add task button is clicked -> one task is added
Array(5) [ {…}, {…}, {…}, {…}, {…} ] <- I get the same array 
Array(5) [ {…}, {…}, {…}, {…}, {…} ] <- I get the same array once again

Ich weiß also nicht, warum sich das Observable so verhält ... vielleicht verstehe ich die next()-Methode nicht ganz?

P粉321584263
P粉321584263

Antworte allen(1)
P粉080643975

据我了解,这段代码有两个问题。首先,您不知道为什么控制台日志有重复。其次,即使您没有对行为主题调用“.next()”,您也不知道为什么视图会更新。

让我们从第一个开始。

您需要了解 rxjs observables 和 BehaviourSubjects 是如何工作的。 普通的可观察对象,当您订阅它时,将等待发出某个值,然后每次发生时,它都会调用您附加到它的操作。例如:

exampleSubject = new Subject();

ngOnInit(): void {
    this.exampleSubject.pipe(
        tap(console.log)
    ).subscribe();
}

emitValue() {
    this.exampleSubject.next("text");
}

现在请注意,在这段代码中,我们仅在 ngOnInit 中订阅了一次。尽管如此,每次调用emitValue()方法(例如从按钮)时,console.log都会被调用。这是因为订阅会一直持续到取消订阅为止。这意味着,每次对主题调用 next() 时都会调用该操作。

那么当您订阅多次时会发生什么?让我们尝试一下:

exampleSubject = new Subject();

ngOnInit(): void {
    subscribeToSubject(); // 1st
    subscribeToSubject(); // 2nd
    subscribeToSubject(); // 3rd
    this.emitValue();
}

emitValue() {
    this.exampleSubject.next("text");
}

subscribeToSubject() {
    this.exampleSubject.pipe(
        tap(console.log)
    ).subscribe();
}

我们订阅了一个主题 3 次,现在每当发出值时,控制台日志都会被调用 3 次。您创建的每个订阅都会调用它。

现在,当我们查看您的示例时,每次单击 addTask 按钮时都会添加订阅。这就是为什么每次添加任务时,都会多一个控制台日志。 但是,嘿,在您的第一个示例中,当您删除 .next() 时,您有一些控制台日志,即使您没有发出任何值,这是为什么呢?现在我们来到BehaviourSubject。这是一种特殊的主题,它拥有其价值,并在订阅后立即发出它。而且每次你​​调用 .next() 时它也会发出,但你没有这样做,所以这就是为什么它每次订阅时都会调用 1 个控制台日志。

你应该做的是打电话

this.tasks.pipe(tap((value) => console.log(value)
)).subscribe();

仅一次,例如在 ngOnInit() 中

好了,现在我们进入第二期

这非常简单,但需要一些关于引用如何工作的知识。

在您的服务中,您将整个任务数组放入 BehaviourSubject 中。事实上,这个主题持有这个数组的引用。这意味着,每当您将新值推送到数组中时,BehaviourSubject 也会拥有它。这就是发生的情况,每当您调用 addTask 方法时,都会将新值推送到任务数组,并且您的 BehaviourSubject 也有它。

Neueste Downloads
Mehr>
Web-Effekte
Quellcode der Website
Website-Materialien
Frontend-Vorlage