Вопрос или проблема
Как удалить элемент из наблюдаемого списка?
У меня есть простой список задач, и при нажатии на “выполнить” этот элемент должен удаляться из списка, но, похоже, ничего не происходит при нажатии на него.
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>
Объяснение
- Использование
BehaviorSubject
: Это позволяет отслеживать изменения в массиве объектов ваших задач и автоматически обновлять подписчиков, когда массив изменяется. - Использование
map
для фильтрации: Этот оператор позволяет вам трансформировать массив, сохраняя только те элементы, которые вы хотите оставить, и передавать обновленный массив обратно черезnext
методBehaviorSubject
. - Подписка на обновленный список: Каждый раз при вызове метода
complete
, подписка наdata$
обновит список в компоненте.
Теперь, когда вы нажмете кнопку "Complete" для конкретной задачи, метод complete
будет вызываться, и соответствующий элемент будет удален из списка. Убедитесь, что ваши наблюдаемые списки (observable lists) корректно обновляются и отображаются в UI.