surveyjs / survey-creator

Scalable open-source survey software to generate dynamic JSON-driven forms within your JavaScript application. The form builder features a drag-and-drop UI, CSS Theme Editor, and GUI for conditional logic and form branching.
https://surveyjs.io/open-source
Other
923 stars 378 forks source link

Angular Material is not correctly applied within a custom survey creator tab #4512

Closed JaneSjs closed 1 year ago

JaneSjs commented 1 year ago

User Issue: T14410 - Angular material is not working properly in Custom plugin tab ( Creator ) https://surveyjs.answerdesk.io/internal/ticket/details/T14410


https://github.com/surveyjs/survey-creator/assets/22372972/93c5ebb5-0d51-441c-b313-16bad9b4162e

JaneSjs commented 1 year ago

I got a reply from our developer (thank you @dk981234 !).

SurveyJS Angular Components (a survey runner and survey creator) work in zoneless mode. This means that they use their own change detection mechanism and track changes to update UI components when it's required. This is the reason why a custom input was not updated as you expected: simply because a creator doesn't detect changes of your custom input.

To handle this correctly, we suggest either of the following options.

Option 1: Programmatically call this.changeDetectorRef.detectChanges() within the focus and onblur events

Subscribe to the focus and onblur events of your input and invoke a custom update function. Within the update function, call the this.changeDetectorRef.detectChanges() function to trigger change detection.

The updated demo is available at creator-with-material-custom-tab_option1.zip.

custom-tab.component.html

<mat-form-field>
  <mat-label>Input label</mat-label>
  <input matInput (focus)="update()" (blur)="update()">
</mat-form-field>

custom-tab.component.ts

import { ChangeDetectorRef, Component } from '@angular/core';

@Component({
  selector: 'app-custom-tab',
  templateUrl: './custom-tab.component.html',
  styleUrls: ['./custom-tab.component.scss']
})
export class CustomTabComponent {
  constructor(private changeDetectorRef: ChangeDetectorRef) {

  }
  public update() {
    this.changeDetectorRef.detectChanges();
  }
}

Option 2: Implement a wrapper component

To integrate your custom form field seamlessly into the Angular application's change detection and rendering cycle, you'll create a wrapper component (named 'custom-tab.component.ts'). This wrapper will encapsulate your custom form field component and ensure that the Angular zone mechanism (NgZone) is applied for efficient change detection within the custom component.

A custom component wrapper (custom-tab.component.ts):

import { DomPortalOutlet, ComponentPortal } from '@angular/cdk/portal';
import { ApplicationRef, Component, createComponent, ComponentRef, ElementRef, Injector, OnDestroy, OnInit, ViewChild, ViewContainerRef, EnvironmentInjector } from '@angular/core';
import { CustomTabContentComponent } from './custom-tab-content.component';

@Component({
  selector: 'app-custom-tab',
  templateUrl: './custom-tab.component.html',
  styleUrls: ['./custom-tab.component.scss']
})
export class CustomTabComponent implements OnInit, OnDestroy {
  @ViewChild("container", { static: true }) containerRef!: ElementRef<HTMLDivElement>;
  private component!: ComponentRef<CustomTabContentComponent>;
  constructor(private appRef: ApplicationRef, private injector: EnvironmentInjector){
  }
  ngOnInit() {
    this.component = createComponent(CustomTabContentComponent, { environmentInjector: this.injector });
    this.containerRef.nativeElement.appendChild(this.component.location.nativeElement);
    this.appRef.attachView(this.component.hostView);
  }
  ngOnDestroy() {
    if (this.component !== null && this.component !== undefined) {
      this.appRef.detachView(this.component.hostView);
      this.component.destroy();
    }
  }
}

The custom field is now declared as a custom-tab-content component. The updated demo is available at creator-with-material-custom-tab_option2.zip.