-
-
Save JoeMeeks/e7932fa291ba1c7e1e66597e5b52c633 to your computer and use it in GitHub Desktop.
<ion-input type="tel" pattern="\d*" placeholder="(xxx) xxx-xxxx" mask="(***) ***-****" [(ngModel)]="phone" name="phone"></ion-input> | |
<ion-input type="tel" pattern="\d*" placeholder="xxx-xx-xxxx" mask="***-**-****" [(ngModel)]="ssn" name="ssn"></ion-input> |
import { Directive, Attribute } from '@angular/core'; | |
@Directive({ | |
selector: '[mask]', | |
host: { | |
'(keyup)': 'onInputChange($event)' | |
} | |
}) | |
export class InputMask { | |
pattern: string; | |
constructor( | |
@Attribute('mask') pattern: string | |
) { | |
this.pattern = pattern; | |
} | |
onInputChange(e) { | |
try { | |
let value = e.target.value, | |
caret = e.target.selectionStart, | |
pattern = this.pattern, | |
reserve = pattern.replace(/\*/, 'g'), | |
applied = '', | |
ordinal = 0; | |
if (e.keyCode === 8 || e.key === 'Backspace' || e.keyCode === 46 || e.key === 'Delete') { | |
if (value.length) { | |
//remove all trailing formatting | |
while (value.length && pattern[value.length] && pattern[value.length] !== '*') { | |
value = value.substring(0, value.length - 1); | |
} | |
//remove all leading formatting to restore placeholder | |
if (pattern.substring(0, value.length).indexOf('*') < 0) { | |
value = value.substring(0, value.length - 1); | |
} | |
} | |
} | |
//apply mask characters | |
for (var i = 0; i < value.length; i++) { | |
//enforce pattern limit | |
if (i < pattern.length) { | |
//match mask | |
if (value[i] === pattern[ordinal]) { | |
applied += value[i]; | |
ordinal++; | |
} else if (reserve.indexOf(value[i]) > -1) { | |
//skip other reserved characters | |
} else { | |
//apply leading formatting | |
while (ordinal < pattern.length && pattern[ordinal] !== '*') { | |
applied += pattern[ordinal]; | |
ordinal++; | |
} | |
applied += value[i]; | |
ordinal++; | |
//apply trailing formatting | |
while (ordinal < pattern.length && pattern[ordinal] !== '*') { | |
applied += pattern[ordinal]; | |
ordinal++; | |
} | |
} | |
} | |
} | |
e.target.value = applied; | |
if (caret < value.length) { | |
e.target.setSelectionRange(caret, caret); | |
} | |
} catch (ex) { | |
console.error(ex.message); | |
} | |
} | |
} |
Great job it's working for me
thanks
and yes @coicoronado thanks to you also
@coicoronado, @luckylooke, @niravjadatiya This has been updated to fix a critical Delete key input bug. Also the code is now far more efficient, performant, and has the added behavior to retain caret position.
@JoeMeeks this directive works great, thanks for the code!
It doesn't work with input type="number".
It doesn't work with input type='number'
Thank you a lot for this!
Quick question to @JoeMeeks : I have a field which can hold 2 types of values, depending on a radio button clicked by the user. How can I change the mask based on the type chosen by the user, realtime?
@JoeMeeks this is really cool! Since you can safely assume this directive is only tied to an input component, you can do something like this to ensure that the mask gets applied on first render, as well:
https://gist.github.com/codinronan/d6ddfc4ab8dba2277386dbed160b050e
@BlacksmithVRS my GitHub notification settings were not letting me know about mentions so I apologize for the late reply. Have you tried:
<ion-input type="tel" pattern="\d*" placeholder="(xxx) xxx-xxxx" [mask]="publicMaskVar" [(ngModel)]="phone" name="phone"></ion-input>
with publicMaskVar being assigned with the (ionChange) handler of your radio input?
@codinconan nice ngAfterViewInit implementation!
thanks man! you saved my day!
@JoeMeeks nice work, how to use this directive for thousand separator?
Even when I try without formBuilder it is buggy, sometimes it mask the value and sometimes it does not.. :/
ionic info: