Как удалить элемент из наблюдаемого списка?

Вопрос или проблема

Как удалить элемент из наблюдаемого списка?

У меня есть простой список задач, и при нажатии на “выполнить” этот элемент должен удаляться из списка, но, похоже, ничего не происходит при нажатии на него.

Stackblitz: Ссылка

HTML:

<div *ngIf="list$; else noList">
  <div *ngFor="let data of list$ | async" class="items">
    <div>{{ data.id }} -</div>
    <div>{{ data.description }}</div>
    <button (click)="complete(data)">Выполнить</button>
  </div>
</div>
<button (click)="add()">Добавить</button>

<ng-template #noList>
  <h1>Загрузка...</h1>
</ng-template>

сервис:

complete(data: any) {
    console.log(data.id);

    let result = this.data$
      .pipe(
        filter((x: any) => x.id !== data.id),
        tap((x: any) => console.log(x))
      )
      .subscribe((val: any) => {
        this.resultObs = of(val);
        console.log(val);
        console.log(this.resultObs);
      });

    return this.resultObs;
  }

Оператор filter не фильтрует массивы сам по себе. Он фильтрует данные, проходящие через конвейер. Если у вас есть это:

arr$: Observable<any[]> = of([1, 2]);

arr$.pipe(
    filter((x) => . . .)
)

x в функции filter будет всем [1, 2] массивом, а не каждым элементом массива. filter здесь фильтрует все данные, проходящие через конвейер. Вы могли бы использовать это, например, чтобы предотвратить выполнение колбэка subscribe, если у пользователя меньше 4 задач:

arr$: Observable<any[]> = of([1, 2]);

arr$.pipe(
    filter((todos: any[]) => todos.length > 4)
).subscribe(() => {
    console.log("Не будет выполнено")
});

arr$.pipe(
    filter((todos: any[]) => todos.length < 4)
).subscribe(() => {
    console.log("Будет выполнено, так как длина массива меньше 4")
});

Если вы хотите преобразовать объект, проходящий через конвейер, используйте map:

let result = this.data$
  .pipe(
    map((xs: any[]) => xs.filter((x) => x.id !== data.id)),
    tap((xs: any[]) => console.log(x))
  )
  .subscribe((val: any) => {
    this.resultObs = of(val);
    console.log(val);
    console.log(this.resultObs);
  });

Это преобразует массив, содержащийся в data, с помощью map, вызывая метод filter для фильтрации.

Ответ или решение

Для удаления элемента из наблюдаемого списка (observable list) в вашем проекте Angular, следует внести некоторые изменения в ваш сервис и компонент. В данной ситуации ошибка заключается в неправильном использовании оператора filter, который не фильтрует массив напрямую, а обрабатывает данные, проходящие через поток.

Вот пошаговое руководство для корректной реализации функции удаления элемента из списка:

Шаг 1: Измените метод complete в сервисе

Вместо использования filter, вам нужно применить оператор map, чтобы модифицировать массив, содержащий ваши элементы. Вот пример корректного кода:

import { Injectable } from '@angular/core';
import { BehaviorSubject, Observable, of } from 'rxjs';
import { map } from 'rxjs/operators';

@Injectable({
  providedIn: 'root'
})
export class TodoService {
  private dataSubject = new BehaviorSubject<any[]>([
    { id: 1, description: 'Task 1' },
    { id: 2, description: 'Task 2' },
    // другие элементы
  ]);
  data$ = this.dataSubject.asObservable();

  complete(data: any) {
    // Используйте оператор map для фильтрации массива
    this.data$.pipe(
      map((items: any[]) => items.filter(item => item.id !== data.id))
    ).subscribe(updatedList => {
      this.dataSubject.next(updatedList); // Обновляем список
    });
  }
}

Шаг 2: Обновите компонент

Убедитесь, что ваш компонент правильно связывается с сервисом и отображает обновленный список:

import { Component } from '@angular/core';
import { TodoService } from './todo.service';

@Component({
  selector: 'app-root',
  templateUrl: './app.component.html',
})
export class AppComponent {
  list$ = this.todoService.data$;

  constructor(private todoService: TodoService) {}

  complete(data: any) {
    this.todoService.complete(data);
  }

  add() {
    // Логика добавления нового элемента
  }
}

Шаг 3: Проверьте HTML-шаблон

Ваш HTML выглядит корректно, но важно убедиться, что он соответствует новым изменениям в компоненте. Вот ваш HTML с небольшими улучшениями:

<div *ngIf="list$ | async as list; else noList">
  <div *ngFor="let data of list" class="items">
    <div>{{ data.id }} - </div>
    <div>{{ data.description }}</div>
    <button (click)="complete(data)">Complete</button>
  </div>
</div>
<button (click)="add()">Add</button>

<ng-template #noList>
  <h1>Loading...</h1>
</ng-template>

Объяснение

  1. Использование BehaviorSubject: Это позволяет отслеживать изменения в массиве объектов ваших задач и автоматически обновлять подписчиков, когда массив изменяется.
  2. Использование map для фильтрации: Этот оператор позволяет вам трансформировать массив, сохраняя только те элементы, которые вы хотите оставить, и передавать обновленный массив обратно через next метод BehaviorSubject.
  3. Подписка на обновленный список: Каждый раз при вызове метода complete, подписка на data$ обновит список в компоненте.

Теперь, когда вы нажмете кнопку "Complete" для конкретной задачи, метод complete будет вызываться, и соответствующий элемент будет удален из списка. Убедитесь, что ваши наблюдаемые списки (observable lists) корректно обновляются и отображаются в UI.

Оцените материал
Добавить комментарий

Капча загружается...