Skip to content

Instantly share code, notes, and snippets.

@JoeMeeks
Last active October 25, 2020 13:44
Show Gist options
  • Save JoeMeeks/e7932fa291ba1c7e1e66597e5b52c633 to your computer and use it in GitHub Desktop.
Save JoeMeeks/e7932fa291ba1c7e1e66597e5b52c633 to your computer and use it in GitHub Desktop.
Custom Ionic 2 & 3 Input Mask Directive
<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);
}
}
}
@parastaneja
Copy link

It doesn't work with input type='number'

@BlacksmithVRS
Copy link

Thank you a lot for this!

@BlacksmithVRS
Copy link

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?

@codinronan
Copy link

@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

@JoeMeeks
Copy link
Author

@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?

@JoeMeeks
Copy link
Author

@codinconan nice ngAfterViewInit implementation!

@mapplics
Copy link

thanks man! you saved my day!

@jscarl
Copy link

jscarl commented Sep 3, 2018

@JoeMeeks nice work, how to use this directive for thousand separator?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment