ExpressionChangedAfterItHasBeenCheckedError上角6,而使用

问题描述 投票:3回答:1

我有角6以下错误

零件

<mat-tab-group [(selectedIndex)]="selectedTabIndex">
  <mat-tab label="Add">
    <ng-template matTabContent>
      <form [formGroup]="entityAddFormGroup">
        <dynamic-material-form [group]="entityAddFormGroup" [model]="entityAddFormCtlModelArray"></dynamic-material-form>
        <button (click)="buttonAddEntityClicked(entityAddFormGroup.value)">Add</button>
      </form>
    </ng-template>
  </mat-tab>
  <mat-tab *ngIf="entityEditFormGroup && currentEntity" label="Edit #{{currentEntity.id}}">
    <!-- TODO correct bug with -->
    <ng-template matTabContent>
      <form [formGroup]="entityEditFormGroup">
        <!-- <h2 i18n>Edit #{{currentEntity.id}}</h2> -->
        <dynamic-material-form [group]="entityEditFormGroup" [model]="entityEditFormCtlModelArray"></dynamic-material-form>
        <button (click)="buttonEditEntityClicked(entityEditFormGroup.value)">Save</button>
      </form>
    </ng-template>
  </mat-tab>
</mat-tab-group>

当我删除第二个mat-tab错误消失

到其他类似的组件,我并没有把2种形式为mat-tab-groupmat-tab并没有这个错误。

花了,同时发现其中的区别。

错误到控制台

ExpressionChangedAfterItHasBeenCheckedError:它检查后表达发生了变化。前值 'NG-有效:真'。当前值: 'NG-有效:假'。

环境

Angular CLI: 6.2.8
Node: 11.9.0
OS: linux x64
Angular: 

TS文件(出口类ElectricityRateListComponent扩展SelectableEntitiesListComponent)

public displayedColumnsArray = [
    'select',
    'id',
    'energyRate',
    'mainTransmissionRate',
    'publicServiceRate',
    'validityStartDate',
    'validityEndDate',
    'electricityType',
    'city',
]; // Gives the order of the columns
public statusMessage: string = ''
public selectedTabIndex: number = 0


protected _elTypeAddSelect: DBEntitySelect<Enumerate> //ElectricityType: Enumerate
protected _elTypeEditSelect: DBEntitySelect<Enumerate> //ElectricityType: Enumerate

protected _cityAddSelect: DBEntitySelect<Enumerate> //City: Enumerate
protected _cityEditSelect: DBEntitySelect<Enumerate> //City: Enumerate

constructor(
    protected router: Router,
    public messageService: MessageService,
    protected logger: LoggerService,
    protected route: ActivatedRoute,
    protected entitiesService: ElectricityRateService,
    protected enumeratesService: EnumerateService,
    protected formBuilder: FormBuilder,
    public formService: DynamicFormService,
    iconRegistry: MatIconRegistry,
    sanitizer: DomSanitizer,
    // private location: Location
) {
    super(router, messageService, logger, route, entitiesService, formBuilder, formService, iconRegistry, sanitizer, new ElectricityRate());

    (...)
}



/**
* Common to add and edit forms
*
* @param aStrangeObject
*/
protected _getCommonFormControlModel(aStrangeObject: Enumerate): DynamicFormControlModel[] {
    let lEntity: ElectricityRate = new ElectricityRate().deserialize(
    aStrangeObject
    )
    console.debug(
    "-----getAddFormControlModel->",
    aStrangeObject,
    lEntity.validityStartDate.constructor.name,
    lEntity.validityEndDate.constructor.name
    )
    const result: DynamicFormControlModel[] = [
    new DynamicInputModel({
        id: "energyRate",
        label: "Energy Rate",
        value: lEntity.energyRate,
        inputType: DYNAMIC_FORM_CONTROL_INPUT_TYPE_NUMBER,
        min: ElectricityRate.MIN_ELECTRICITY_RATE,
        max: ElectricityRate.MAX_ELECTRICITY_RATE,
        placeholder: "Energy Rate"
    }),
    new DynamicInputModel({
        id: "mainTransmissionRate",
        label: "Transmission Rate",
        inputType: DYNAMIC_FORM_CONTROL_INPUT_TYPE_NUMBER,
        min: ElectricityRate.MIN_ELECTRICITY_RATE,
        max: ElectricityRate.MAX_ELECTRICITY_RATE,
        value: lEntity.mainTransmissionRate.toString(),
        placeholder: "Transmission Rate"
    }),
    new DynamicInputModel({
        id: "publicServiceRate",
        label: "Public Service Rate",
        inputType: DYNAMIC_FORM_CONTROL_INPUT_TYPE_NUMBER,
        min: ElectricityRate.MIN_ELECTRICITY_RATE,
        max: ElectricityRate.MAX_ELECTRICITY_RATE,
        value: lEntity.publicServiceRate.toString(),
        placeholder: "Public Service Rate"
    }),
    new DynamicInputModel({
        id: "validityStartDate",
        label: "Validity start date",
        inputType: DYNAMIC_FORM_CONTROL_INPUT_TYPE_DATE,
        maxLength: 10,
        value: MiscHelper.dateToDynamicInputDate(lEntity.validityStartDate),
        placeholder: "Validity start date"
    }),
    new DynamicInputModel({
        id: "validityEndDate",
        label: "Validity end date",
        inputType: DYNAMIC_FORM_CONTROL_INPUT_TYPE_DATE,
        value: MiscHelper.dateToDynamicInputDate(lEntity.validityEndDate),
        placeholder: "Validity end date"
    })
    ]
    return result

}

