在我的离子项目(最后一个版本和最后一个角度版本)中,我使用动态表单来从我的休息 api 构建动态表单。
这个想法是我的后端服务器以 json 格式的表单输入和我需要的所有属性(整个表单)返回。目的是创建动态表单,如果在我的后端编辑表单(例如添加输入或属性),我不必更新和“重新构建”我的 ionic 应用程序以便进行更改考虑到。
这就是我按照动态表单流程/教程所做的。
首先我在ionic app.module.ts
文件中声明 的导入import { ReactiveFormsModule } from '@angular/forms';
,然后在导入中导入模块:
@NgModule({
declarations: [
MyApp,
HomePage,
// ...
FormBaseComponent,
DynamicCustomRestFormComponent,
],
imports: [
BrowserModule,
HttpModule,
IonicModule.forRoot(MyApp),
ReactiveFormsModule,
IonicStorageModule.forRoot(),
],
// ...
然后我在名为的文件夹中创建了表单基本模型(相对于问题模型):/src/models/
form-base.ts
export class FormBase<T>{
value: T;
key: string;
label: string;
required: boolean;
controlType: string;
name: string;
fullName: string;
id: number;
disabled: boolean;
attr: Array<any>;
errors: Array<any>;
constructor(options: {
value?: T,
key?: string,
label?: string,
required?: boolean,
controlType?: string,
name?: string,
fullName?: string,
id?: number,
disabled?: boolean,
attr?: Array<any>,
errors?: Array<any>,
} = {} ) {
this.value = options.value;
this.key = options.key || '';
this.label = options.label || '';
this.required = !!options.required;
this.controlType = options.controlType || '';
this.name = options.name || '';
this.fullName = options.fullName || '';
this.id = options.id;
this.disabled = !!options.disabled;
this.attr = options.attr;
this.errors = options.errors;
}
/src/models/form-components
例如,我在名为的子文件夹中创建了 sub*form 组件text.js
(相对于文档中的 question-textbox.ts 文件):
import { FormBase } from '../form-base';
export class Text extends FormBase<string> {
controlType = 'text';
type: string;
constructor(options: {} = {}) {
super(options);
this.type = options['type'] || 'text';
}
}
我有/src/providers/rest-api/ folder
名为的服务form-service.ts
:
import { Validators,
FormControl,
FormGroup } from '@angular/forms';
import { HttpErrorResponse } from '@angular/common/http';
import { Injectable, Inject } from '@angular/core';
import { FormBase } from '../../models/form-base';
import { Text } from '../../models/form-components/text';
import { Http } from '@angular/http';
import { FORM_API_SUFFIX } from "../services.constants";
import { EnvVariables } from '../../app/environment-variables/environment-variables.token';
import 'rxjs/add/operator/map';
@Injectable()
export class FormProvider {
protected url: URL;
protected suffix: string;
public formInputs: FormBase<any>[] = [];
constructor(public http: Http, @Inject(EnvVariables) public envVariables) { }
// fetch form data by rest api
fetchFormRestView(name: string = null) {
let formName = name;
this.suffix = FORM_API_SUFFIX + formName;
this.url = new URL(this.suffix, this.envVariables.apiEndpoint);
this.http.get(this.url.href)
.subscribe(
data => {
let formData = this.getChildrenField(JSON.parse(data['_body'].toString()));
return this.buildForm(this.createInputFiedlType(formData));
},
(err: HttpErrorResponse) => {
if (err.error instanceof Error) {
console.log('An error occurred:', err.error.message);
} else {
console.log(`the fetchFormFields function get a server returned code ${err.status});
}
}
);
}
// iterate on each form field from data recovered by the rest form api
getChildrenField(data) {
let fieldsArray: Array<Object> = [];
// loop on the keys directly
Object.keys(data['children']).forEach( key => {
fieldsArray[key] = data['children'][key];
});
return fieldsArray;
}
createInputFiedlType(data) {
let fieldInfo: Array<Object> = [];
// console.info("form field in array here => ", data);
// The Object.keys() method returns an array of a given object's own enumerable properties, in the same order as that provided by a for...in loop (the difference being that a for-in loop enumerates properties in the prototype chain as well).
Object.keys(data).forEach( key => {
// console.log(key); //key
fieldInfo = data[key]['vars'];
// console.log(fieldInfo); //value
// swith to manage and instanciate input type field for ForBase Class
switch (fieldInfo['block_prefixes'][1]) {
// ezpublish/symfony choice field
case 'choice' :
this.formInputs.push(
// I have many type of field, I put only the text component but it's the same logic for all
// text field
default:
this.formInputs.push(
new Text({
key: fieldInfo['full_name'],
name: fieldInfo['name'],
fullName: fieldInfo['full_name'],
label: fieldInfo['label'],
value: fieldInfo['value'],
required: fieldInfo['required'],
id: fieldInfo['id'],
disabled: fieldInfo['disabled'],
attr: fieldInfo['attr'],
errors: fieldInfo['errors']
}),
);
break;
}
});
return this.formInputs;
}
// build the form
buildForm(formInputs: FormBase<any>[]) {
let group: any = {};
formInputs.forEach(input => {
group = ({
[input.key] :
input.required ? new FormControl(input.value || '', Validators.required)
: new FormControl(input.value || ''),
});
});
return new FormGroup(group);
}
}
作为文档中问题表单组件ionic generate component
的描述,我通过运行命令(ionic cli)来创建我的组件。中的一个组件/src/components/form-base/
。
form-base.ts
:
import { Component, Input, OnInit } from '@angular/core';
import { FormGroup } from '@angular/forms';
import { FormBase } from '../../models/form-base';
import { FormProvider } from '../../providers/rest-api/form-service';
@Component({
selector: 'form-base',
templateUrl: 'form-base.html',
providers: [ FormProvider ]
})
export class FormBaseComponent implements OnInit {
@Input() formInputs: FormBase<any>[] = [];
form: FormGroup;
payLoad: string = '';
constructor(private formService: FormProvider) { }
/**
* @method ngOnInit
* @return {FormGroup}
*/
ngOnInit() {
return this.form = this.formService.buildForm(this.formInputs);
}
/**
* @method onSubmit
* @return {[type]}
*/
onSubmit() {
return this.payLoad = JSON.stringify(this.form.value);
}
}
form-base.html
:
<div>
<form (ngSubmit)="onSubmit()" [formGroup]="form">
<div *ngFor="let input of formInputs">
<app-form-input [input]="input" [form]="form"></app-form-input>
</div>
<div>
<button ion-button type="submit" [disabled]="!form.valid">Valider</button>
</div>
</form>
<div *ngIf="payLoad">
<strong>Saved the following values</strong><br>{{payLoad}}
</div>
</div>
然后我让另一个组件扩展form-base
名为的组件dynamic-custom-rest-form
:
ts:
import { Component, Input } from '@angular/core';
import { FormGroup } from '@angular/forms';
import { FormBase } from '../../models/form-base';
@Component({
selector: 'app-form-input',
templateUrl: './dynamic-custom-rest-form.html'
})
export class DynamicCustomRestFormComponent {
@Input() input: FormBase<any>;
@Input() form: FormGroup;
}
html:
<div [formGroup]="form">
<label [attr.for]="input.key">{{input.label}} :</label>
<div [ngSwitch]="input.controlType">
<input
*ngSwitchCase="'text'"
[formControlName]="input.key"
[formControl]="input.key"
[id]="input.key"
[type]="input.controlType"
[value]="input.value"
[name]="input.name"
[placeholder]="input.name"
/>
</div>
<div class="errorMessage" *ngIf="!isValid">{{input.label}} is required</div>
</div>
最后,在我的页面中,这里是我的离子视图的页面组件,我有这个:
src/pages/home/home.ts
:
import { NavController, ViewController, App } from 'ionic-angular';
import { Component } from '@angular/core';
import { Storage } from '@ionic/storage';
import { FormProvider } from '../../providers/rest-api/form-service';
@Component({
selector: 'page-home',
templateUrl: 'home.html',
providers: [
FormProvider,
],
})
export class HomePage {
constructor(
public navCtrl: NavController,
public loginService: LoginProvider,
public formService: FormProvider,
private storage: Storage,
private app: App,
private viewCtrl: ViewController)
{
this.viewCtrl.showBackButton(false);
this.formService.fetchFormRestView();
}
}
src/pages/home/home.html
:
<ion-header>
<!-- header html -->
</ion-header>
<ion-content padding>
<form-base [formInputs]="formService.formInputs"></form-base>
</ion-content>
调用表单服务时出现两个错误
第一的:
错误:找不到名称为“input[inputName]”的控件
第二个错误:
TypeError:无法分配给“input [inputName]”上的属性“validator”:不是对象
通过遵循 Angular 文档,我真的不明白我的错误在哪里。
谢谢。