Appearently Browsers still have troubles (although Chrome and Firefox Nightly do fine). I have a hacky approach for the people that really have to use a number field (maybe because of the little arrows that text fields dont have).
I added a keyup
event Handler to the form input and tracked the state and set the value once it comes valid again.
var currentString = '';_x000D_
var badState = false;_x000D_
var lastCharacter = null;_x000D_
_x000D_
document.getElementById("field").addEventListener("keyup",($event) => {_x000D_
if ($event.target.value && !$event.target.validity.badInput) {_x000D_
currentString = $event.target.value;_x000D_
} else if ($event.target.validity.badInput) {_x000D_
if (badState && lastCharacter && lastCharacter.match(/[\.,]/) && !isNaN(+$event.key)) {_x000D_
$event.target.value = ((+currentString) + (+$event.key / 10));_x000D_
badState = false;_x000D_
} else {_x000D_
badState = true;_x000D_
}_x000D_
}_x000D_
document.getElementById("number").textContent = $event.target.value;_x000D_
lastCharacter = $event.key;_x000D_
});_x000D_
_x000D_
document.getElementById("field").addEventListener("blur",($event) => {_x000D_
if (badState) {_x000D_
$event.target.value = (+currentString);_x000D_
badState = false;_x000D_
document.getElementById("number").textContent = $event.target.value;_x000D_
}_x000D_
});
_x000D_
#result{_x000D_
padding: 10px;_x000D_
background-color: orange;_x000D_
font-size: 25px;_x000D_
width: 400px;_x000D_
margin-top: 10px;_x000D_
display: inline-block;_x000D_
}_x000D_
_x000D_
#field{_x000D_
padding: 5px;_x000D_
border-radius: 5px;_x000D_
border: 1px solid grey;_x000D_
width: 400px;_x000D_
}
_x000D_
<input type="number" id="field" keyup="keyUpFunction" step="0.01" placeholder="Field for both comma separators">_x000D_
_x000D_
<span id="result" >_x000D_
<span >Current value: </span>_x000D_
<span id="number"></span>_x000D_
</span>
_x000D_
@Directive({_x000D_
selector: '[appFloatHelper]'_x000D_
})_x000D_
export class FloatHelperDirective {_x000D_
private currentString = '';_x000D_
private badState = false;_x000D_
private lastCharacter: string = null;_x000D_
_x000D_
@HostListener("blur", ['$event']) onBlur(event) {_x000D_
if (this.badState) {_x000D_
this.model.update.emit((+this.currentString));_x000D_
this.badState = false;_x000D_
}_x000D_
}_x000D_
_x000D_
constructor(private renderer: Renderer2, private el: ElementRef, private change: ChangeDetectorRef, private zone: NgZone, private model: NgModel) {_x000D_
this.renderer.listen(this.el.nativeElement, 'keyup', (e: KeyboardEvent) => {_x000D_
if (el.nativeElement.value && !el.nativeElement.validity.badInput) {_x000D_
this.currentString = el.nativeElement.value;_x000D_
} else if (el.nativeElement.validity.badInput) {_x000D_
if (this.badState && this.lastCharacter && this.lastCharacter.match(/[\.,]/) && !isNaN(+e.key)) {_x000D_
model.update.emit((+this.currentString) + (+e.key / 10));_x000D_
el.nativeElement.value = (+this.currentString) + (+e.key / 10);_x000D_
this.badState = false;_x000D_
} else {_x000D_
this.badState = true;_x000D_
}_x000D_
} _x000D_
this.lastCharacter = e.key;_x000D_
});_x000D_
}_x000D_
_x000D_
}
_x000D_
<input type="number" matInput placeholder="Field for both comma separators" appFloatHelper [(ngModel)]="numberModel">
_x000D_