-
-
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); | |
} | |
} | |
} |
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?
It doesn't work with input type='number'