Subject and Observable, how to delete item, filter() list and next()

送分小仙女□ 提交于 2019-12-11 00:12:48

问题


I have a list of songs setup with Subject and Observable (shown with | async in view), and now I want to delete a song off the list, do some filter() and call next() on the Subject.

How and where do I filter? Right now I am doing getValue() on Subject and passing that to next() on, well, Subject. This just seems wrong and circularish.

I also tried subscribing to the Subject and getting the data that way, filtering it and calling next() inside subscribe(), but I got a RangeError.

I could filter the Observable by storing all deleted id's. The Subject's list then becomes out of sync by having deleted songs on there and also every observer would have to have the deleted-id's-list which seems ludicrous. I'm rapidly growing old and mental. Please help me internet :(

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();
  }

}

回答1:


i recommand you to create new attribute on your component, where you will last store state. (here understands array of songs).

Is always better conceptualize your code by, internal property who represent your state (or store) and another attribute who have the role to sync rest of your application (by observable / event).

Another tips is to strong type your code by model. Will be easier to debug and maintain.

Then you just have to update it according to your logic and next on your Subject

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);
        }
    }



回答2:


If you stop relying on the async pipe and use a variable to handle your songs, it gets way easier :

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);
  }
}

This way, you can simply filter like it is a simple array of object.




回答3:


If you want to keep everything in streams then you could take a page from the Redux playbook and do something like this:

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>

Or something like this:

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>


来源:https://stackoverflow.com/questions/49385487/subject-and-observable-how-to-delete-item-filter-list-and-next

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!