telerik / kendo-angular

Issue tracker - Kendo UI for Angular
http://www.telerik.com/kendo-angular-ui/
Other
469 stars 217 forks source link

Getting ExpressionChangedAfterItHasBeenCheckedError while select item in a cascaded DropDownList #3872

Closed joerg-bergner closed 1 year ago

joerg-bergner commented 1 year ago

Describe the bug Implemented a dialog with cascading DropDownLists.

To Reproduce

ERROR Error: NG0100: ExpressionChangedAfterItHasBeenCheckedError: Expression has changed after it was checked. Previous value for 'k-form-field-disabled': 'true'. Current value: 'false'. Find more at https://angular.io/errors/NG0100

Expected behavior Run without any problems

Browser

import { Component, OnInit } from '@angular/core';
import { DialogContentBase, DialogRef } from '@progress/kendo-angular-dialog';
import { FormControl, FormGroup } from '@angular/forms';
import { environment } from '../../../../../../environments/environment';

interface DropDownListItem {
    id: string | null;
    title: string;
}

interface Module {
    id: string;
    title: string;
}

interface Action {
    id: string;
    title: string;
    module: Module;
}

@Component({
    selector:    'app-cascading-test',
    template: `
                  <kendo-dialog-titlebar (close)="onCancel()">
                      <div class="d-flex align-items-center full-width">
                          <div class="me-auto">Cascading-Test</div>
                      </div>
                  </kendo-dialog-titlebar>

                  <form [formGroup]="form" (submit)="onSubmit()">
                      <div class="row">
                          <div class="col">
                              <kendo-formfield>
                                  <kendo-floatinglabel class="full-width" text="Typ" [optional]="false">
                                      <kendo-dropdownlist formControlName="blogEntryType"
                                                          valueField="id"
                                                          textField="title"
                                                          [data]="filteredBlogEntryTypes"
                                                          [filterable]="true"
                                                          [defaultItem]="defaultDDLItem"
                                                          (filterChange)="onHandleBlogEntryTypeFilter($event)"
                                                          (valueChange)="onBlogEntryTypeChanged($event)">
                                      </kendo-dropdownlist>
                                  </kendo-floatinglabel>
                              </kendo-formfield>

                              <kendo-formfield>
                                  <kendo-floatinglabel class="full-width" text="Module" [optional]="false">
                                      <kendo-dropdownlist formControlName="module"
                                                          valueField="id"
                                                          textField="title"
                                                          [data]="filteredModules"
                                                          [filterable]="true"
                                                          [defaultItem]="defaultDDLItem"
                                                          (filterChange)="onHandleModuleFilter($event)"
                                                          (valueChange)="onHandleModuleChanged($event)">
                                      </kendo-dropdownlist>
                                  </kendo-floatinglabel>
                              </kendo-formfield>

                              <kendo-formfield>
                                  <kendo-floatinglabel class="full-width" text="Action" [optional]="false">
                                      <kendo-dropdownlist formControlName="action"
                                                          valueField="id"
                                                          textField="title"
                                                          [data]="filteredActions"
                                                          [filterable]="true"
                                                          [defaultItem]="defaultDDLItem"
                                                          (filterChange)="onHandleActionFilter($event)">
                                      </kendo-dropdownlist>
                                  </kendo-floatinglabel>
                              </kendo-formfield>
                          </div>
                      </div>
                  </form>

                  <kendo-dialog-actions>
                      <button type="button" (click)="onSubmit()" [primary]="true" [disabled]="isFormInvalid" kendoButton>Save</button>
                      <button type="button" (click)="onCancel()" [primary]="false" [disabled]="false" kendoButton>Cancel</button>
                  </kendo-dialog-actions>
              `,
    styles:      []
})
export class CascadingTestComponent extends DialogContentBase implements OnInit {

    // FIELDS
    // --------------------------------------------------------------------------------------------
    isInDevMode: boolean = !environment.production;
    isLoadingDone: boolean = false;

    form = new FormGroup({
        blogEntryType: new FormControl<DropDownListItem | undefined>(undefined),
        module:        new FormControl<Module | undefined>({ value: undefined, disabled: true }),
        action:        new FormControl<Action | undefined>({ value: undefined, disabled: true })
    });

    defaultDDLItem: DropDownListItem = { id: null, title: 'PLEASE SELECT...' };

    blogEntryTypes: DropDownListItem[] = [
        { id: 'MODULE', title: 'Module' },
        { id: 'MEMO', title: 'Memo' },
        { id: 'AIM', title: 'Aim' }
    ];
    filteredBlogEntryTypes: DropDownListItem[] = [ ...this.blogEntryTypes ];

    modules: Module[] = [
        { id: '1', title: 'Module 1' },
        { id: '2', title: 'Module 2' },
        { id: '3', title: 'Module 3' },
        { id: '4', title: 'Module 4' }
    ];
    filteredModules: Module[] = [ ...this.modules ];

    actions: Action[] = [
        { id: '1', title: 'Action 1', module: this.modules[ 0 ] },
        { id: '2', title: 'Action 2', module: this.modules[ 0 ] },
        { id: '3', title: 'Action 3', module: this.modules[ 1 ] },
        { id: '4', title: 'Action 4', module: this.modules[ 2 ] },
        { id: '4', title: 'Action 4', module: this.modules[ 3 ] }
    ];
    filteredActions: Action[] = [ ...this.actions ];

    // COMPONENT
    // MWMWMWMWMWMWMWMWMWMWMWMWMWMWMWMWMWMWMWMWMWMWMWMWMWMWMWMWMWMWMWMWMWMWMWMWMWMWMWMWMWMWMWMWMWMW

    constructor (
        dialogRef: DialogRef
    ) {
        super(dialogRef);
    }

