将 null 设置为输入字段的空字符串值
Set null as empty string value for input field
我有一个 input
字段映射到我的控制器中的一个实体,具有 ngModel
双向绑定:
<input type="text" [(ngModel)]="entity.one_attribute" />
当我初始化我的控制器时,我有这个实体:
{ one_attribute: null }
如果用户开始填写字段但没有立即提交表单并清空该字段,我的实体将更新为:
{ one_attribute: "" }
是否可以定义自动将空字符串更改为null
?
在查看了一堆关于 ValueAccessor
和 HostListener
解决方案的答案后,我制定了一个可行的解决方案(使用 RC1 测试):
import {NgControl} from "@angular/common";
import {Directive, ElementRef, HostListener} from "@angular/core";
@Directive({
selector: 'input[nullValue]'
})
export class NullDefaultValueDirective {
constructor(private el: ElementRef, private control: NgControl) {}
@HostListener('input', ['$event.target'])
onEvent(target: HTMLInputElement){
this.control.viewToModelUpdate((target.value === '') ? null : target.value);
}
}
然后在您的输入字段中使用它:
<input [(ngModel)]="bindedValue" nullValue/>
我已经在全球范围内完成了。但它不是 100%。我找不到 angular 4 在 body 上调用 JSON.stringify 的方法。我希望有人可以在这里提供帮助。
在 4.3 中新的 HttpClient 出来之前,我继续使用包装器 class 来处理 Http 服务。我这样做是因为 Angular2 及以后的版本中没有拦截器。我的包装纸看起来像这样。
@Injectable()
export class MyDao {
constructor(private http: Http) {
}
public get(options?:MyRequestOptions){...}
public post(url:string,body,options?:MyRequestOptions){
let reqOptions = new BaseRequestOptions();
reqOptions.url = url;
reqOptions.params= this.processParams(options);
reqOptions.header= this.processHeaders(options); //ex. add global headers
reqOptions.body=this.removeEmptyStringsInBody(body);
this.http.post(options);
}
到目前为止一切顺利。但是我没有找到像 AngularJS 中那样好的 transformRequest,所以在我找到它之前,我已经实现了 transformEmptyStringAsNull,如下所示:
private removeEmptyStringsInBody(body:any) {
let bodyTemp= JSON.stringify(body, function (key, value) {
return value === "" ? null : value
});
return JSON.parse(bodyTemp);
}
我知道我会做一个额外的字符串化以再次解析的方式很丑陋。但是我不需要在应用程序的其余部分做任何事情。
经过大量研究,我刚刚形成了这个解决方案。这有点 hacky since the Angular team doesn't recommend extending DefaultValueAccessor
,但它会自动为每个输入工作,而无需单独标记每个输入。
import { Directive, forwardRef, Renderer2, ElementRef, Input } from '@angular/core';
import { DefaultValueAccessor, NG_VALUE_ACCESSOR } from '@angular/forms';
export const VALUE_ACCESSOR: any = {
provide: NG_VALUE_ACCESSOR,
useExisting: forwardRef(() => InputExtensionValueAccessor),
multi: true
};
@Directive({
selector: 'input:not([type]),input[type=text],input[type=password],input[type=email],input[type=tel],textarea',
host: {
'(input)': '_handleInput($event.target.value)',
'(blur)': 'onTouched()',
'(compositionstart)': '_compositionStart()',
'(compositionend)': '_compositionEnd($event.target.value)'
},
providers: [VALUE_ACCESSOR]
})
export class InputExtensionValueAccessor extends DefaultValueAccessor {
// HACK: Allows use of DefaultValueAccessor as base
// (https://github.com/angular/angular/issues/9146)
static decorators = null;
constructor(renderer: Renderer2, elementRef: ElementRef) {
super(renderer, elementRef, (null as any));
}
registerOnChange(fn: (_: any) => void): void {
super.registerOnChange(value => {
// Convert empty string from the view to null for the model
fn(value === '' ? null : value);
});
}
}
这对我来说在 Angular 4.4.5.
上非常有用
虽然在采纳的回答中@jobou的解决方案是有道理的,但我认为仍然需要记住在每个字段中添加'nullValue',这是一个相当大的开销。
我换了一个方向,查看了Interceptor服务中的数据。
intercept(request: HttpRequest<any>, next: HttpHandler): Observable<HttpEvent<any>> {
...
request = this.nullValueService.setNullValues();
...
}
我创建了一个服务来检查请求正文中的每个值,如果找到一个空字符串,它就会设置为 Null。
@Injectable()
export class NullValueService {
...
setNullValues(request) {
for (let key in request.body) {
if (request.body[key] === '') {
request.body[key] = null;
}
}
}
}
此解决方案适用于我的后端,我认为在大多数情况下应该没问题。
所有这些值访问器的东西让我头晕目眩。
相反,我用 setValue
覆盖扩展了 FormControl
:
export class StringFormControl extends FormControl
{
constructor(formState?: any, validatorOrOpts?: ValidatorFn | ValidatorFn[] | AbstractControlOptions | null, asyncValidator?: AsyncValidatorFn | AsyncValidatorFn[] | null)
{
super(formState, validatorOrOpts, asyncValidator)
}
setValue(value: any, options?: {
onlySelf?: boolean;
emitEvent?: boolean;
emitModelToViewChange?: boolean;
emitViewToModelChange?: boolean;
})
{
if (value === '')
{
value = null;
}
super.setValue(value, options)
}
}
签名刚刚从 FormControl
class.
复制而来
这不适用于 FormBuilder
,但如果您使用 new FormControl()
创建表单,则可以改用 new StringFormControl()
。
目前我认为也不需要添加 patchValue
,但如果您想要完整,可以添加。如果需要,您还可以添加配置选项以启用该功能。
我有一个 input
字段映射到我的控制器中的一个实体,具有 ngModel
双向绑定:
<input type="text" [(ngModel)]="entity.one_attribute" />
当我初始化我的控制器时,我有这个实体:
{ one_attribute: null }
如果用户开始填写字段但没有立即提交表单并清空该字段,我的实体将更新为:
{ one_attribute: "" }
是否可以定义自动将空字符串更改为null
?
在查看了一堆关于 ValueAccessor
和 HostListener
解决方案的答案后,我制定了一个可行的解决方案(使用 RC1 测试):
import {NgControl} from "@angular/common";
import {Directive, ElementRef, HostListener} from "@angular/core";
@Directive({
selector: 'input[nullValue]'
})
export class NullDefaultValueDirective {
constructor(private el: ElementRef, private control: NgControl) {}
@HostListener('input', ['$event.target'])
onEvent(target: HTMLInputElement){
this.control.viewToModelUpdate((target.value === '') ? null : target.value);
}
}
然后在您的输入字段中使用它:
<input [(ngModel)]="bindedValue" nullValue/>
我已经在全球范围内完成了。但它不是 100%。我找不到 angular 4 在 body 上调用 JSON.stringify 的方法。我希望有人可以在这里提供帮助。 在 4.3 中新的 HttpClient 出来之前,我继续使用包装器 class 来处理 Http 服务。我这样做是因为 Angular2 及以后的版本中没有拦截器。我的包装纸看起来像这样。
@Injectable()
export class MyDao {
constructor(private http: Http) {
}
public get(options?:MyRequestOptions){...}
public post(url:string,body,options?:MyRequestOptions){
let reqOptions = new BaseRequestOptions();
reqOptions.url = url;
reqOptions.params= this.processParams(options);
reqOptions.header= this.processHeaders(options); //ex. add global headers
reqOptions.body=this.removeEmptyStringsInBody(body);
this.http.post(options);
}
到目前为止一切顺利。但是我没有找到像 AngularJS 中那样好的 transformRequest,所以在我找到它之前,我已经实现了 transformEmptyStringAsNull,如下所示:
private removeEmptyStringsInBody(body:any) {
let bodyTemp= JSON.stringify(body, function (key, value) {
return value === "" ? null : value
});
return JSON.parse(bodyTemp);
}
我知道我会做一个额外的字符串化以再次解析的方式很丑陋。但是我不需要在应用程序的其余部分做任何事情。
经过大量研究,我刚刚形成了这个解决方案。这有点 hacky since the Angular team doesn't recommend extending DefaultValueAccessor
,但它会自动为每个输入工作,而无需单独标记每个输入。
import { Directive, forwardRef, Renderer2, ElementRef, Input } from '@angular/core';
import { DefaultValueAccessor, NG_VALUE_ACCESSOR } from '@angular/forms';
export const VALUE_ACCESSOR: any = {
provide: NG_VALUE_ACCESSOR,
useExisting: forwardRef(() => InputExtensionValueAccessor),
multi: true
};
@Directive({
selector: 'input:not([type]),input[type=text],input[type=password],input[type=email],input[type=tel],textarea',
host: {
'(input)': '_handleInput($event.target.value)',
'(blur)': 'onTouched()',
'(compositionstart)': '_compositionStart()',
'(compositionend)': '_compositionEnd($event.target.value)'
},
providers: [VALUE_ACCESSOR]
})
export class InputExtensionValueAccessor extends DefaultValueAccessor {
// HACK: Allows use of DefaultValueAccessor as base
// (https://github.com/angular/angular/issues/9146)
static decorators = null;
constructor(renderer: Renderer2, elementRef: ElementRef) {
super(renderer, elementRef, (null as any));
}
registerOnChange(fn: (_: any) => void): void {
super.registerOnChange(value => {
// Convert empty string from the view to null for the model
fn(value === '' ? null : value);
});
}
}
这对我来说在 Angular 4.4.5.
上非常有用虽然在采纳的回答中@jobou的解决方案是有道理的,但我认为仍然需要记住在每个字段中添加'nullValue',这是一个相当大的开销。
我换了一个方向,查看了Interceptor服务中的数据。
intercept(request: HttpRequest<any>, next: HttpHandler): Observable<HttpEvent<any>> {
...
request = this.nullValueService.setNullValues();
...
}
我创建了一个服务来检查请求正文中的每个值,如果找到一个空字符串,它就会设置为 Null。
@Injectable()
export class NullValueService {
...
setNullValues(request) {
for (let key in request.body) {
if (request.body[key] === '') {
request.body[key] = null;
}
}
}
}
此解决方案适用于我的后端,我认为在大多数情况下应该没问题。
所有这些值访问器的东西让我头晕目眩。
相反,我用 setValue
覆盖扩展了 FormControl
:
export class StringFormControl extends FormControl
{
constructor(formState?: any, validatorOrOpts?: ValidatorFn | ValidatorFn[] | AbstractControlOptions | null, asyncValidator?: AsyncValidatorFn | AsyncValidatorFn[] | null)
{
super(formState, validatorOrOpts, asyncValidator)
}
setValue(value: any, options?: {
onlySelf?: boolean;
emitEvent?: boolean;
emitModelToViewChange?: boolean;
emitViewToModelChange?: boolean;
})
{
if (value === '')
{
value = null;
}
super.setValue(value, options)
}
}
签名刚刚从 FormControl
class.
这不适用于 FormBuilder
,但如果您使用 new FormControl()
创建表单,则可以改用 new StringFormControl()
。
目前我认为也不需要添加 patchValue
,但如果您想要完整,可以添加。如果需要,您还可以添加配置选项以启用该功能。