1

我有一个角度组件,它从 @Input 变量中获取数据,并使用它来过滤来自服务的其他数据。(简化的)结构如下所示:

<phones-component>
 <phone-details>
  <..>
  <phone-history-component [phoneNumber$]="phoneNumber$"></phone-history-component>
 <phone-details>
</phones-component>

电话详细信息组件:

 changeLogs$: Observable<AuditLog[]>;

 constructor(
   private auditLogService: AuditLogService, //ngrx-data service
 ){

  ngOnInit(): void {
      this.changeLogs$ = combineLatest([this.phoneNumber$, this.auditLogService.entities$])
           .pipe(
                 map(pn => pn[1].filter(logs => logs.pkId === pn[0].id))); //filters the logs to show only log entries from the corresponding phonenumber
   }

路由配置如下:

{
  path: 'phones',
  component: PhonesComponent,
},
{
  path: 'phones/:id',
  component: PhoneDetailsComponent,
},

只要我从父容器“输入”PhoneDetailsComponent,一切都会按预期工作。当用户手动重新加载 PhoneDetails 视图时,changeLogs$ 数组为空,除非我手动注入解包后的 observable 的属性,如下所示:

  <phone-history-component [phoneNumberId]="(phoneNumber$ | async).id" [phoneNumber$]="phoneNumber$"></phone-history-component>

如果我像这样注入 phoneNumber$

[phoneNumber]="(phoneNumber$ | async)"

它不起作用。(我不在组件中使用这些额外的输入)

现在我知道还有其他方法可以实现此功能,但我只是对代码为何如此表现感兴趣?

感觉就像我对基本的 rxjs 管道缺乏一些基本的了解。那么..这里到底发生了什么?

任何帮助表示赞赏。

更新 BizzyBob 是对的。直接重载组件函数第一次执行phoneNumber$Observable 没有任何值filter我通过快速检查解决了这个问题:

  map(pn => {
        return !!pn[0] ? pn[1].filter(logs => logs.pkId === pn[0].id) : null;
      }));

现在..这是推荐的方法,还是有更好的方法?

4

2 回答 2

2

要创建正确的 rxjs 管道,您可以使用startWithoperator 和switchMap. 然后您的代码将如下所示:

ngOnInit(): void {
  this.changeLogs$ = this.phoneNumber$
                          .pipe(
                             startWith({}),
                             switchMap(item => this.filterData(item))
                              );
 }

filterData(value = {}): Observable<any>{
 return this.auditLogService.entities$
            .pipe(
                map(data => data.filter(logs => logs.pkId === value.id))
                );
 }
于 2020-05-13T13:55:21.313 回答
1

如何phoneNumber$定义?也许formControl.valueChanges可以观察到?这不会调用初始值,因此您可以使用 rxjsstartWith()运算符。如果你不能很好地使用它(出于某种奇怪的原因),那BehaviorSubject就是下一个选择。

我在片段中重现了您的问题并提供了 2 个推荐的解决方案。看一看!

let changeLogs$ = null;
let phoneNumber$ = null;
let auditLogService = {
  entities$: rxjs.of([{pkId: 1}])
}

function ngOnInit() {
      changeLogs$ = rxjs.combineLatest([phoneNumber$, auditLogService.entities$])
           .pipe(
                 rxjs.operators.map(pn => pn[1].filter(logs => logs.pkId === pn[0].id)))
   }
   
console.log('empty observable');
phoneNumber$ = rxjs.of();
ngOnInit();
let subscribtion = changeLogs$.subscribe(data => console.log(data));
subscribtion.unsubscribe();

console.log('observable with data');
phoneNumber$ = rxjs.of({id: 1})
ngOnInit();
subscribtion = changeLogs$.subscribe(data => console.log(data));
subscribtion.unsubscribe();

console.log('empty observable with startWith');
phoneNumber$ = rxjs.of().pipe(rxjs.operators.startWith({id: 1}));
ngOnInit();
subscribtion = changeLogs$.subscribe(data => console.log(data));
subscribtion.unsubscribe();

console.log('behaviorsubject with data');
phoneNumber$ = new rxjs.BehaviorSubject({id: 1})
ngOnInit();
subscribtion = changeLogs$.subscribe(data => console.log(data));
<script src="https://cdnjs.cloudflare.com/ajax/libs/rxjs/6.5.5/rxjs.umd.js"></script>

于 2020-05-13T12:44:17.713 回答