/**
* called by SelectableEntitiesListComponent->onInit
*
* @param aStrangeObject
*/
protected _getAddFormControlModel(aStrangeObject: Enumerate): DynamicFormControlModel[] {
    //console.debug('getAddFormControlModel->aStrangeObject:', aStrangeObject)
    let lEntity: Enumerate = new Enumerate().deserialize(aStrangeObject)
    console.debug('-----getAddFormControlModel->aStrangeObject, lEntity:', aStrangeObject, lEntity)
    //Add form fields
    const result: DynamicFormControlModel[] = this._getCommonFormControlModel(aStrangeObject)
    result.push(this._elTypeAddSelect.asDynamicInputModel())
    result.push(this._cityAddSelect.asDynamicInputModel())
    return result
}



/**
* Built onRowClicked
*
* @param anId
* @param aStrangeObject can be a row of dataTable
*/
protected _getEditFormControlModel(aStrangeObject: Enumerate): DynamicFormControlModel[] {
    console.log('getEditFormControlModel:', aStrangeObject)
    let result = this._getCommonFormControlModel(aStrangeObject)
    result = result.concat(DBEntity.getIdFormControlModel('id', aStrangeObject))
    result.push(this._elTypeEditSelect.asDynamicInputModel())
    result.push(this._cityEditSelect.asDynamicInputModel())
    // console.log('getEditFormControlModel:', result)

    return result
}

出口抽象类SelectableEntitiesListComponent扩展EntityListComponent {

public ngOnInit() {
    super.ngOnInit()
    this._setSelects()
}

/**
* redefine
*/
public onReloadClicked(anEvent) {
    super.onReloadClicked(anEvent)
    this._setSelects()
}


/**
* redefine
*/
public afterEntityUpdatedSucessful(){
    super.afterEntityUpdatedSucessful()
    this._setSelects()
}

/**
*
*/
protected abstract _setSelects()


}

