反应形式的 p-multiselect
p-multiselect in a reactive form
我有一个从 json 文件生成的表单。我确实收到了一个看起来像这样的 json :
{
"lang": "en",
"name": "user-opinion",
"localName": "My opinion",
"controls": [
{
"name": "hobbies",
"label": "My hobbies",
"value": "",
"type": "select",
"selected": "Select your favorite hobby",
"multi": true,
"selectOptions": [
{ "key": "tennis", "value": "Tennis" },
{ "key": "golf", "value": "Golf" },
{ "key": "bike", "value": "Vélo"}
],
"validators": {}
},
{
"name": "softwares",
"label": "Logiciels",
"value": "",
"type": "select",
"selected": "Choisissez vos IDE",
"multi": true,
"selectOptions": [
{ "key": "eclipse", "value": "Eclipse" },
{ "key": "intellij", "value": "Intellij" },
{ "key": "webstorm", "value": "Webstorm"}
],
"validators": {}
},
{
"name": "comments",
"label": "Comments",
"value": "",
"type": "textarea",
"validators": {}
},
{
"name": "chips",
"label": "Chips",
"values": [],
"type": "chips",
"validators": {}
},
{
"name": "rating",
"label": "Rating",
"rating": 0,
"stars": 5,
"type": "rating",
"validators": {}
},
{
"name": "countries",
"label": "Pays",
"value": "",
"type": "cascadeSelect",
"cascadeSelectOptions": [
{
"name": "Australie",
"code": "AU",
"lvl1elt": [
{
"name": "Nouvelles Galles du Sud",
"lvl2elt": [
{"cname": "Sydney", "code": "A-SY"},
{"cname": "Newcastle", "code": "A-NE"},
{"cname": "Wollongong", "code": "A-WO"}
]
},
{
"name": "Queensland",
"lvl2elt": [
{"cname": "Brisbane", "code": "A-BR"},
{"cname": "Townsville", "code": "A-TO"}
]
}
]
},
{
"name": "Canada",
"code": "CA",
"lvl1elt": [
{
"name": "Quebec",
"lvl2elt": [
{"cname": "Montreal", "code": "C-MO"},
{"cname": "Quebec City", "code": "C-QU"}
]
},
{
"name": "Ontario",
"lvl2elt": [
{"cname": "Ottawa", "code": "C-OT"},
{"cname": "Toronto", "code": "C-TO"}
]
}
]
},
{
"name": "Etats Unis",
"code": "US",
"lvl1elt": [
{
"name": "Californie",
"lvl2elt": [
{"cname": "Los Angeles", "code": "US-LA"},
{"cname": "San Diego", "code": "US-SD"},
{"cname": "San Francisco", "code": "US-SF"}
]
},
{
"name": "Floride",
"lvl2elt": [
{"cname": "Jacksonville", "code": "US-JA"},
{"cname": "Miami", "code": "US-MI"},
{"cname": "Tampa", "code": "US-TA"},
{"cname": "Orlando", "code": "US-OR"}
]
},
{
"name": "Texas",
"lvl2elt": [
{"cname": "Austin", "code": "US-AU"},
{"cname": "Dallas", "code": "US-DA"},
{"cname": "Houston", "code": "US-HO"}
]
}
]
}
],
"validators": {}
}
]
}
这个json是用API得到的,用URL取回它:
ngOnInit(): void {
// loading json response from back
// console.log(this.router.url);
let currentRoute = this.router.url;
this.apiService.getTranslatedForm(currentRoute).subscribe(
(response: any) => {
this.jsonResponse = response
this.buildForm((this.jsonResponse.controls))
},
(error: any) => {
console.log(error)
},
() => {
console.log("Done");
}
)
}
表格的每个元素都是
buildForm(controls: JsonFormControls[]): void {
// we will loop all entries of JsonFormControls objects from the controls array
console.log("controls", controls);
let repeatedInputFormGroup = this.fb.group({});
for (const control of controls) {
// some inputs have one or more validators: input can be required, have a min length, x length...
const controlValidators = [];
// a control has a key and a value.
// example: "validators": { "required": true, "minLength": 10 }
// this snippet is reusable: can be optimized if used in many forms
for (const [key, value] of Object.entries(control.validators)) {
switch (key) {
case 'min':
controlValidators.push(Validators.min(value));
break;
case 'max':
controlValidators.push(Validators.max(value));
break;
case 'required':
if (value) {
controlValidators.push(Validators.required);
}
break;
case 'requiredTrue':
if (value) {
controlValidators.push(Validators.requiredTrue);
}
break;
case 'email':
if (value) {
controlValidators.push(Validators.email);
}
break;
case 'minLength':
controlValidators.push(Validators.minLength(value));
break;
case 'maxLength':
controlValidators.push(Validators.maxLength(value));
break;
case 'pattern':
controlValidators.push(Validators.pattern(value));
break;
case 'nullValidator':
if (value) {
controlValidators.push(Validators.nullValidator);
}
break;
default:
break;
}
}
// we must handle repeated inputs
const formControl = this.fb.control(control.value, controlValidators);
if (control.repeat) {
this.form.addControl(control.name, this.fb.array([formControl]));
} else {
this.form.addControl(control.name, formControl);
}
}
}
为此 json 我确实进行了 API 调用以检索 JSON。我循环遍历 JSON 对象以显示表单元素。
<!-- creating the form and loop -->
<span *ngIf="form != null">
<h3>{{ jsonResponse.localName }}</h3>
<form [formGroup]="form" (ngSubmit)="onSubmit()">
<div *ngFor="let control of jsonResponse.controls">
<div class="mb-3">
<span *ngIf="control.label != '' && control.type !== 'toggle' && control.type !== 'checkbox'">
<label class="form-label">{{ control.label }}</label>
</span>
<!-- for inputs that are not repeatable -->
<span *ngIf="inputTypes.includes(control.type) && control.repeat === false">
<input
[type]="control.type"
formControlName="{{control.name}}"
[value]="control.value"
class="form-control"
/>
</span>
<!-- for inputs that are repeatable -->
<span *ngIf="inputTypes.includes(control.type) && control.repeat === true">
<div formArrayName="{{ control.name }}">
<div
*ngFor="let item of getControls(control.name); let id = index"
class="input-group mb-3">
<input class="form-control" formControlName="{{ id }}"/>
<button
type="button"
class="btn btn-outline-secondary"
(click)="deleteInputItem(control.name, id)">
Remove
</button>
</div>
<button
type="button"
class="btn btn-primary"
(click)="addInputItem(control.name)">
Add entry
</button>
</div>
</span>
<!-- text area -->
<span *ngIf="control.type === 'textarea'">
<textarea
[formControlName]="control.name"
[value]="control.value"
class="form-control"
></textarea>
</span>
<!-- select -->
<span *ngIf="control.type === 'select' && control.multi == false">
<select
[formControlName]="control.name"
class="form-select form-select-lg mb-3">
<option selected>{{ control.selected }}</option>
<option
*ngFor="let option of control.selectOptions"
value="{{ option.key }}"> {{ option.value }}
</option>
</select>
</span>
<!--select multi -->
<span *ngIf="control.type === 'select' && control.multi == true">
<!-- <p>{{ control | json }}</p>-->
<!-- {{ control.selectOptions | json }}-->
<p-multiSelect
[formControlName]="control.name"
[options]="control.selectOptions"
[(ngModel)]="selection"
optionLabel="value"></p-multiSelect>
</span>
<!-- cascade select -->
<span *ngIf="control.type === 'cascadeSelect'">
<!-- the optionGroupChildren depends on object passed in control.cascadeSelectOptions -->
<p-cascadeSelect [options]="control.cascadeSelectOptions"
formControlName = "{{ control.name }}"
optionLabel="cname"
optionGroupLabel="name"
[optionGroupChildren]="['lvl1elt', 'lvl2elt']"
[style]="{'minWidth': '14rem', 'maxWidth': '100%'}"
placeholder="Select a City"
>
<ng-template pTemplate="option" let-option>
<div class="country-item">
<img *ngIf="option.states"/>
<i class="pi pi-compass p-mr-2" *ngIf="option.lvl2elt"></i>
<i class="pi pi-map-marker p-mr-2" *ngIf="option.cname"></i>
<span>{{option.cname || option.name}}</span>
</div>
</ng-template>
</p-cascadeSelect>
</span>
<!-- chips -->
<span *ngIf="control.type === 'chips'">
<p-chips
[(ngModel)]="control.values"
formControlName = "{{ control.name }}"
>
</p-chips>
</span>
<!-- range -->
<span *ngIf="control.type === 'range'">
<p-slider
formControlName = "{{ control.name }}">
</p-slider>
</span>
<span *ngIf="control.type === 'rangeSlide'">
<p-slider
formControlName = "{{ control.name }}"
[min] = "getValue(control.options.min)"
[max] ="getValue(control.options.max)"
[step] ="getValue(control.options.step)"
[range]="true"
>
</p-slider>
</span>
<!-- handling checkboxes -->
<span *ngIf="control.type === 'checkbox'">
<p-checkbox [formControlName]="control.name"
value="{{ control.name }}"></p-checkbox>
<label class="form-check-label"> {{ control.label }}</label>
</span>
<!-- toggle -->
<span *ngIf="control.type === 'toggle'">
<p-inputSwitch
[formControlName]="control.name">
</p-inputSwitch>
</span>
<!-- rating -->
<span *ngIf="control.type === 'rating'">
<p-rating
[cancel]="false"
[formControlName]="control.name"></p-rating>
</span>
<!-- radio buttons -->
<span *ngIf="control.type === 'radio'">
<div class="form-check form-check-inline" *ngFor="let option of control.radioOptions">
<input class="form-check-input"
type="radio" name="{{control.name }}"
id="{{option.key}}-{{option.value}}"
[formControlName]="control.name"
value="{{option.value}}">
<label class="form-check-label" for="{{option.key}}-{{option.value}}">{{option.value}}</label>
</div>
</span>
</div>
</div>
<div>
<button
class="btn btn-primary"
type="submit">
Submit
</button>
</div>
</form>
</span>
<span *ngIf="form != null">
{{ this.form.value | json }}
</span>
我可以使用许多不同的输入(文本、文本区域...)和 primeng 功能,例如 p-multiselect;
<span *ngIf="control.type === 'select' && control.multi == true">
<p>{{ control | json }}</p>
{{ control.selectOptions | json }}
<p-multiSelect
[formControlName]="control.name"
[options]="control.selectOptions"
[(ngModel)]="selection"
optionLabel="value"></p-multiSelect>
</span>
</div>
在这个阶段,我的不同表单元素是动态显示的。我确实有每个输入的选项。我的问题是,出于测试目的,我还显示了表单中的值:
<span *ngIf="form != null">
{{ this.form.value | json }}
</span>
我确实为两个输入获得了我为另一个输入设置的值。在业余爱好中,我没有“Eclipse”值,但它是在两个输入的爱好形式值中设置的。
一不小心解决了一整天的问题。
<span *ngIf="control.type === 'select' && control.multi == true">
<p-multiSelect
[formControlName]="control.name"
[options]="control.selectOptions"
optionLabel="value">
</p-multiSelect>
</span>
我在两个多选中的选择都使用 ngModel 添加到数组中。我只是删除了这些东西:[(ngModel)]="selection"
,现在它就像一个魅力。当我将它发送到数据库时,我只需要清理我的表单。
我有一个从 json 文件生成的表单。我确实收到了一个看起来像这样的 json :
{
"lang": "en",
"name": "user-opinion",
"localName": "My opinion",
"controls": [
{
"name": "hobbies",
"label": "My hobbies",
"value": "",
"type": "select",
"selected": "Select your favorite hobby",
"multi": true,
"selectOptions": [
{ "key": "tennis", "value": "Tennis" },
{ "key": "golf", "value": "Golf" },
{ "key": "bike", "value": "Vélo"}
],
"validators": {}
},
{
"name": "softwares",
"label": "Logiciels",
"value": "",
"type": "select",
"selected": "Choisissez vos IDE",
"multi": true,
"selectOptions": [
{ "key": "eclipse", "value": "Eclipse" },
{ "key": "intellij", "value": "Intellij" },
{ "key": "webstorm", "value": "Webstorm"}
],
"validators": {}
},
{
"name": "comments",
"label": "Comments",
"value": "",
"type": "textarea",
"validators": {}
},
{
"name": "chips",
"label": "Chips",
"values": [],
"type": "chips",
"validators": {}
},
{
"name": "rating",
"label": "Rating",
"rating": 0,
"stars": 5,
"type": "rating",
"validators": {}
},
{
"name": "countries",
"label": "Pays",
"value": "",
"type": "cascadeSelect",
"cascadeSelectOptions": [
{
"name": "Australie",
"code": "AU",
"lvl1elt": [
{
"name": "Nouvelles Galles du Sud",
"lvl2elt": [
{"cname": "Sydney", "code": "A-SY"},
{"cname": "Newcastle", "code": "A-NE"},
{"cname": "Wollongong", "code": "A-WO"}
]
},
{
"name": "Queensland",
"lvl2elt": [
{"cname": "Brisbane", "code": "A-BR"},
{"cname": "Townsville", "code": "A-TO"}
]
}
]
},
{
"name": "Canada",
"code": "CA",
"lvl1elt": [
{
"name": "Quebec",
"lvl2elt": [
{"cname": "Montreal", "code": "C-MO"},
{"cname": "Quebec City", "code": "C-QU"}
]
},
{
"name": "Ontario",
"lvl2elt": [
{"cname": "Ottawa", "code": "C-OT"},
{"cname": "Toronto", "code": "C-TO"}
]
}
]
},
{
"name": "Etats Unis",
"code": "US",
"lvl1elt": [
{
"name": "Californie",
"lvl2elt": [
{"cname": "Los Angeles", "code": "US-LA"},
{"cname": "San Diego", "code": "US-SD"},
{"cname": "San Francisco", "code": "US-SF"}
]
},
{
"name": "Floride",
"lvl2elt": [
{"cname": "Jacksonville", "code": "US-JA"},
{"cname": "Miami", "code": "US-MI"},
{"cname": "Tampa", "code": "US-TA"},
{"cname": "Orlando", "code": "US-OR"}
]
},
{
"name": "Texas",
"lvl2elt": [
{"cname": "Austin", "code": "US-AU"},
{"cname": "Dallas", "code": "US-DA"},
{"cname": "Houston", "code": "US-HO"}
]
}
]
}
],
"validators": {}
}
]
}
这个json是用API得到的,用URL取回它:
ngOnInit(): void {
// loading json response from back
// console.log(this.router.url);
let currentRoute = this.router.url;
this.apiService.getTranslatedForm(currentRoute).subscribe(
(response: any) => {
this.jsonResponse = response
this.buildForm((this.jsonResponse.controls))
},
(error: any) => {
console.log(error)
},
() => {
console.log("Done");
}
)
}
表格的每个元素都是
buildForm(controls: JsonFormControls[]): void {
// we will loop all entries of JsonFormControls objects from the controls array
console.log("controls", controls);
let repeatedInputFormGroup = this.fb.group({});
for (const control of controls) {
// some inputs have one or more validators: input can be required, have a min length, x length...
const controlValidators = [];
// a control has a key and a value.
// example: "validators": { "required": true, "minLength": 10 }
// this snippet is reusable: can be optimized if used in many forms
for (const [key, value] of Object.entries(control.validators)) {
switch (key) {
case 'min':
controlValidators.push(Validators.min(value));
break;
case 'max':
controlValidators.push(Validators.max(value));
break;
case 'required':
if (value) {
controlValidators.push(Validators.required);
}
break;
case 'requiredTrue':
if (value) {
controlValidators.push(Validators.requiredTrue);
}
break;
case 'email':
if (value) {
controlValidators.push(Validators.email);
}
break;
case 'minLength':
controlValidators.push(Validators.minLength(value));
break;
case 'maxLength':
controlValidators.push(Validators.maxLength(value));
break;
case 'pattern':
controlValidators.push(Validators.pattern(value));
break;
case 'nullValidator':
if (value) {
controlValidators.push(Validators.nullValidator);
}
break;
default:
break;
}
}
// we must handle repeated inputs
const formControl = this.fb.control(control.value, controlValidators);
if (control.repeat) {
this.form.addControl(control.name, this.fb.array([formControl]));
} else {
this.form.addControl(control.name, formControl);
}
}
}
为此 json 我确实进行了 API 调用以检索 JSON。我循环遍历 JSON 对象以显示表单元素。
<!-- creating the form and loop -->
<span *ngIf="form != null">
<h3>{{ jsonResponse.localName }}</h3>
<form [formGroup]="form" (ngSubmit)="onSubmit()">
<div *ngFor="let control of jsonResponse.controls">
<div class="mb-3">
<span *ngIf="control.label != '' && control.type !== 'toggle' && control.type !== 'checkbox'">
<label class="form-label">{{ control.label }}</label>
</span>
<!-- for inputs that are not repeatable -->
<span *ngIf="inputTypes.includes(control.type) && control.repeat === false">
<input
[type]="control.type"
formControlName="{{control.name}}"
[value]="control.value"
class="form-control"
/>
</span>
<!-- for inputs that are repeatable -->
<span *ngIf="inputTypes.includes(control.type) && control.repeat === true">
<div formArrayName="{{ control.name }}">
<div
*ngFor="let item of getControls(control.name); let id = index"
class="input-group mb-3">
<input class="form-control" formControlName="{{ id }}"/>
<button
type="button"
class="btn btn-outline-secondary"
(click)="deleteInputItem(control.name, id)">
Remove
</button>
</div>
<button
type="button"
class="btn btn-primary"
(click)="addInputItem(control.name)">
Add entry
</button>
</div>
</span>
<!-- text area -->
<span *ngIf="control.type === 'textarea'">
<textarea
[formControlName]="control.name"
[value]="control.value"
class="form-control"
></textarea>
</span>
<!-- select -->
<span *ngIf="control.type === 'select' && control.multi == false">
<select
[formControlName]="control.name"
class="form-select form-select-lg mb-3">
<option selected>{{ control.selected }}</option>
<option
*ngFor="let option of control.selectOptions"
value="{{ option.key }}"> {{ option.value }}
</option>
</select>
</span>
<!--select multi -->
<span *ngIf="control.type === 'select' && control.multi == true">
<!-- <p>{{ control | json }}</p>-->
<!-- {{ control.selectOptions | json }}-->
<p-multiSelect
[formControlName]="control.name"
[options]="control.selectOptions"
[(ngModel)]="selection"
optionLabel="value"></p-multiSelect>
</span>
<!-- cascade select -->
<span *ngIf="control.type === 'cascadeSelect'">
<!-- the optionGroupChildren depends on object passed in control.cascadeSelectOptions -->
<p-cascadeSelect [options]="control.cascadeSelectOptions"
formControlName = "{{ control.name }}"
optionLabel="cname"
optionGroupLabel="name"
[optionGroupChildren]="['lvl1elt', 'lvl2elt']"
[style]="{'minWidth': '14rem', 'maxWidth': '100%'}"
placeholder="Select a City"
>
<ng-template pTemplate="option" let-option>
<div class="country-item">
<img *ngIf="option.states"/>
<i class="pi pi-compass p-mr-2" *ngIf="option.lvl2elt"></i>
<i class="pi pi-map-marker p-mr-2" *ngIf="option.cname"></i>
<span>{{option.cname || option.name}}</span>
</div>
</ng-template>
</p-cascadeSelect>
</span>
<!-- chips -->
<span *ngIf="control.type === 'chips'">
<p-chips
[(ngModel)]="control.values"
formControlName = "{{ control.name }}"
>
</p-chips>
</span>
<!-- range -->
<span *ngIf="control.type === 'range'">
<p-slider
formControlName = "{{ control.name }}">
</p-slider>
</span>
<span *ngIf="control.type === 'rangeSlide'">
<p-slider
formControlName = "{{ control.name }}"
[min] = "getValue(control.options.min)"
[max] ="getValue(control.options.max)"
[step] ="getValue(control.options.step)"
[range]="true"
>
</p-slider>
</span>
<!-- handling checkboxes -->
<span *ngIf="control.type === 'checkbox'">
<p-checkbox [formControlName]="control.name"
value="{{ control.name }}"></p-checkbox>
<label class="form-check-label"> {{ control.label }}</label>
</span>
<!-- toggle -->
<span *ngIf="control.type === 'toggle'">
<p-inputSwitch
[formControlName]="control.name">
</p-inputSwitch>
</span>
<!-- rating -->
<span *ngIf="control.type === 'rating'">
<p-rating
[cancel]="false"
[formControlName]="control.name"></p-rating>
</span>
<!-- radio buttons -->
<span *ngIf="control.type === 'radio'">
<div class="form-check form-check-inline" *ngFor="let option of control.radioOptions">
<input class="form-check-input"
type="radio" name="{{control.name }}"
id="{{option.key}}-{{option.value}}"
[formControlName]="control.name"
value="{{option.value}}">
<label class="form-check-label" for="{{option.key}}-{{option.value}}">{{option.value}}</label>
</div>
</span>
</div>
</div>
<div>
<button
class="btn btn-primary"
type="submit">
Submit
</button>
</div>
</form>
</span>
<span *ngIf="form != null">
{{ this.form.value | json }}
</span>
我可以使用许多不同的输入(文本、文本区域...)和 primeng 功能,例如 p-multiselect;
<span *ngIf="control.type === 'select' && control.multi == true">
<p>{{ control | json }}</p>
{{ control.selectOptions | json }}
<p-multiSelect
[formControlName]="control.name"
[options]="control.selectOptions"
[(ngModel)]="selection"
optionLabel="value"></p-multiSelect>
</span>
</div>
在这个阶段,我的不同表单元素是动态显示的。我确实有每个输入的选项。我的问题是,出于测试目的,我还显示了表单中的值:
<span *ngIf="form != null">
{{ this.form.value | json }}
</span>
我确实为两个输入获得了我为另一个输入设置的值。在业余爱好中,我没有“Eclipse”值,但它是在两个输入的爱好形式值中设置的。
一不小心解决了一整天的问题。
<span *ngIf="control.type === 'select' && control.multi == true">
<p-multiSelect
[formControlName]="control.name"
[options]="control.selectOptions"
optionLabel="value">
</p-multiSelect>
</span>
我在两个多选中的选择都使用 ngModel 添加到数组中。我只是删除了这些东西:[(ngModel)]="selection"
,现在它就像一个魅力。当我将它发送到数据库时,我只需要清理我的表单。