    ngOnInit (): void {
    }

    // ACTIONS
    // MWMWMWMWMWMWMWMWMWMWMWMWMWMWMWMWMWMWMWMWMWMWMWMWMWMWMWMWMWMWMWMWMWMWMWMWMWMWMWMWMWMWMWMWMWMW

    onSubmit () {

    }

    onCancel () {
        this.dialog.close();
    }

    onHandleBlogEntryTypeFilter (value: string) {
        this.filteredBlogEntryTypes = this.blogEntryTypes.filter((item: DropDownListItem) => item.title!.toLowerCase().indexOf(value.toLowerCase()) !== -1);
    }

    onHandleModuleFilter (value: string) {
        this.filteredModules = this.modules.filter((item: Module) => item.title!.toLowerCase().indexOf(value.toLowerCase()) !== -1);
    }

    onHandleActionFilter (value: string) {
        this.filteredActions = this.actions.filter((item: Action) => item.title!.toLowerCase().indexOf(value.toLowerCase()) !== -1);
    }

    onBlogEntryTypeChanged (item: DropDownListItem) {
        if (item.id === 'MODULE') {
            this.form.get('module')?.enable();
        }
    }

    onHandleModuleChanged (module: Module) {
        this.filteredActions = this.actions.filter((item: Action) => item.module.id === module.id);
        this.form.get('action')?.enable();
    }

    // VIEW HELPER
    // MWMWMWMWMWMWMWMWMWMWMWMWMWMWMWMWMWMWMWMWMWMWMWMWMWMWMWMWMWMWMWMWMWMWMWMWMWMWMWMWMWMWMWMWMWMW

    get isFormInvalid (): boolean {
        return !(this.isLoadingDone && this.form.valid);
    }
}

output from npm ls --depth 0:

├── __ngcc_entry_points__.json@ extraneous
├── @angular-devkit/build-angular@14.2.9
├── @angular/animations@14.2.10
├── @angular/cdk@14.2.7
├── @angular/cli@14.2.9
├── @angular/common@14.2.10
├── @angular/compiler-cli@14.2.10
├── @angular/compiler@14.2.10
├── @angular/core@14.2.10
├── @angular/forms@14.2.10
├── @angular/localize@14.2.10
├── @angular/material@14.2.7
├── @angular/platform-browser-dynamic@14.2.10
├── @angular/platform-browser@14.2.10
├── @angular/router@14.2.10
├── @apollo/client@3.6.9
├── @fortawesome/fontawesome-pro@6.1.1
├── @progress/kendo-angular-buttons@8.2.2
├── @progress/kendo-angular-charts@7.3.2
├── @progress/kendo-angular-common@3.2.2
├── @progress/kendo-angular-dateinputs@7.1.6
├── @progress/kendo-angular-dialog@7.1.5
├── @progress/kendo-angular-dropdowns@7.2.4
├── @progress/kendo-angular-editor@4.2.0
├── @progress/kendo-angular-excel-export@5.0.2
├── @progress/kendo-angular-grid@7.4.2
├── @progress/kendo-angular-icons@2.0.3
├── @progress/kendo-angular-indicators@3.0.2
├── @progress/kendo-angular-inputs@10.1.2
├── @progress/kendo-angular-intl@4.1.1
├── @progress/kendo-angular-l10n@4.0.1
├── @progress/kendo-angular-label@4.0.2
├── @progress/kendo-angular-layout@7.2.0
├── @progress/kendo-angular-menu@4.1.1
├── @progress/kendo-angular-navigation@2.1.1
├── @progress/kendo-angular-notification@4.0.1
├── @progress/kendo-angular-pager@4.0.6
├── @progress/kendo-angular-pdf-export@4.0.1
├── @progress/kendo-angular-popup@5.0.2
├── @progress/kendo-angular-progressbar@3.1.2
├── @progress/kendo-angular-ripple@4.0.1
├── @progress/kendo-angular-toolbar@6.1.2
├── @progress/kendo-angular-tooltip@4.1.1
├── @progress/kendo-angular-treeview@7.1.5
├── @progress/kendo-angular-upload@9.0.4
├── @progress/kendo-data-query@1.6.0
├── @progress/kendo-drawing@1.17.2
├── @progress/kendo-file-saver@1.1.1
├── @progress/kendo-licensing@1.2.2
├── @progress/kendo-svg-icons@0.6.0
├── @progress/kendo-theme-default@5.12.0
├── @progress/kendo-theme-material@5.12.0
├── @types/jasmine@3.6.11
├── @types/node@12.20.55
├── apollo-angular@4.1.0
├── bootstrap@5.2.2
├── codelyzer@6.0.2
├── date-fns@2.29.3
├── graphql@15.8.0
├── hammerjs@2.0.8
├── jasmine-core@4.5.0
├── jasmine-spec-reporter@7.0.0
├── karma-chrome-launcher@3.1.1
├── karma-coverage@2.2.0
├── karma-jasmine-html-reporter@2.0.0
├── karma-jasmine@5.1.0
├── karma@6.4.1
├── ngx-auto-unsubscribe@3.0.1
├── ngx-cookie-service@14.0.1
├── protractor@7.0.0
├── rxjs@7.5.7
├── ts-node@8.3.0
├── tslib@2.4.0
├── tslint@6.1.3
├── typescript@4.6.4
├── xng-breadcrumb@8.0.2
└── zone.js@0.11.6
Jebati commented 1 year ago

Encountered this problem after the update.

michaelmarcuccio commented 1 year ago

This problem is specifically caused by the following: <kendo-formfield> This issue is a duplicate of the following: #3783

dtopalov commented 1 year ago

Closing as a duplicate of https://github.com/telerik/kendo-angular/issues/3783