Angular 2 Установите null как пустое строковое значение для поля ввода
у меня есть поле ввода, сопоставленное с сущностью в моем контроллере с ngModel
2-способ привязки.
<input type="text" [(ngModel)]="entity.one_attribute" />
когда я инициализирую свой контроллер, у меня есть этот объект:
{ one_attribute: null }
если пользователь начинает заполнять поле, но не отправляет форму сразу и опустошает поле, моя сущность обновляется, чтобы стать:
{ one_attribute: "" }
можно ли определить, что пустая строка должна быть автоматически изменена на null?
3 ответов
после просмотра кучу ответов о 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/>
Я только что сформировал это решение после многих исследований. Это своего рода hacky так как команда Angular не рекомендует расширять 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.
Я сделал это глобально. Но это не 100%. Я не смог найти метод, где angular 4 вызывает JSON.stringify на теле. Надеюсь, кто-нибудь сможет помочь. Пока новый HttpClient в 4.3 не выйдет, я продолжаю использовать класс-оболочку для службы Http. Я делаю это, потому что в Angular2 и forward не было перехватчиков. Моя обертка выглядит примерно так.
@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);
}
пока все хорошо. Но я не нашел хорошего transformRequest как в AngularJS, так что пока я найти его я реализовал transformEmptyStringAsNull следующим образом:
private removeEmptyStringsInBody(body:any) {
let bodyTemp= JSON.stringify(body, function (key, value) {
return value === "" ? null : value
});
return JSON.parse(bodyTemp);
}
Я знаю, что это уродливо, так как я сделаю дополнительную stringify обратно для разбора снова. Но мне не нужно ничего делать в остальной части приложения.