出口抽象类EntityListComponent扩展ReloadableComponent实现AfterViewInit,OnInit的{

protected _currentEntity: D = null // Set to null and not undefined cause of list.component.html tests for it  reason explained https://stackoverflow.com/questions/5076944/what-is-the-difference-between-null-and-undefined-in-javascript
protected abstract displayedColumnsArray: Array<string>; // Gives the order of the columns
public entitiesListTitle = this.constructor.name

// FORMS
entityAddFormGroup: FormGroup;
entityAddFormCtlModelArray: DynamicFormControlModel[];
entityEditFormGroup: FormGroup;
entityEditFormCtlModelArray: DynamicFormControlModel[];

// DATA TABLE variables
dataSource: SseEntityDataSource<D>;
selectionModel = new SelectionModel<D>(true, []);
@ViewChild(MatPaginator) paginator: MatPaginator;
@ViewChild(MatSort) sort: MatSort;

constructor(
    protected router: Router,
    public messageService: MessageService,
    protected logger: LoggerService,
    protected route: ActivatedRoute,
    protected entitiesService: SseEntityService<D>,
    protected formBuilder: FormBuilder,
    public formService: DynamicFormService,
    iconRegistry: MatIconRegistry,
    sanitizer: DomSanitizer,
    public entityPrototype: DBEntity,
    // private location: Location
) {
    super(
    iconRegistry,
    sanitizer,
    )
    if (entityPrototype === undefined || entityPrototype == null){
    throw new Error('constructor error, create me in the caller entityPrototype!')
    }
}

/**
* calls this._getAddFormControlModel() and adds it to entityAddFormCtlModelArray
*/
public ngOnInit() {
    // console.debug('ngOnInit called')
    if (this.entityPrototype === undefined){
    throw new Error('entity-list.component->ngOnInit-> this.entityPrototype is undefined, set it into constructor of descendant')
    }
    this.entitiesListTitle = StringHelper.camelCaseToSpaces(this.constructor.name.replace('Component', ''))


    this.dataSource = new SseEntityDataSource<D>(this.logger, this.entitiesService, this, this.entityPrototype);
    this.setMessageService();
    this.entityAddFormCtlModelArray = this._getAddFormControlModel(this.entityPrototype);
    this.entityAddFormGroup = this.formService.createFormGroup(this.entityAddFormCtlModelArray);

    this.dataSource.loadEntities()
}

protected abstract _getCommonFormControlModel(aStrangeObject: DBEntity): DynamicFormControlModel[]
protected abstract _getAddFormControlModel(aStrangeObject: DBEntity): DynamicFormControlModel[]

public ngAfterViewInit() {
    this.dataSource.paginator = this.paginator;
    this.dataSource.sort = this.sort;
//    this.cdr.detectChanges();
}

get currentEntity(): D {
    return this._currentEntity;
}

set currentEntity(value: D) {
    this._currentEntity = value;
    this.entitiesService.currentEntity = value;
}

/**
* Require dataSource not null
*/
public loadDatasourceWithPaginator() {
    // Init currentEntityId
    try {
    this.dataSource.loadEntities();
    } catch (e) {
    this.messageService.add(new UserMessage('Error loading entities', e, UserMessageType.Error));
    throw e;
    }
}

public applyFilter(filterValue: string) {
    filterValue = filterValue.trim(); // Remove whitespace
    filterValue = filterValue.toLowerCase(); // Datasource defaults to lowercase matches
    this.dataSource.filter = filterValue;
}


/**
* Require dataSource not null
*/
public setMessageService() {
    this.dataSource.messagesForUsers$.subscribe(
    usrMessage => {
        this.messageService.add(usrMessage);
    }
    );
}


abstract onRowClicked(row: any): void;

public buttonAddEntityClicked(dataValues: any) {
    console.debug('buttonAddEntityClicked-------->from Entitylist.components dataValues:', dataValues);
    let lEntity = this.entityPrototype.deserialize(dataValues, false)
    console.debug('buttonAddEntityClicked-------->from Entitylist.components lEntity:', lEntity);
    console.debug('buttonAddEntityClicked-------->from Entitylist.components lEntity.toJSON():', lEntity.toJSON());

    this.entitiesService.addEntityFromFormData(lEntity.toJSON()).subscribe(
    lData => {
        const msg = `Entity added successfully`;
        this.messageService.add(new UserMessage(msg, lData, UserMessageType.Info));
        this.afterEntityUpdatedSucessful()
    },
    lError => {
        const msg = `Entity add Error`;
        console.error('buttonAddEntityClicked->Error:', lError)
        this.messageService.add(new UserMessage(msg, lError, UserMessageType.Error));
        throw lError;
    }
    );
}

public afterEntityUpdatedSucessful(){
    this.loadDatasourceWithPaginator();
}


public buttonEditEntityClicked(jsonStringValues: string) {
    this.logger.debug('buttonAddEntityClicked-> from Entitylist.components:', jsonStringValues);
    let lEntity = this.entityPrototype.deserialize(jsonStringValues, false)
    this.logger.debug('buttonEditEntityClicked-> Entitylist.components: jsonStringValues, lEntity:', jsonStringValues, lEntity);

    this.entitiesService.updateEntityFromFormData(lEntity.toJSON()).subscribe(
    lData => {
        const msg = `Entity updated successfully`;
        this.messageService.add(new UserMessage(msg, lData, UserMessageType.Info));
        this.afterEntityUpdatedSucessful()
    },
    lError => {
        const msg = `Entity update Error`;
        console.error('buttonEditEntityClicked->Error:', lError)
        this.messageService.add(new UserMessage(msg, lError, UserMessageType.Error));
        throw lError;
    }
    );
}


public buttonRemoveSelectedRowsClicked() {
    let toReloadObservable: Observable<Object> = null;
    this.selectionModel.selected.forEach(item => {
    this.logger.debug('Deleting selected item:', item);
    toReloadObservable = this.entitiesService.deleteFromId(item.id);
    toReloadObservable.subscribe(
        data => {
        const msg = `Entity ${item.id} deleted successfully`;
        this.messageService.add(new UserMessage(msg, data, UserMessageType.Info));
        this.afterEntityUpdatedSucessful()
        },
        error => {
        const msg = `Error while deleting entity ${item.id}`;
        this.messageService.add(new UserMessage(msg, error, UserMessageType.Error));
        throw error;
        }
    );
    });
    this.selectionModel = new SelectionModel<D>(true, []);
    this._currentEntity = null;
    // When all are removed reload data source
}

public onReloadClicked(anEvent) {
    this.loadDatasourceWithPaginator();
}

public buttonMasterToggleClicked() {
    this.isAllSelected() ?
    this.selectionModel.clear() :
    this.dataSource.data.forEach(row => this.selectionModel.select(row));
}

public sampleAddButtonClicked() {
    Constants.SAMPLE_COMPANIES_JSON_DATA.forEach( (entity) => {
    // console.log('sampleAddButtonClicked', JSON.stringify(entity));
    this.buttonAddEntityClicked(entity);
    });
}

public isAllSelected() {
    const numSelected = this.selectionModel.selected.length;
    const numRows = this.dataSource.entitiesCount();
    return numSelected === numRows;
}

protected _updateEditFormFields(toUpdate: any) {
    console.log("updateEditFormFields->toUpdate, model", toUpdate, this.entityEditFormCtlModelArray);
    Object.entries(toUpdate).forEach(([key, value]) => {
    // console.log('updateEditFormFields->setting key', key, 'value:', value);
    const inputModel = this.formService.findById(key, this.entityEditFormCtlModelArray) as DynamicInputModel;

    if (inputModel == null) {
        throw new Error('updateEditFormFields->InputModel is null, key ' + key + ' not found into entityEditFormCtlModel val:' + value );
    }
    inputModel.valueUpdates.next(value as string)//If not reloading recreate the formGroup with this.entityAddFormGroup = this.formService.createFormGroup(this.entityAddFormCtlModelArray);
    // inputModel.valueUpdates.subscribe(value => console.log('new value assigned to field: ', newVal));
    // inputModel.disabledUpdates.next(true);
    });
}


}

