将 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

在查看了一堆关于 ValueAccessorHostListener 解决方案的答案后,我制定了一个可行的解决方案(使用 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,但如果您想要完整,可以添加。如果需要,您还可以添加配置选项以启用该功能。