如何使异步数据与角度中的动态表单模式一起工作

时间:2017-07-24 14:16:21

标签: angular typescript angular-forms

我正在使用Angular中的动态表单模式,我们拥有各种形式。这对我们来说是一种方便的方式,因为我们只需要在ngOnInit中定义控件,它将动态构建我们需要的表单。但是,有些表单必须初始化值,并且可以使用async / await检索某些值。

这是动态表单的问题,因为当我初始化异步数据时,它会在控制台上抛出错误,并且表单没有显示在视图上。

我试图在ngOnInit上添加async并等待异步数据。如示例代码所示:

async ngOnInit() {
    const pageUrl = await this.fooService.getTabUrl();
    const security = this.barService.getSecurity();

    const controls: Array<ControlBase<any>> = [
        new ControlTextbox({
            key: "url",
            order: 0,
            readonly: true,
            type: "text",
            value: pageUrl
        }),
        new ControlDropdown({
            key: "security",
            label: "Security",
            order: 2,
            options: security,
            type: "dropdown",
            value: security[0].id
        })
    ];
    this.controls = controls;
}

我还在视图上添加了一个异步管道:

<form class="{{formClass}}" (ngSubmit)="onSubmit()" [formGroup]="form" role="form">
    <app-form-control *ngFor="let ctrl of controls | async" [control]="ctrl  | async" [form]="form | async"></app-form-control>
    <div class="form-group">
        <button type="submit" class="btn btn-primary btn-block" [disabled]="!form.valid">{{btnText}}</button>
    </div>
</form>

然而,这不起作用。

有关详细信息,请参阅屏幕截图。 enter image description here

附加代码:

export class FormControlComponent implements OnInit {
    @Input() public control: ControlBase<string | boolean | undefined>;
    @Input() public form: FormGroup;

    constructor() { }

    get valid() {
        return this.form.controls[this.control.key].valid;
    }

    get invalid() {
        return !this.form.controls[this.control.key].valid && this.form.controls[this.control.key].touched;
    }

    ngOnInit() { }
}

export class DynamicFormComponent implements OnInit {
    @Input() public controls: Array<ControlBase<any>> = [];
    @Input() public btnText = "Submit";
    @Output() public formSubmit: EventEmitter<any> = new EventEmitter<any>();
    public form: FormGroup;

    constructor(public _controlService: FormControlService) { }

    ngOnInit() {
        const sortedControls = this.controls.sort((a, b) => a.order - b.order);
        this.form = this._controlService.toControlGroup(sortedControls);
    }

    onSubmit(): void {
        this.formSubmit.emit(this.form.value);
    }
}

export class FormControlService {
    constructor() { }

    public toControlGroup(controls: Array<ControlBase<any>>) {
        const group: any = {};

        controls.forEach(control => {
            const validators: any = [];

            // Required
            if (control.required) {
                validators.push(Validators.required);
            }

            // remove for brevity

            group[control.key] = new FormControl(control.value || "", validators);
        });

        return new FormGroup(group);
    }
}

我还是新手并且学习Angular。关于在初始化异步数据时如何克服这个问题的任何建议?

1 个答案:

答案 0 :(得分:4)

如果表单元素正在等待任何异步数据,或者表单格式特别复杂,我通常会在表单元素上添加ngIf语句。基本上正在发生的事情,我认为你已经知道这一点,是表单没有及时初始化以击败正在呈现给屏幕的页面。根据具体情况,有时可能会抛出错误,也可能不会。添加:

<form *ngIf="form">

您将强制角度等待form实例化。我看到的另一个问题可能是由于您在使用异步数据的表单实例化过程中预先填充了formcontrol。这通常不是很好。更好的方法(除非你有方便的数据),是实例化一个emtpy表单,然后调用以获取你想要放在表单中的数据。然后,使用get()setValue()方法填充表单控件。对于用户而言,差异甚至不明显,但在我看来,这是预先填充表单的更可靠的方法。例如,您可以执行以下操作来预填充用户角色编辑表单的“用户角色名称”:

<强>一些-component.component.ts

ngOnInit() {
    // Instantiate your form.
    buildUserRoleFormGroup();
}

buildUserRoleFormGroup() {

    // INSTANTIATE YOUR FORM HERE.....

    // Then populate it.
    populateUserRoleForm();
}

populateUserRoleForm() {
    this._UserRolesService.getUserRole(this.userRoleID)
        .subscribe(_UserRole => {
            // Save the data to a component property
            this.userRole = _UserRole.data;

            // Get the user role name form control
            let userRoleName = this.createUserRoleForm.get('userRoleName')
            // Set the user role name form control value
            userRoleName.setValue(this.userRole.userRoleName);
            },
            _Error => {
                console.log(_Error);
            })
    }