3

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

}
4

3 回答 3

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);
        }
    }
于 2018-03-20T13:45:39.063 回答
0

如果您想将所有内容保存在流中,那么您可以从 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>

于 2018-03-20T13:40:50.473 回答
0

如果您停止依赖异步管道并使用变量来处理您的歌曲,它会变得更容易:

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

这样,您可以简单地过滤,就像它是一个简单的对象数组。

于 2018-03-20T13:42:32.537 回答