Тема и Наблюдаемый, как удалить элемент, список фильтра () и следующий ()
У меня есть список песен с настройками Subject и Observable (показано с | async
на вид), и теперь я хочу удалить песню из списка, сделать некоторые filter()
и позвонить next()
на предмет.
Как и где я могу фильтровать? Сейчас занимаюсь getValue()
на тему и передачи этого next()
на, ну, тема. Это только кажется неправильным и округлым.
Я также пытался подписаться на тему и получать данные таким образом, фильтруя их и вызывая next()
внутри subscribe()
, но я получил RangeError.
Я мог бы отфильтровать Observable, сохранив все удаленные идентификаторы. После этого список субъекта становится несинхронным, так как там есть удаленные песни, и каждый наблюдатель должен иметь список удаленных идентификаторов, который кажется нелепым. Я быстро старею и становлюсь ментальным. Пожалуйста, помогите мне интернет:(
export class ArtistComponent implements OnInit {
private repertoire$;
private repertoireSubject;
constructor(
private route: ActivatedRoute,
private service: ArtistService
) {
this.getRepertoire().subscribe(
songs => this.repertoireSubject.next(songs)
);
}
getRepertoire() {
return this.route.paramMap
.switchMap((params: ParamMap) =>
this.service.fetchRepertoire(params.get('id')));
}
//THIS IS WHERE I'M HAVING TROUBLE
delete(id): void {
this.repertoireSubject.next(
this.repertoireSubject.getValue().filter(song => (song.id !== id))
);
// TODO remove song from repertoire API
}
ngOnInit() {
this.repertoireSubject = new BehaviorSubject<any>(null);
this.repertoire$ = this.repertoireSubject.asObservable();
}
}
3 ответа
Я рекомендую вам создать новый атрибут на вашем компоненте, где вы будете в последний раз хранить состояние. (здесь понимает множество песен).
Всегда лучше концептуализировать ваш код с помощью внутреннего свойства, которое представляет ваше состояние (или хранилище), и другого атрибута, который играет роль для синхронизации остальной части вашего приложения (с помощью наблюдаемого / события).
Еще один совет - набирайте код по модели. Будет легче отлаживать и поддерживать.
Тогда вам просто нужно обновить его в соответствии с вашей логикой, а затем по вашей теме
export interface SongModel {
id: number;
title: string;
artiste: string;
}
export class ArtistComponent implements OnInit {
private repertoire$ : Observable<SongModel[]>;
private repertoireSubject: BehaviorSubject<SongModel[]>;
//Array of song, should be same type than repertoireSubject.
private songs: SongModel[];
constructor(
private route: ActivatedRoute,
private service: ArtistService
) {
//We push all actual references.
this.getRepertoire().subscribe(
songs => {
this.songs = songs;
this.repertoireSubject.next(this.songs);
}
);
}
ngOnInit() {
//Because is suject of array, you should init by empty array.
this.repertoireSubject = new BehaviorSubject<SongModel[]>([]);
this.repertoire$ = this.repertoireSubject.asObservable();
}
getRepertoire() {
return this.route.paramMap
.switchMap((params: ParamMap) =>
this.service.fetchRepertoire(params.get('id')));
}
//THIS IS WHERE I'M HAVING TROUBLE
delete(id: number): void {
// Update your array referencial.
this.songs = this.songs.filter(songs => songs.id !== id);
// Notify rest of your application.
this.repertoireSubject.next(this.songs);
}
}
Если вы хотите сохранить все в потоках, вы можете взять страницу из книги воспроизведения Redux и сделать что-то вроде этого:
const actions = new Rx.Subject();
const ActionType = {
SET: '[Song] SET',
DELETE: '[Song] DELETE'
};
const songs = [
{ id: 1, name: 'First' },
{ id: 2, name: 'Second' },
{ id: 3, name: 'Third' },
{ id: 4, name: 'Fourth' },
{ id: 5, name: 'Fifth' }
];
actions
.do(x => { console.log(x.type, x.payload); })
.scan((state, action) => {
switch(action.type) {
case ActionType.SET:
return action.payload;
case ActionType.DELETE:
return state.filter(x => x.id !== action.payload);
}
return state;
}, [])
.subscribe(x => { console.log('State:', x); });
window.setTimeout(() => {
actions.next({ type: ActionType.SET, payload: songs });
}, 1000);
window.setTimeout(() => {
actions.next({ type: ActionType.DELETE, payload: 2 });
}, 2000);
window.setTimeout(() => {
actions.next({ type: ActionType.DELETE, payload: 5 });
}, 3000);
<script src="https://cdnjs.cloudflare.com/ajax/libs/rxjs/5.5.7/Rx.min.js"></script>
Или как то так:
const deletes = new Rx.Subject();
const songs = Rx.Observable.of([
{ id: 1, name: 'First' },
{ id: 2, name: 'Second' },
{ id: 3, name: 'Third' },
{ id: 4, name: 'Fourth' },
{ id: 5, name: 'Fifth' }
]);
window.setTimeout(() => {
deletes.next(2);
}, 1000);
window.setTimeout(() => {
deletes.next(5);
}, 2000);
songs.switchMap(state => {
return deletes.scan((state, id) => {
console.log('Delete: ', id);
return state.filter(x => x.id !== id);
}, state)
.startWith(state);
}).subscribe(x => { console.log('State:', x); });
<script src="https://cdnjs.cloudflare.com/ajax/libs/rxjs/5.5.7/Rx.min.js"></script>
Если вы перестанете полагаться на асинхронный канал и используете переменную для обработки ваших песен, это станет намного проще:
import { filter } from 'rxjs/operators';
export class ArtistComponent implements OnInit {
private songs: any;
constructor(
private route: ActivatedRoute,
private service: ArtistService
) {
this.getRepertoire().subscribe(songs => this.songs = songs);
}
getRepertoire() {
return this.route.paramMap
.switchMap((params: ParamMap) =>
this.service.fetchRepertoire(params.get('id')));
}
delete(id): void {
this.songs = this.songs.filter(song => song.id !== id);
}
}
Таким образом, вы можете просто фильтровать, как будто это простой массив объектов.