If you're looking for some helpful tips, here are two approaches that have been effective for me. While I can't guarantee they are foolproof methods, they have served me well thus far. Additionally, I make sure to unsubscribe in ngOnDestroy as standard practice.
#1 Ensure completion using takeUntil with your own Subject
In TypeScript, the valueChanges property is of type Observable. If you prefer not to work directly with Observables and want access to a Subject instead, you can create your own Subject by utilizing the takeUntil operator. This method allows you to enforce completion at a higher level than the valueChanges observable. Be cautious when placing the takeUntil operator - avoid positioning it before a switchmap or else the switched observable will persist, preventing cancellation of your subscription. To address this issue, place the operator just before the subscribe function.
For example:
const stop = new Subject();
// simulate ngOnDestroy
window.setTimeout(() => {
stop.next();
stop.complete();
}, 3500);
Observable
.interval(1000)
.takeUntil(stop)
.subscribe(
() => { console.log('next'); },
() => { console.log('error'); },
() => { console.log('complete'); }
);
Check out a live demonstration here:
#2 Include the FormBuilder in your component's provider list
This is my preferred method. Personally, I encapsulate my form within a service that utilizes the FormBuilder, achieving a similar outcome. By providing the service at the component level, the service is recreated each time the component is generated. I adopted this approach after encountering peculiar bugs caused by lingering observable streams derived from valueChanges extending beyond the component's lifespan. These streams would inadvertently resubscribe upon component recreation.
For instance:
@Component({
selector: 'my-form',
templateUrl: './my-form.component.html',
styleUrls: ['./my-form.component.scss'],
changeDetection: ChangeDetectionStrategy.OnPush,
providers: [ FormBuilder ] // <-- ADD THIS LINE
})
export class MyFormComponent implements OnInit, OnDestroy {
}
It's important to note that while this doesn't trigger completion propagation, it does furnish you with a fresh source subject with each component instantiation.