Исключение ExpressionChangedAfterItHasBeenCheckedError, если предварительно выбрать реактивную форму внутри диалога - PullRequest
0 голосов
/ 19 октября 2019

Я пытаюсь реализовать диалог с реактивной формой, с инициализацией данных динамической формы. Диалог находится в подкомпоненте, вызванном моим основным компонентом.

Когда я пытаюсь предварительно выбрать / заполнить компоненты формы, я получаю эту ошибку:

ExpressionChangedAfterItHasBeenCheckedError: Expression has changed after it was checked. Previous value: 'status-success: false'. Current value: 'status-success: true'.

Проблема возникает только тогда, когда я предварительно выбираю компонент nb-datepicker со значением, если используется нольв качестве значения все в порядке.

Вот мой код:

основной компонент

<i (click)="editdialog.bookingToEdit(booking)" class="fas fa-edit"></i>
....
<ngx-booking-edit #editdialog></ngx-booking-edit>

субкомпонент с диалоговым окном

@Component({
  selector: 'ngx-booking-edit',
  templateUrl: './booking-edit.component.html',
  styleUrls: ['./booking-edit.component.scss'],
})
export class BookingEditComponent implements AfterViewInit {

  @Output() updateBookingData = new EventEmitter<Booking>();
  @ViewChild('dialogedit') editDialog: TemplateRef<any>;

  private initialData: InitialDataResponse;
  private booking: Booking;
  public bookingForm: FormGroup;
  public newContractPartner = false;

  constructor(private formBuilder: FormBuilder,
              private  dialogService: NbDialogService,
              private initialDataService: InitialDataService,
              private toasterService: ToasterService) {
  }

  ngAfterViewInit() {
    this.loadInitData();

  }

  private loadInitData() {

    // REST call init data
    this.initialDataService.getInitalData()
      .map(resp => resp)
      .subscribe(
        (data) => {
          this.initialData = data.body;
        },
        (err) => {
          console.error('status', err.status);
        },
      );
  }

  //this method is called from the main component with an booking object
  bookingToEdit(bookingToEdit: Booking) {
    this.booking = bookingToEdit;
    this.initForm();

    this.dialogService.open(this.editDialog, {});
  }

  private initForm() {

    const today: Date = new Date();
    this.bookingForm = this.formBuilder.group({
      description: [this.booking.description, [Validators.required, MyValidators.nospaceValidator]],
      amount: [this.booking.amount, [Validators.required, MyValidators.validateAmount]],
      date: [today, Validators.required],
      capitalSource: [this.booking.capitalSource.id, Validators.required],
      category: [this.booking.subcategory.id, Validators.required],
      contractpartner: [this.booking.contractPartner.name, [Validators.required, MyValidators.nospaceValidator]],
      privatCheckbox: [this.booking.privatBookingOfUser],
    });

  }


}

субдиалог шаблона

