1

I am currently building a stepper component which has multiple step components rendered in its ng-content. In the stepper component i am accessing these components via ContentChildren. Each step component has a Subject which calls .next() when an Input value changes. I am doing this to notify the Parent (StepperComponent), so that i can recreate my model for the view. First i subscribed to each Subject in a foreach loop. I would like to know if it is possible to combine all of my subjects and subscribe to that. Since i dont really care which step changed and just wanna know that some step changed. I have currently tried with merge() and combineLatest() which both did not fire when one of the subjects called .next().

This is how i used merge and combine latest

this.stepsOnChangeSubscription = merge(this.steps.map(s => s.onChanges$))
  .pipe(takeUntil(this.unsubscribe$)).subscribe(_ => this.createStepConfig());

this.stepsOnChangeSubscription = combineLatest(this.steps.map(s => s.onChanges$))
  .pipe(takeUntil(this.unsubscribe$)).subscribe(_ => this.createStepConfig());

Steps are defined as

@ContentChildren(StepComponent) steps: QueryList<StepComponent>;

and $unsubscribe is just to unsubscribe in ngOnDestroy

Patrick
  • 349
  • 1
  • 7
  • 15
  • It looks to me like you could be better off using [**one** service based `Subject` that each component calls `next()` on](https://angular.io/guide/component-interaction#parent-and-children-communicate-via-a-service). Would simplify this a great deal. Unless there is a particular reason you need these in this configuration? – Liam Aug 13 '20 at 10:10
  • I had that option in mind and i think it would work . But it felt somehow wrong to only have a service for one subject. – Patrick Aug 13 '20 at 10:24
  • I think Patrick's solution is a better fit here - Having a service subject would make the components interface less clear. Here, the component has a well-defined Output - the `onChanges$` Observable, while with a Service you don't really know what the component is doing with it. – ggradnig Aug 13 '20 at 10:48
  • There's nothing wrong with a service as the communication method, [angular themselves recommend it](https://angular.io/guide/component-interaction#parent-and-children-communicate-via-a-service). Why make this more complicated than it needs to be? [KISS](https://en.wikipedia.org/wiki/KISS_principle) – Liam Aug 13 '20 at 10:55
  • I think there is more to this topic than can be discussed in the comments ;) Just because Angular lists it as a possible way of doing things doesn't make it recommended in every situation – ggradnig Aug 13 '20 at 11:22
  • And "simple" in this case is really subjective - one finds services and shared subjects easier and others find using operators simpler. It all depends on what you're used to. – ggradnig Aug 13 '20 at 11:25

1 Answers1

2

With combineLatest you need each Subject to emit at least once before you are notified.

merge is the right choice, but it needs a spreaded array (i.e. ...myArray).

So, the following should help:

this.stepsOnChangeSubscription = merge(...this.steps.map(s => s.onChanges$))
  .pipe(takeUntil(this.unsubscribe$)).subscribe(_ => this.createStepConfig());
ggradnig
  • 13,119
  • 2
  • 37
  • 61