【问题标题】:Subject and Observable, how to delete item, filter() list and next()Subject 和 Observable,如何删除 item,filter() list 和 next()
【发布时间】:2018-03-20 13:25:12
【问题描述】:

我有一个包含主题和可观察的歌曲列表(显示为| async),现在我想从列表中删除一首歌曲,做一些filter() 并在主题上调用next()

我如何过滤以及在哪里过滤?现在我正在对主题执行 getValue() 并将其传递给主题上的 next()。这似乎是错误的和循环的。

我也尝试订阅主题并以这种方式获取数据,过滤它并在subscribe() 中调用next(),但我得到了一个 RangeError。

我可以通过存储所有已删除的 id 来过滤 Observable。然后,主题的列表会因为删除了那里的歌曲而变得不同步,而且每个观察者都必须拥有似乎很可笑的已删除 ID 的列表。我正在迅速变老和精神。请帮我上网:(

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

}

【问题讨论】:

  • 是的,你应该有一些歌曲的数组,否则会变得非常困难。
  • @trichetriche 那么如何解决删除歌曲后主题不同步的问题?我在想一定有一些我还没有弄清楚的过滤方式
  • 好的,我正在回答,请稍等

标签: angular rxjs observable rxjs5 subject-observer


【解决方案1】:

我建议您在组件上创建新属性,您将在其中最后存储状态。 (这里理解歌曲数组)。

通过代表您的状态(或存储)的内部属性和负责同步应用程序其余部分(通过可观察/事件)的另一个属性来更好地概念化您的代码。

另一个提示是按模型强类型化您的代码。将更容易调试和维护。

然后你只需要根据你的逻辑和你的主题更新它

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

【讨论】:

  • 谢谢!这对我来说是最干净的,尽管我一直认为整个事情都可以只用 Observable-filters 来完成。我将实现这一点并强类型化模型。
【解决方案2】:

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

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.songs = this.songs.filter(...),因为它不是就地操作。
  • 感谢您的关注!
  • 感谢您的回答,@trichetriche。我真的很想学习和使用主题/观察者模式,所以我将采用另一种解决方案。感谢您抽出宝贵时间:)
  • 你想学不代表一定要用。但我明白你的意思,祝你好运!
【解决方案3】:

如果您想将所有内容保存在流中,那么您可以从 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);
&lt;script src="https://cdnjs.cloudflare.com/ajax/libs/rxjs/5.5.7/Rx.min.js"&gt;&lt;/script&gt;

或者是这样的:

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); });
&lt;script src="https://cdnjs.cloudflare.com/ajax/libs/rxjs/5.5.7/Rx.min.js"&gt;&lt;/script&gt;

【讨论】:

  • 谢谢!这段代码不错,我敢肯定,但对我来说看起来有点陌生,仍然掌握了 angular 的窍门,所以 Redux 将在未来出现。感谢您抽出宝贵时间:)
猜你喜欢
  • 2018-08-02
  • 2021-12-28
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2018-08-03
  • 1970-01-01
  • 2018-07-22
  • 1970-01-01
相关资源
最近更新 更多