Reputation: 3136
I'm creating a reactive form which is dynamically created. I get data from a service in the form of an observable (that comes from a Subject), and want to subscribe to the form's valueChanges
after it's created. When I first set it up, I subscribed to each of the service's observables and to valueChanges, but I saw data from every step of the form's creation, which makes sense, as the two service observables could emit after.
So I refactored the setup to use Observable.zip
, which as far as I can understand, makes the code linear, so the form should be setup before I get to my valueChanges
subscription, but I still see every step of the form creation happening.
this.filterForm = this.formBuilder.group({
'search': '',
'types': this.formBuilder.group({}),
'dates': this.formBuilder.array([]),
});
let formData = Observable.zip(
this.eventService.getTypes(),
this.eventService.getDates()
);
formData.forEach(data => {
let types = data[0],
dates = data[1];
types.forEach(type => {
this.types.push(type);
(<FormGroup>(this.filterForm.get('types'))).addControl(type, new FormControl())
});
dates.forEach(date => {
this.dates.push(date.format('dddd'));
(<FormArray>(this.filterForm.get('dates'))).push(new FormControl(false))
});
this.filterForm.valueChanges.subscribe(data => console.log(data));
});
When I say I see the values as the form is building, I mean this:
{search: "", types: {…}, dates: Array(0)}
{search: "", types: {…}, dates: Array(1)}
{search: "", types: {…}, dates: Array(2)}
{search: "", types: {…}, dates: Array(3)}
{search: "", types: {…}, dates: Array(4)}
{search: "", types: {…}, dates: Array(5)}
There are a dozen entries before it as the types field builds.
Is this how this should be working? My goal is to subscribe AFTER the form is built, so I only get the values when the form is changed by the user.
Upvotes: 3
Views: 3805
Reputation: 96891
I have a little different suggestion. In RxJS apart from the subscribe()
method that subscribes to the chain there are also toPromise()
and forEach()
that internally subscribe as well. See https://github.com/ReactiveX/rxjs/blob/master/src/internal/Observable.ts#L223
And I think that's what's happening to you. The forEach
method subscribes to the formData
Observable and invokes its callback for every value emitted. But you're also using this.filterForm.valueChanges.subscribe
inside the callback which means you're creating a new subscription every time formData
emits a value.
I don't know what your code should do but I think you could restructure it to something like the following:
Observable.zip(
this.eventService.getTypes(),
this.eventService.getDates()
)
.do(data => {
let types = data[0],
dates = data[1];
types.forEach(type => {
this.types.push(type);
(<FormGroup>(this.filterForm.get('types'))).addControl(type, new FormControl())
});
dates.forEach(date => {
this.dates.push(date.format('dddd'));
(<FormArray>(this.filterForm.get('dates'))).push(new FormControl(false))
});
})
.switchMap(() => this.filterForm.valueChanges))
.subscribe(data => console.log(data));
Upvotes: 1
Reputation: 987
Don't subscribe in the OnInit lifecycle hook. Create a method that handles the subscription and call that method after declaring the form model in OnInit.
More generally though you can skip a specific number of emissions using the skip() operator, or use filter/mapping to only process emits after a specific condition / structure has been met.
Upvotes: 0