代码之家  ›  专栏  ›  技术社区  ›  Pipo

使用<mat tab>

  •  3
  • Pipo  · 技术社区  · 6 年前

    角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 错误消失了

    在其他类似的组件中,我没有将这两个窗体放到 mat-tab-group 垫片 不要有这个错误。

    花了一段时间去发现不同之处。

    控制台出错

    ExpressionChangedAfterithasbeencheckedError:表达式在检查后已更改。上一个值:“ng valid:true”。当前值:“ng valid:false”。

    环境

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

    TS文件(导出类ElectricityRateList组件扩展SelectableEntitiesList组件)

    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扩展可重载组件实现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

    1 回复  |  直到 6 年前
        1
  •  5
  •   Tasnim Fabiha    6 年前

    错误背后的原因:

    我认为这个错误与mat tab无关。这个错误通常与初始开发阶段有关,在这个阶段中,生命周期钩子的使用如下 ngAfterViewInit . 直接从角度博客引用-

    这种类型的错误通常出现在初始开发之后 阶段,当我们在模板中开始有更多的表达式时, 我们通常已经开始使用一些生命周期挂钩,比如 事后。

    不能在ngafterviewInit()中使用paginator引用并立即修改数据源,因为这将触发对数据的进一步修改,但角度视图生成过程尚未完成,因此不清楚模板中用作表达式的变量值是否应修改该变量或前一个变量。一个。

    可能的解决方案:

    为了解决这个问题,我们需要先让Angular显示加载标志设置为false的数据。

    因此,一个可能的解决方案是 setTimeOut delay(0) 在对数据源进行排序之前 ngAfterViewInit() .

    此解决方案工作的原因:

    • 标志的初始值为假,因此加载指示器不会最初显示。
    • ngafterview初始化() 获取调用,但不立即调用数据源,因此不修改加载指示器 将通过同步方式 ngafterview初始化() .

    • 然后,Angular完成视图的渲染,并在屏幕上反映最新的数据更改,javascript vm回合完成。

    • 过了一会儿, setTimeout() 呼叫(也用于内部 延迟(0) )被触发,只有在数据源加载 数据。
    • 加载标志设置为真,此时将显示加载指示器。
    • 角度完成对视图的渲染,并反映屏幕上的最新更改,从而使加载指示器 显示。

    资源:

    为了更深入地理解这个问题,请看一下 this documentation 从我引用的地方。这里用例子解释了整个场景。

    你也可以看看 this 答案何处 使用 ngAfterContentInit 代替 后进 列为另一个可能的解决方案。

    我希望这会有所帮助。

    更新:

    替代解决方案:

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

    1. 代替使用 setTimeOut() , changeDetector.detectChanges() 也可以使用。

      这里我直接从@jo_va的建议中解释:

      提到changedetector.detectchanges()可能很有趣。 其中changedector是注入的changedetectorref。这是另一个 广泛应用于解决这个问题,我认为比 设置超时。

    2. 而且 Promise.resolve 可能是另一种选择 setTimeout .