<toaster-container></toaster-container>
<ng-template #dialogedit let-data let-ref="dialogRef">
  <form [formGroup]="bookingForm">
    <nb-card>
      <nb-card-header>Buchung bearbeiten</nb-card-header>
      <nb-card-body>


        <div class="row">

          <div class="col-md-3">
            <div class="form-group">
              <div class="input-group input-group-sm">
                <input nbInput fullWidth
                       placeholder="Buchungsdatum"
                       formControlName="date"
                       style="line-height: 1rem;"
                       [ngClass]="{
                                                'status-danger': bookingForm.controls.date.invalid && bookingForm.controls.date.dirty,
                                                'status-success': bookingForm.controls.date.valid && bookingForm.controls.date.dirty }"
                       [nbDatepicker]="formpicker">
                <nb-datepicker #formpicker></nb-datepicker>
              </div>
            </div>
          </div>

          <div class="col-md-6">
            <div class="form-group input-group-sm">
              <input [ngClass]="{
                                  'status-danger': bookingForm.controls.description.invalid && bookingForm.controls.description.dirty,
                                  'status-success': bookingForm.controls.description.valid && bookingForm.controls.description.dirty }"
                     class="status" fullWidth id="description" nbInput placeholder="Beschreibung"
                     formControlName="description" type="text"/>
            </div>
          </div>


          <div class="col-md-2">
            <div class="form-group input-group-sm">
              <input [ngClass]="{
                                  'status-danger': bookingForm.controls.amount.invalid && bookingForm.controls.amount.dirty,
                                  'status-success': bookingForm.controls.amount.valid && bookingForm.controls.amount.dirty }"
                     class="status" fullWidth nbInput step="0.10" type="number"
                     formControlName="amount"
                     placeholder="Betrag" type="text"/>

            </div>
          </div>


        </div>
        <div class="row">


          <div class="col-md-5">
            <div class="form-group input-group-sm">
              <input nbInput fullWidth id="typeahead-format" placeholder="Vertragspartner"
                     formControlName="contractpartner"
                     type="text" placeholder="Vertragspartner"
                     [ngClass]="{
                                  'newContractPartner': newContractPartner,
                                  'status-danger': bookingForm.controls.contractpartner.invalid && bookingForm.controls.contractpartner.dirty,
                                  'status-success': bookingForm.controls.contractpartner.valid && bookingForm.controls.contractpartner.dirty }"
                     [ngbTypeahead]="searchContractPartner" class="status"/>
            </div>
          </div>

          <div class="col-md-4">
            <div class="form-group input-group-sm">
              <nb-select nbSelect fullWidth [size]="formSize"
                         formControlName="category" placeholder="Kategorie"
                         [ngClass]="{
                                  'status-danger': bookingForm.controls.category.invalid && bookingForm.controls.category.dirty,
                                  'status-success': bookingForm.controls.category.valid && bookingForm.controls.category.dirty }">
                <option disabled [value]=null>Kategorie</option>
                <nb-option-group *ngFor="let category of initialData?.categories" title="{{category.name}}">
                  <nb-option *ngFor="let subcategory of category.subcategories" [value]="subcategory.id">
                    {{subcategory.name}}
                  </nb-option>
                </nb-option-group>
              </nb-select>
            </div>
          </div>

          <div class="col-md-3">
            <div class="form-group input-group-sm">
              <nb-select fullWidth [size]="formSize"
                         formControlName="capitalSource"
                         [ngClass]="{
                                  'status-danger': bookingForm.controls.capitalSource.invalid && bookingForm.controls.capitalSource.dirty,
                                  'status-success': bookingForm.controls.capitalSource.valid && bookingForm.controls.capitalSource.dirty }">
                <nb-option disabled [value]=null>Kapitalquelle</nb-option>
                <nb-option *ngFor="let capitalSource of initialData?.capitalSources" [value]="capitalSource.id">
                  {{capitalSource.description}}
                </nb-option>
              </nb-select>
            </div>
          </div>

          <div class="col-md-2">
            <div class="form-group input-group-sm" style="color: #a1a1e5 !important;">

              <nb-checkbox formControlName="privatCheckbox">Privat</nb-checkbox>
            </div>
          </div>
        </div>


      </nb-card-body>
      <nb-card-footer>
        <div class="col-md-12">
          <div class="form-group input-group-sm">
            <div _ngcontent-c40="" class="container-btn">
              <button [disabled]="!bookingForm.valid" nbButton>
                Buchung speichern
              </button>
            </div>

          </div>
        </div>
      </nb-card-footer>
    </nb-card>
  </form>
</ng-template>

Я попытался вызвать метод dialog.open внутри setTimeout и попытался предварительно заполнить данные формы внутри resolvedPromise.then (() => {...}, но безуспешно.

Кто-нибудь знает, где может быть проблема?

StackBlitz: stackblitz.com / edit / github-lv7hp3

1 Ответ

0 голосов
/ 19 октября 2019

Выполнить change detection явно после изменения. Добавьте этот фрагмент кода в свой компонент, и это должно решить вашу проблему.

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

constructor(private cdRef:ChangeDetectorRef) {}

ngAfterViewChecked()
{
  this.cdRef.detectChanges();
}

Angular Docs link => https://angular.io/api/core/ChangeDetectionStrategy

GitHub проблема => https://github.com/angular/angular/issues/15634

...