this post高度相关

angular
1个回答
3
投票

原因的错误背后:

我不认为这个错误与毡片。这个错误通常与最初的开发阶段,那里有生命周期的钩子一样ngAfterViewInit的用途。直接从角Blog-报价

这种类型的错误通常显示了超出了最初的发展阶段,当我们开始在我们的模板一些表情,和我们通常开始使用一些生命周期挂钩像AfterViewInit的。

你不能使用ngAfterViewInit(分页程序参考),并立即修改数据源,因为这将触发数据,但斜角视图生成过程的进一步的修改还没有完成,所以它不是清楚,如果变量的值,你使用如在应该修改所述一个或前一个模板的表达式。

可能的解决方案:

为了解决这个问题,我们需要让角第一显示器设置为false装载标志数据。

所以,一个可能的解决方案是在setTimeOut排序的数据源之前,使用delay(0)ngAfterViewInit()

之所以出现这种解决方案的工作:

  • 的标志的初始值是假的,并且因此负载指示将不会被最初显示。
  • ngAfterViewInit()被调用,但数据源不立即调用,所以没有负载指示的修改将同步通过ngAfterViewInit()进行。
  • 角然后完成渲染视图,并反映在屏幕上的最新数据变化,以及JavaScript虚拟机依次完成。
  • 一会儿后,setTimeout()调用(也可用于内部delay(0))被触发,才把数据源加载其数据。
  • 装载标志设置为true,并且负载指示现在将显示。
  • 角完成渲染视图,并反映在屏幕上,这引起负载指示灯得到显示上的最新变化。

资源:

要了解这个问题更深入,请看看this documentation从我所引用的位置。这里的整体情况与为例进行说明。

您还可以看看this答案,其中地方ngAfterContentInit使用ngAfterViewInit被列为另一种可能的解决方案。

我希望这会有所帮助。

更新:

替代方案:

正如在评论中提到@jo_va,也有这个问题的其他可能的解决方案。

  1. 在地方使用setTimeOut()的,changeDetector.detectChanges()也可以使用。 在这里,我直接从@ jo_va的建议说明: 这可能是有趣的,何况changeDetector.detectChanges()其中changeDector是注入ChangeDetectorRef。这是另一种广泛使用的解决了这个问题,我觉得比setTimeout的清洁剂。
  2. Promise.resolve可能是另一种替代setTimeout
© www.soinside.com 2019 - 2024. All rights reserved.