Angular2 - 动态更新表单模型

时间:2016-04-21 13:42:22

标签: typescript angular

我正在使用Angular2.beta15,我有一个表单,我试图根据用户从选择输入中选择的内容进行变异。

所以当使用以下代码触发偶数时,我正在更改formModel (ControlGroup)

public onActionChange(event: Event): void {
    this.jobAction = +(<HTMLSelectElement>event.srcElement).value;
    this._updateFormModel();
}
private _updateFormModel() {

    const optionals: string[] = ['jobPath', 'jobHost', 'password', 'username'];

    optionals.forEach( s => {
        this.log(`Setting null validator on ${s}`);
        this.formModel.controls[s].validator = Validators.nullValidator;
    });

    switch (this.jobAction ) {
        case 1: // email
            this.formModel.controls['username'].validator = Validators.required;
            break;
    }
}

逻辑是我将根据用户从下拉框中选择的内容隐藏我不需要的表单字段。如果用户稍后从下拉框中选择不同的项目,这将保留值,但我只是删除所有表单验证nullValidator,以便表单有效。

以下是我的组件视图:

<div class="row">
<div class="col-xs-12">
    <form [ngFormModel]="formModel" (ngSubmit)="onSubmit()" class="form-horizontal" novalidate>
        <div class="panel panel-default">
            <div *ngIf="isAddOperation" class="panel-heading">Add a New Job</div>
            <div *ngIf="!isAddOperation" class="panel-heading">Modify Job</div>
            <div *ngIf="formModel && ! busy" class="panel-body">

                <global-error-message [isError]="isError" [message]="messageDescription"></global-error-message>

                <div class="form-group">
                    <label for="name" class="col-xs-12 col-sm-3 control-label">Name</label>
                    <div class="col-xs-12  col-sm-9">
                        <input ngControl="name" type="text" id="name" class="form-control" placeholder="Job Name" required autofocus />
                    </div>
                    <div class="col-sm-9 col-sm-offset-3 col-xs-12">
                        <error-message controlName="name"></error-message>
                    </div>
                </div>

                <div class="form-group">
                    <label for="description" class="col-xs-12 col-sm-3 control-label">Description</label>
                    <div class="col-xs-12  col-sm-9">
                        <textarea ngControl="description" id="description" class="form-control" placeholder="Description"></textarea>
                    </div>
                    <div class="col-sm-9 col-sm-offset-3 col-xs-12">
                        <error-message controlName="description"></error-message>
                    </div>
                </div>

                <div class="form-group">
                    <label for="query" class="col-xs-12 col-sm-3 control-label">Query</label>
                    <div class="col-xs-12  col-sm-9">
                        <textarea ngControl="query" id="query" class="form-control" placeholder="select * from ..."></textarea>
                    </div>
                    <div class="col-sm-9 col-sm-offset-3 col-xs-12">
                        <error-message controlName="query"></error-message>
                    </div>
                </div>

                <div class="form-group">
                    <label for="dataSource" class="col-xs-12 col-sm-3 control-label">Data Source</label>
                    <div class="col-xs-12  col-sm-9">
                         <select id="dataSource" class="form-control" ngControl="dataSource">
                            <option value="">=== Select One ===</option>
                            <option *ngFor="#d of dataSources" [value]="d.id">{{d.name}}</option>
                        </select>
                    </div>
                    <div class="col-sm-9 col-sm-offset-3 col-xs-12">
                        <error-message controlName="dataSource"></error-message>
                    </div>
                </div>

                <div class="form-group">
                    <label for="includeHeaders" class="col-xs-12 col-sm-3 control-label">Header Row</label>
                    <div class="col-xs-12  col-sm-9">
                        <input id="includeHeaders" type="checkbox" ngControl="includeHeaders"/>
                    </div>
                    <div class="col-sm-9 col-sm-offset-3 col-xs-12">
                        <error-message controlName="includeHeaders"></error-message>
                    </div>
                </div>

                <div class="form-group">
                    <label for="publicJob" class="col-xs-12 col-sm-3 control-label">Public Job</label>
                    <div class="col-xs-12  col-sm-9">
                        <input id="publicJob" type="checkbox" ngControl="publicJob"/>
                    </div>
                    <div class="col-sm-9 col-sm-offset-3 col-xs-12">
                        <error-message controlName="publicJob"></error-message>
                    </div>
                </div>

                <div class="form-group">
                    <label for="jobAction" class="col-xs-12 col-sm-3 control-label">Action</label>
                    <div class="col-xs-12  col-sm-9">
                         <select controlName="jobAction" id="jobAction" (change)="onActionChange($event)" class="form-control"  >
                            <option value="0">No Action / Manual Execution</option>
                            <option value="1">Email</option>
                            <option value="2">FTP</option>
                            <option value="3">SFTP</option>
                        </select>
                    </div>
                    <div class="col-sm-9 col-sm-offset-3 col-xs-12">
                        <error-message controlName="jobAction"></error-message>
                    </div>
                </div>

                <div *ngIf="jobAction == 1" class="form-group">
                    <label for="username" class="col-xs-12 col-sm-3 control-label">Recipient Email</label>
                    <div class="col-xs-12  col-sm-9">
                         <input ngControl="username" type="text" id="username" class="form-control" placeholder="email@domain.com"  />
                    </div>
                    <div class="col-sm-9 col-sm-offset-3 col-xs-12">
                        <error-message controlName="username"></error-message>
                    </div>
                </div>

                <div class="col-sm-offset-3  col-sm-9 col-xs-12">
                    <input [disabled]="!formModel.valid" class="btn btn-primary" *ngIf="isAddOperation" value="Add" type="submit" />
                    <input [disabled]="!formModel.valid" class="btn btn-primary" *ngIf="!isAddOperation" value="Update" type="submit" />
                </div>
            </div>
        </div>
    </form> 
