改变流源

时间:2019-03-15 07:50:28

标签: angular rxjs

在angular中,我有负责查询参数更改的抽象类:

@Injectable()
export abstract class PageDetailsAbstract<T> implements OnInit {
  object: string;
  id: number;
  details$: Observable<T>;

  constructor(
    protected entitiesSelectors: EntitiesSelectors) {
  }

  ngOnInit() {
    this.route.params
      .pipe(
        map(resp => +resp.id),
      )
      .subscribe(resp => {
        this.id = resp;
        this.adopt();
      });
  }

  protected adopt() {
    if (this.id) {
      this.details$ = this.entitiesSelectors.details(this.object, this.id);
    }
  }
}

对于详细信息页面,我也有具体的课程:

@Component({
  selector: 'app-page-maile-szczegoly',
  templateUrl: './page-maile-szczegoly.component.html',
  styles: [],
  changeDetection: ChangeDetectionStrategy.OnPush
})
export class PageMailsDetailComponent extends PageDetailsAbstract<Mail> {
  object = 'mails';
  // ...

  ngOnInit() {
    super.ngOnInit();
    this.details$
      .subscribe(data => {
        // do something with it
        // it's triggered only once
      });
  }
}

问题是subscribe中的ngOnInit仅被触发一次。这是因为父类中有this.details$ = this.entitiesSelectors.details(this.object, this.id);this.detials$的实例及其subscribe函数被销毁。

如何总是拥有this.details$的相同实例,却总是用this.entitiesSelectors.details(this.object, this.id)来纠正一个实例?

1 个答案:

答案 0 :(得分:1)

您可以使用Subjecthttp://reactivex.io/rxjs/class/es6/Subject.js~Subject.html

我创建了一个非常简化的stackblitz以显示其工作原理 https://stackblitz.com/edit/angular-zyvkpu?file=src%2Fapp%2Fapp.component.ts

查看控制台输出以进行验证。

这是相关的代码部分(适应您的情况,而不是stackblitz的1:1)

@Injectable()
export abstract class PageDetailsAbstract<T> implements OnInit {
  object: string;
  id: number;
  details$: Subject<T> = new Subject<T>();

  constructor(
    protected entitiesSelectors: EntitiesSelectors) {
  }

  ngOnInit() {
    this.route.params
      .pipe(
        map(resp => +resp.id),
      )
      .subscribe(resp => {
        this.id = resp;
        this.adopt();
      });
  }

  protected adopt() {
    if (this.id) {
      this.entitiesSelectors.details(this.object, this.id).subscribe((detail: T) => {
        this.details$.next(detail);
    });
  }
}

我个人将对代码进行一些重组。 这是一个更清洁的选择,因为您不会嵌套对subscribe的多次调用,而且您不会(至少不会那么依赖)存在于rxjs流之外的this.id

@Injectable()
export abstract class PageDetailsAbstract<T> implements OnInit {
  object: string;
  id: number;
  details$: Subject<T> = new Subject<T>();

  constructor(
    protected entitiesSelectors: EntitiesSelectors) {
  }

  ngOnInit() {
    this.route.params
      .pipe(
        mergeMap((resp: any) => {
         return this.adopt(+resp.id);
      }))
      .subscribe((detail: T) => {
        this.details$.next(detail);
      });
  }

  protected adopt(id: number): Observable<T> {
    if (this.id) {
      return this.entitiesSelectors.details(this.object, this.id);
    }
    else {
      return of(null); // or whatever is appropriate in your case (e.g. EMPTY or something)
    }
}

您的子类将保持不变。

为什么
至于为什么您的示例不起作用: 我认为这是因为details$实在是太冷了。 (https://blog.thoughtram.io/angular/2016/06/16/cold-vs-hot-observables.html

当您重新分配details$时,您将创建一个新的可观察对象,并且先前进行的订阅没有意义,因为它是用于另一个可观察对象的。您需要重新订阅。

Subject非常热门。 也许您也可以使用publishshare之类的东西将您的冷观测值转换为高温观测值。