У меня есть пример приложения Angular с глобальной службой данных, который я пытаюсь разделить между несколькими экземплярами компонента путем его фильтрации. Это прототип для моего более крупного приложения, но у меня одна и та же проблема в обоих случаях. Я пытался использовать трубу фильтра, но я получаю сообщение об ошибке. На данный момент я хотел бы получить некоторые рекомендации по наилучшей практике в этой ситуации, поскольку я впервые пытаюсь это сделать. Если это конвейерная фильтрация, то мне нужна помощь в сообщении об ошибке.
В моем примере используется тестовый API, который возвращает элементы ToDo. Я хочу использовать сервис для компонента, который показывает завершенные элементы в одном экземпляре и неполные элементы в другом. Существует также фильтрация по userId только потому, что я чувствовал, что мне нужен еще один элемент для этого.
Служба:
import { Injectable } from '@angular/core';
import { HttpClient } from '@angular/common/http';
import { Observable, BehaviorSubject } from 'rxjs';
export interface Todo {
userId: number;
id: number;
title: string;
completed: boolean;
}
@Injectable({
providedIn: 'root'
})
export class TodosService {
private _todos$: BehaviorSubject<Todo[]> = new BehaviorSubject(null);
constructor(private http: HttpClient) { }
// Public Methods
get todos() {
return this._todos$.asObservable();
}
loadData() {
this._listTodos()
.subscribe(
res => {
this._todos$.next(res);
},
err => console.log('Error receiving todo items.', err)
);
}
addTodo(todo: Todo) {
this._addTodo(todo).subscribe( item => {
const d = this._todos$.getValue();
d.push(item);
this._todos$.next(d);
});
}
// Private methods
_listTodos(): Observable<Todo[]> {
return this.http.get<Todo[]>('https://jsonplaceholder.typicode.com/todos');
}
_addTodo(todo: Todo): Observable<Todo> {
return this.http.post<Todo>('https://jsonplaceholder.typicode.com/todos', todo);
}
}
Компонент:
<div>
<button (click)="addTodo()">Add Todo</button>
</div>
<ng-container *ngIf="(todosService.todos | todoFilter$: complete: userId | async) as todos">
<h1>Completed: {{ complete }} UserId: {{ userId }} </h1>
<ul>
<li *ngFor="let todo of todos">
{{ todo.id}} - {{ todo.userId }} - {{ todo.title }}
</li>
</ul>
</ng-container>
import { Component, OnInit, Input, OnChanges } from '@angular/core';
import { Todo, TodosService} from '../../services/todos.service';
@Component({
selector: 'app-todo',
templateUrl: './todo.component.html',
styleUrls: ['./todo.component.css']
})
export class TodoComponent implements OnChanges {
@Input() complete: false;
@Input() userId: number = null;
constructor(private todosService: TodosService) { }
ngOnChanges() {
this.load();
}
async load() {
await this.todosService.loadData();
}
addTodo() {
const t: Todo = {
id: 999,
userId: this.userId,
title: 'Css Add',
completed: this.complete
};
this.todosService.addTodo(t);
}
}
Pipe Filter (выдает ошибку: невозможно прочитать свойство 'filter' из null. Очевидно, kws является нулевым но я не уверен, почему, если элементы $ заполнены?):
import { Pipe, PipeTransform } from '@angular/core';
import { Todo } from '../services/todos.service';
import { Observable } from 'rxjs';
import { map } from 'rxjs/operators';
@Pipe({
name: 'todoFilter$'
})
export class TodoFilterPipe implements PipeTransform {
transform(items$: Observable<Todo[]>, complete: boolean, userId: number): any {
if (!items$ || items$ == null || items$ === undefined) { return null; }
return items$.pipe(map(
kws => kws.filter(kw => (kw.completed === complete && (!userId || kw.userId === userId))
))
);
}
}
Пример страницы компонента приложения:
<app-todo complete="false"></app-todo>
<app-todo complete="true" userId="1"></app-todo>
Скажите, пожалуйста, как лучше всего это сделать?