angular - چگونه FormControl واکنشی را هنگام نابود شدن کامپوننت از بین ببریم؟
من یک کامپوننت دارم با FormControl و اشتراکی بر روی تغییر مقدار آن:
@Component(...)
export class FooComponent implements OnInit, OnDestroy {
fooFormControl: FormControl;
...
ngOnInit() {
this.fooFormControl.valueChanges.subscribe(
() => {...},
() => {},
() => {
// never happens
},
);
}
ngOnDestroy() {
//happens
}
}
اما وقتی کامپوننت از بین میرود، عنصر FormControl نابود نمیشود و callback مربوط به onComplete هرگز اتفاق نمیافتد.
راه صحیح برای از بین بردن عنصر FormControl هنگام از بین رفتن کامپوننت چیست؟
پاسخ شماره ۱
مشاهدهگرهای FormControl قرار نیست تکمیل شوند، هرچند امکان انجام این کار به صورت دستی وجود دارد اگر منطق خاصی به تابع بازگشتی کامل مربوط باشد.
از آنجا که valueChanges یک انتشاردهنده رویداد است و از RxJS Subject به ارث میبرد، میتوان از آن لغو اشتراک کرد یا آن را تکمیل کرد:
ngOnDestroy() {
this.fooFormControl.valueChanges.complete();
// and/or
this.fooFormControl.valueChanges.unsubscribe();
}
ممکن است EventEmitter در آینده یک موضوع نباشد. این ممکن است باعث تغییرات شکسته شود، اما در حال حاضر به طور کامل بر RxJS متکی است.
پاسخ شماره ۲
در اینجا دو ایده وجود دارد که ممکن است به شما کمک کند. مطمئن نیستم که آیا آنها "راه درست" هستند یا نه، اما تا حالا برای من خیلی خوب جواب دادهاند. علاوه بر این، من طبق معمول در ngOnDestroy لغو اشتراک میکنم.
۱# اجبار به تکمیل با معرفی موضوع خود از طریق takeUntil
در تایپاسکریپت، ویژگی valueChanges از نوع Observable است. اگر نمیخواهید این مسئله را دور بزنید و به Subject دسترسی پیدا کنید، میتوانید با استفاده از عملگر takeUntil، Subject مخصوص خود را معرفی کنید. این اجازه میدهد تا اتمام جریان، در سطحی بالاتر از observable مربوط به valueChanges اعمال شود. مهم است که عملگر takeUntil را قبل از switchMap قرار ندهید، زیرا در غیر این صورت observable سوئیچ شده ادامه مییابد و اشتراک شما لغو نخواهد شد! به همین دلیل، آن را درست قبل از عملگر subscribe قرار دادهام.
در اینجا یک نمونه است:
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'); }
);
در اینجا یک مثال عملی وجود دارد: https://rxviz.com/v/RoQNBnOM
مرحله ۲: FormBuilder را به فهرست ارائهدهندگان کامپوننت خود اضافه کنید
این کاری است که معمولاً انجام میدهم. در واقع معمولاً فرم خود را داخل یک سرویس که از FormBuilder استفاده میکند کپسوله میکنم، اما تأثیر همان است. ارائه سرویس در این سطح باعث میشود سرویس هر بار که کامپوننت ایجاد میشود، نابود و دوباره ایجاد شود. من شروع به انجام این کار کردم چون مدام با باگهای عجیبی مواجه میشدم که ناشی از جریانهای observable بودند که از valueChanges ایجاد شده بودند و پس از پایان عمر کامپوننت باقی میماندند. آنها وقتی کامپوننت دوباره ایجاد میشد، دوباره مشترک میشدند و چیزهایی از این دست.
در اینجا یک نمونه است:
@Component({
selector: 'my-form',
templateUrl: './my-form.component.html',
styleUrls: ['./my-form.component.scss'],
changeDetection: ChangeDetectionStrategy.OnPush,
providers: [ FormBuilder ] // <-- THIS PART
})
export class MyFormComponent implements OnInit, OnDestroy {
}
توجه داشته باشید که این در واقع تکمیل را منتشر نمیکند. اما هر بار یک موضوع منبع تازه به شما میدهد.
