javascriptangularinnerhtml

innerHTML property binding does not update without page reload Angular


I have a file upload form and an empty div with innerHTML property binding. When a user upload a file (excel), it will be processed by the server, converted to html and stored in the database. Angular will then get the data from the database, sanitize it (so css styling will not be removed) and insert it to the empty div through innerHTML property binding.

The problem is, after file upload it does not update the content of div without a page reload. I want to update <div *ngIf="budgetHTML" [(innerHTML)]="budgetHTML"></div> without reloading the page.

I'm new to angular, any advice on how I can achieve this?

component.html

<div *ngIf="budgetHTML" [(innerHTML)]="budgetHTML">
</div>

component.ts (I only included relevant code)

export class ProgramProjectInformationComponent implements OnInit, OnDestroy {

  budgetHTML: SafeHtml;

  constructor(
    private store: Store<AppStateInterface>,
    private formBuilder: UntypedFormBuilder,
    private grantProposalService: GrantProposalService,
    private route: ActivatedRoute,
    private sanitizer: DomSanitizer,
  ) {}

  ngOnInit(): void {
    this.initializeForm();
    this.initializedValues();
    this.initializePdoPrograms();
    this.initializePdoBankAccounts();
    this.intializeListeners();
    this.initializeBudgetTable();
  }

  initializedValues() {
    this.grantProposalData$ = this.store.pipe(select(grantProposalSelector));
    this.isLoading$ = this.store.pipe(select(isFetchingGrantProposalSelector));
    this.composeBankAccount$ = this.store.pipe(select(grantProposalComposeBankAccountSelector));
    this.grantProposalDraftStatus$ = this.store.select(grantProposalDraftStatusSelector);
  }

    initializeBudgetTable() {
        this.grantProposalData$
        .pipe(
            takeUntil(this.destroy$)
        )
        .subscribe(data => {
      this.budgetHTML = this.sanitizer.bypassSecurityTrustHtml(data?.budget_info);
    });     
    }

  /**
   * for importing budget data to database
   *
   * @return void
   */
  importBudget() {
    this.loadingUpload = true;
    
    if (this.budgetForm.valid) {      
      // convert formControl to FormData to allow sending images/files
      let formData = this.convertToFormData();      
      
      this.grantProposalId$.pipe(takeUntil(this.destroy$)).subscribe(id => {
        formData.set('grant_proposal_id', id);
      });
      
      this.grantProposalService
        .importBudgetTable(formData)
        .pipe(
          takeUntil(this.destroy$)
        )
        .subscribe({
          next: (res) => {
            this.showAlertDataSaved(res.results.message);
            this.initializedValues();
            this.initializeBudgetTable();
          },
          error: (err) => {
            this.showAlertSavingFailed(err.error.message);
            this.loadingUpload = false;
          },
          complete: () => {
            this.loadingUpload = false;
          }
        })
    } else {
      this.loadingUpload = false;
      this.showAlertCheckFields('Please fill in the required fields to proceed.');
    }
  }
}


Solution

    1. Make sure that this stream this.grantProposalData$ = this.store.pipe(select(grantProposalSelector)); emits a new value after you get the response from the API.
    2. Wait for grant_proposal_id to be set before you send the formData.
    3. You don't need takeUntil(this.destroy$) every time you call importBudget, you can use first instead.

    So try the following:

    importBudget() {
      this.loadingUpload = true;
    
      if (this.budgetForm.valid) {
        // convert formControl to FormData to allow sending images/files
        const formData = this.convertToFormData();
    
        this.grantProposalId$
          .pipe(
            first(),
            switchMap(id => {
              formData.set('grant_proposal_id', id);
    
              return this.grantProposalService.importBudgetTable(formData).pipe(first());
            }),
          )
          .subscribe({
            next: res => {
              this.showAlertDataSaved(res.results.message);
              // Remove these 2 lines and make sure you assign a new value for grantProposalSelector
              // this.initializedValues();
              // this.initializeBudgetTable();
            },
            error: err => {
              this.showAlertSavingFailed(err.error.message);
              this.loadingUpload = false;
            },
            complete: () => {
              this.loadingUpload = false;
            },
          });
      } else {
        this.loadingUpload = false;
        this.showAlertCheckFields('Please fill in the required fields to proceed.');
      }
    }