چگونه یک اعتبارسنج فرم سفارشی ایجاد کنیم که فقط JSON معتبر را در Angular بپذیرد
در برنامه Angular من، یک فرم واکنشی دارم که برای سادهسازی فرض میکنم فقط یک کنترل به نام configJson دارد که در DOM توسط یک <textarea> نمایش داده شده است.
من نیاز دارم این کنترل فرم را طوری اعتبارسنجی کنم که تنها متن JSON معتبر از ورودی کاربر را بپذیرد و در غیر این صورت پیام خطا نمایش دهد.
در اینجا کلاس و قالب مؤلفه من آمده است:
import { Component, OnInit } from '@angular/core';
import { FormControl, FormGroup } from '@angular/forms';
@Component({
selector: 'app-configuration',
templateUrl: './configuration.component.html',
styleUrls: ['./configuration.component.scss']
})
export class ConfigurationComponent implements OnInit {
form: FormGroup;
constructor() {}
ngOnInit() {
this.form = new FormGroup({
'configJson': new FormControl(),
});
// TODO: someone add JSON validation
}
loadJsonConfiguration() {
const config = JSON.parse(this.form.get('configJson').value);
// some logic here using the parsed "config" object...
}
}
<form [formGroup]="form">
<div class="form-group">
<label for="json-config-textarea">Parse from JSON:</label>
<textarea
class="form-control"
id="json-config-textarea"
rows="10"
[formControlName]="'configJson'"
></textarea>
</div>
<div [hidden]="form.get('configJson').pristine || form.get('configJson').valid">
Please insert a valid JSON.
</div>
<div class="form-group text-right">
<button
class="btn btn-primary"
(click)="loadJsonConfiguration()"
[disabled]="form.get('configJson').pristine || form.get('configJson').invalid"
>Load JSON Configuration</button>
</div>
</form>
پاسخ شماره ۱
در ابتدا تلاش کردم جواب نویسنده اصلی را ویرایش کنم، اما توسط داوران همتا رد شد به دلیل:
این ویرایش با هدف خطاب قرار دادن نویسنده پست انجام شده و به عنوان یک ویرایش هیچ معنایی ندارد. باید به صورت یک نظر یا پاسخ نوشته میشد.
پس، این نسخه تغییر یافته من است:
import {AbstractControl, ValidationErrors, ValidatorFn} from '@angular/forms';
export function jsonValidator(control: AbstractControl): ValidationErrors | null {
try {
JSON.parse(control.value);
} catch (e) {
return { jsonInvalid: true };
}
return null;
};
import { Component, OnInit } from '@angular/core';
import { FormControl, FormGroup, Validators } from '@angular/forms';
import { jsonValidator } from './json.validator';
@Component({
selector: 'app-configuration',
templateUrl: './configuration.component.html',
styleUrls: ['./configuration.component.scss']
})
export class ConfigurationComponent implements OnInit {
form: FormGroup;
ngOnInit() {
this.form = new FormGroup({
configJson: new FormControl(Validators.compose(Validators.required, jsonValidator))
});
}
loadJsonConfiguration() {
...
}
}
پاسخ شماره ۲
یک راهحل ایجاد یک اعتبارسنج فرم سفارشی و پیوست آن به کنترل فرم است. وظیفه اعتبارسنج این است که فقط JSON معتبر را بپذیرد.
این شکل ظاهری ولیدیتور من است:
import {AbstractControl, ValidationErrors, ValidatorFn} from '@angular/forms';
export function jsonValidator(): ValidatorFn {
return (control: AbstractControl): ValidationErrors | null => {
const error: ValidationErrors = { jsonInvalid: true };
try {
JSON.parse(control.value);
} catch (e) {
control.setErrors(error);
return error;
}
control.setErrors(null);
return null;
};
}
میتوان آن را به راحتی با موارد زیر تست واحد کرد:
import { FormControl, ValidationErrors, ValidatorFn } from '@angular/forms';
import Spy = jasmine.Spy;
import { jsonValidator } from './json.validator';
describe('JSON Validator', () => {
let control: FormControl;
let spySetErrors: Spy;
let validator: ValidatorFn;
const errorName = 'jsonInvalid';
beforeEach(() => {
control = new FormControl(null);
validator = jsonValidator();
spySetErrors = spyOn(control, 'setErrors').and.callThrough();
});
for (const { testId, valid, value } of [
{ testId: 1, valid: true, value: '{}' },
{ testId: 2, valid: true, value: '{"myKey": "myValue"}' },
{ testId: 3, valid: true, value: '{"myKey1": "myValue1", "myKey2": "myValue2"}' },
// more valid cases can be added...
{ testId: 4, valid: false, value: 'this is not a valid json' },
{ testId: 5, valid: false, value: '{"theJsonFormat": "doesntLikePendingCommas",}' },
{ testId: 6, valid: false, value: '{"theJsonFormat": doesntLikeMissingQuotes }' },
// more invalid cases ca be added...
]) {
it(`should only trigger the error when the control's value is not a valid JSON [${testId}]`, () => {
const error: ValidationErrors = { [errorName]: true };
control.setValue(value);
if (valid) {
expect(validator(control)).toBeNull();
expect(control.getError(errorName)).toBeFalsy();
} else {
expect(validator(control)).toEqual(error);
expect(control.getError(errorName)).toBe(true);
}
});
}
});
در ngOnInit کامپوننت، باید اعتبارسنج جدید اضافه شود:
this.form.get('configJson').setValidators([
Validators.required, // this makes the field mandatory
jsonValidator(), // this forces the user to insert valid json
]);
پس کلاس کامپوننت اکنون به این شکل است:
import { Component, OnInit } from '@angular/core';
import { FormControl, FormGroup, Validators } from '@angular/forms';
import { jsonValidator } from './json.validator';
@Component({
selector: 'app-configuration',
templateUrl: './configuration.component.html',
styleUrls: ['./configuration.component.scss']
})
export class ConfigurationComponent implements OnInit {
form: FormGroup;
constructor() {}
ngOnInit() {
this.form = new FormGroup({
'configJson': new FormControl(),
});
this.form.get('configJson').setValidators([
Validators.required,
jsonValidator(),
]);
}
loadJsonConfiguration() {
const config = JSON.parse(this.form.get('configJson').value);
// some logic here using the parsed "config" object...
}
}
