'Force Change Detection after Angular Material matDialog closes

I have a parent component that houses a FilterBookmarksComponent and a ManageBookmarksSidebar.

The FilterBookmarksComponent has a Angular Material Dropdown that changes the list of filters below it according to the selection.

The ManageBookmarksSidebar has a button to create a new bookmark from existing Bookmarks by adding filters to it.

Issue:

Whenever I create a new Bookmark that way the name appears in dropdown but the filter list is not updating.

Probable cause:

After Debugging I figured out that change detection (with OnChanges) is not happening after I close the matDialog. Is there any way I can force (OnChanges) change Detection.

This is the code of the MatDialog:

saveConfirmation(bookmarkId): void {
    if (this.form.valid) {
      let saveBookmarkData: BookmarkFiltersListViewModel = {
        filterName: this.form.value.bookmarkFilterName,
        filterData: this.selectedFilters,
        id: this.form.value.id,
        orderNo: this.form.value.orderNo

      }
      //this._dialogRef.close(saveBookmarkData);

      if (bookmarkId) {
        this.save(saveBookmarkData);
      }
      else {
        const confirmDialogRef = this._dialog.open(ConfirmComponent,
          {
            panelClass: 'common-form-dialog',
            data: {
              title: this._translateService.instant('NEW-OFFERS.BOOKMARK-FILTER.ConfirmationTitle'),
              content: this._translateService.instant('NEW-OFFERS.BOOKMARK-FILTER.ConfirmationContent').replace('{{bookmarkName}}', this.form.value.bookmarkFilterName),
              okButtonText: 'Yes',
              cancelButtonText: 'No',
              reverseButtons: true
            },
            width: '600px',
          });

        confirmDialogRef.afterClosed().subscribe((confirmResult) => {
          if (confirmResult) {
            this.save(saveBookmarkData);
          }
        });
      }
    }
  }


  save(saveBookmarkData: BookmarkFiltersListViewModel): void {
    if (this.form.valid) {
      this.bookmarkFilterVM.id = saveBookmarkData.id;
      this.bookmarkFilterVM.isActive = true;
      this.bookmarkFilterVM.ruleName = saveBookmarkData.filterName;
      this.bookmarkFilterVM.orderNo = saveBookmarkData.orderNo;
      this.bookmarkFilterVM.shipmentType = saveBookmarkData.filterData.shipmentType ? saveBookmarkData.filterData.shipmentType.join(',') : '';
      this.bookmarkFilterVM.offerType = saveBookmarkData.filterData.offerType ? saveBookmarkData.filterData.offerType.join(',') : '';
      this.bookmarkFilterVM.tractionType = saveBookmarkData.filterData.tractionType;
      this.bookmarkFilterVM.vehicleSize = saveBookmarkData.filterData.vehicleSize ? saveBookmarkData.filterData.vehicleSize.join(',') : '';
      this.bookmarkFilterVM.vehicleBuildUp = saveBookmarkData.filterData.vehicleBuildup ? saveBookmarkData.filterData.vehicleBuildup.join(',') : '';

      this.bookmarkFilterVM.leadTimeMax = saveBookmarkData.filterData.leadTimeMax ? saveBookmarkData.filterData.leadTimeMax : 0;
      this.bookmarkFilterVM.leadTimeMin = saveBookmarkData.filterData.leadTimeMin ? saveBookmarkData.filterData.leadTimeMin : 0;
      this.bookmarkFilterVM.totalLoadWeightMax = saveBookmarkData.filterData.totalLoadWeightMax ? saveBookmarkData.filterData.totalLoadWeightMax : 0;
      this.bookmarkFilterVM.totalLoadWeightMin = saveBookmarkData.filterData.totalLoadWeightMin ? saveBookmarkData.filterData.totalLoadWeightMin : 0;
      this.bookmarkFilterVM.excludeJobMatching = saveBookmarkData.filterData.excludeJobMatching ? saveBookmarkData.filterData.excludeJobMatching.join(',') : '';

      this._newOfferService.saveBookmarkFilter(this.bookmarkFilterVM).subscribe(res => {
        console.log(res);
        this._toastrService.show(saveBookmarkData.id ? 'Bookmark filter updated.' : 'Bookmark filter created.', saveBookmarkData.filterName,
          { timeOut: 20000000, tapToDismiss: false, disableTimeOut: 'extendedTimeOut', closeButton: true, toastClass: 'primary-500 bookmark-toast', positionClass: 'inline-bottom-left' }
        );
        
        this._dialogRef.close(saveBookmarkData);

//This is where I want to force change Detection


      }, err=>{
        this._matDialog.open(AlertComponent, { panelClass: 'alert-dialog', data: { title: 'Alert', message: err.error.message }});
      })


    }
  }

PS: Edited: My change detection is happening as soon as(before?) I open the matDialog, since thats where the data is being changed. But the data has not been saved until the before the matDialog closes and then change Detection doesn't happen and I get empty list of filter in frontend.



Solution 1:[1]

You can force Angular to detect changes you the ChangeDetectionRef which you'll have to first inject into your component.

  constructor(
    private changeDetectionRef: ChangeDetectorRef
  ) {}

and then where ever you want to force Angular to detect changes you can just add this:

    this.changeDetectionRef.detectChanges();

Note this will only check this component and its children. If you want to force the change detection in the component where the dialog was opened from, then you'll have to the dialog reference when it was opened and subscribe to its afterClosed() event and then force angular to detect changes inside that subscribe, so it would look like this then:

//Just an example
const dialogRef = this._dialog.open(ConfirmComponent, {});
dialogRef.afterClosed().subscribe(()=>{
    this.changeDetectionRef.detectChanges();
})

Solution 2:[2]

Not entirely sure, but seems fishy to me that the save() function, which should update your filters is done afterClosed(). If the dialog holds the data to be saved, how can save() run, if dialog is closed (no longer exists)?

        confirmDialogRef.afterClosed().subscribe((confirmResult) => {
          if (confirmResult) {
            this.save(saveBookmarkData);
          }

Regarding change detection, you could also update props with ... spread operator which would trigger change dedection to run without injectind cd Ref.

this.bookmarkFilterVM = {...this.bookmarkFilterVM}

Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source
Solution 1
Solution 2 Joosep Parts