</div>

我遇到的问题是我得到了:

angular2.dev.js:23925 EXCEPTION: Expression '!formModel.valid in JobEditComponent@151:31' has changed after it was checked. Previous value: 'false'. Current value: 'true' in [!formModel.valid in JobEditComponent@151:31]BrowserDomAdapter.logError @ angular2.dev.js:23925BrowserDomAdapter.logGroup @ angular2.dev.js:23936ExceptionHandler.call @ angular2.dev.js:1320(anonymous function) @ angular2.dev.js:12857schedulerFn @ angular2.dev.js:13264SafeSubscriber.__tryOrUnsub @ Rx.js:10775SafeSubscriber.next @ Rx.js:10730Subscriber._next @ Rx.js:10690Subscriber.next @ Rx.js:10667Subject._finalNext @ Rx.js:11191Subject._next @ Rx.js:11183Subject.next @ Rx.js:11142EventEmitter.emit @ angular2.dev.js:13245NgZone._zoneImpl.ng_zone_impl_1.NgZoneImpl.onError @ angular2.dev.js:13666NgZoneImpl.inner.inner.fork.onHandleError @ angular2.dev.js:2143ZoneDelegate.handleError @ angular2-polyfills.js:394Zone.runGuarded @ angular2-polyfills.js:300NgZoneImpl.runInner @ angular2.dev.js:2155NgZone.run @ angular2.dev.js:13749(anonymous function) @ angular2.dev.js:12956schedulerFn @ angular2.dev.js:13264SafeSubscriber.__tryOrUnsub @ Rx.js:10775SafeSubscriber.next @ Rx.js:10730Subscriber._next @ Rx.js:10690Subscriber.next @ Rx.js:10667Subject._finalNext @ Rx.js:11191Subject._next @ Rx.js:11183Subject.next @ Rx.js:11142EventEmitter.emit @ angular2.dev.js:13245NgZone._checkStable @ angular2.dev.js:13689NgZone._zoneImpl.ng_zone_impl_1.NgZoneImpl.onLeave @ angular2.dev.js:13656NgZoneImpl.inner.inner.fork.onInvoke @ angular2.dev.js:2128ZoneDelegate.invoke @ angular2-polyfills.js:389Zone.runGuarded @ angular2-polyfills.js:297NgZoneImpl.runInner @ angular2.dev.js:2155NgZone.run @ angular2.dev.js:13749outsideHandler @ angular2.dev.js:13594ZoneDelegate.invokeTask @ angular2-polyfills.js:423Zone.runTask @ angular2-polyfills.js:320ZoneTask.invoke @ angular2-polyfills.js:490ListPicker._handleMouseUp @ about:blank:546
angular2.dev.js:23925 ORIGINAL EXCEPTION: Expression '!formModel.valid in JobEditComponent@151:31' has changed after it was checked. Previous value: 'false'. Current value: 'true'

所以我正在做一些改变检测不喜欢的事情,但我不确定如何从组件动态更改表单。我知道调用enableProdMode()可能会删除错误,但我想弄清楚我做错了什么。对不起,我是Angular2的新手!

**更新**

所以我能够让表单工作,但它看起来很笨拙,所以我仍然在寻找一种更好的方法来实现模型可能会根据用户响应而改变的形式。

以下是我必须做的事情:

 enableProdMode();  // so change detection wouldn't yell at me

然后在我的组件中,我必须包含/排除组件,我不确定它到底是什么,它似乎是Validators.required的重复或特殊情况。有什么我想念的吗,我做错了什么,改变检测还在抱怨?

public onActionChange(event: Event): void {
    this.jobAction = +(<HTMLSelectElement>event.srcElement).value;
    this._updateFormModel();
}

private _updateFormModel() {

    const optionals: string[] = ['jobPath', 'jobHost', 'password', 'username'];

    optionals.forEach( s => {
        this.formModel.controls[s].validator = Validators.nullValidator;
        this.formModel.exclude(s);
    });

    switch (this.jobAction ) {
        case 0:
            break;
        case 1: // email
            this.formModel.controls['username'].validator = Validators.required;
            this.formModel.include('username');         
            break;
    }

}

1 个答案:

答案 0 :(得分:0)

我认为您应该利用ChangeDetectorRef类及其detectChanges方法,而不是禁用开发模式。

export class MyComponent {
  constructor(private _cdr:ChangeDetectorRef) {
  }

  _updateFormModel() {
    (...)
    this._cdr.detectChanges();
  }
}