Set an empty value as an empty string value for an input field

I have an input field mapped to an entity in my controller with two-way ngModel binding:

 <input type="text" [(ngModel)]="entity.one_attribute" /> 

When I initialize my controller, I have this entity:

 { one_attribute: null } 

If the user starts filling out the field, but does not submit the form immediately and clears the field, my entity is updated and becomes:

 { one_attribute: "" } 

Is it possible to determine that an empty string should be automatically replaced by null ?

+13
angular angular2-template
source share
3 answers

After looking at a bunch of answers about ValueAccessor and HostListener I made a working solution (tested with 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); } } 

Then use it this way in your input fields:

 <input [(ngModel)]="bindedValue" nullValue/> 
+17
source share

I just formed this solution after much research. This is a kind of hacker since the Angular team does not recommend extending DefaultValueAccessor , but it automatically works for each input without the need to mark each individually.

 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); }); } } 

This works fine for me on Angular 4.4.5.

+5
source share

I have done it globally. But this is not 100%. I could not find a method in which angular 4 calls JSON.stringify on the body. I hope someone can help here. Until the new HttpClient appears in version 4.3, I continue to use the wrapper class for the Http service. I do this because interceptors are not present in Angular2 and forward. My cover looks something like this.

 @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); } 

So far so good. But I did not find any good transformRequest, as in AngularJS, so until I find it, I implemented transformEmptyStringAsNull like this:

  private removeEmptyStringsInBody(body:any) { let bodyTemp= JSON.stringify(body, function (key, value) { return value === "" ? null : value }); return JSON.parse(bodyTemp); } 

I know this is ugly in the sense that I will again make an extra line back for parsing. But I do not need to do anything in the rest of the application.

-3
source share

All Articles