Skip to content
This repository has been archived by the owner on May 5, 2018. It is now read-only.

Commit

Permalink
fix(mask): NgModelController.$viewValue always properly set
Browse files Browse the repository at this point in the history
Uninitializing mask now restores the model value to the input.
IinitializeElement() now uses $modelValue instead of $viewValue for
more accurate/consistent behavior. Fix bug with value parser was
setting for $viewValue.

In short, $viewValue will never be set to an empty mask. And
$modelValue is now King Hippo.
  • Loading branch information
shaungrady committed Mar 15, 2013
1 parent d3856e9 commit ed41c89
Show file tree
Hide file tree
Showing 2 changed files with 72 additions and 66 deletions.
23 changes: 9 additions & 14 deletions modules/directives/mask/demo/index.html
Expand Up @@ -8,10 +8,8 @@
<link rel="stylesheet" href="http://twitter.github.com/bootstrap/assets/css/bootstrap.css">
</head>
<body ng-app="ui.directives">

<div class="container-fluid">

<form class="form-horizontal">
<form name="demo" class="form-horizontal">
<div class="control-group">

<div class="controls">
Expand All @@ -20,9 +18,14 @@

<label class="control-label">Masked Input:</label>
<div class="controls">
<input ui-mask="{{y}}" ng-model="x" placeholder="Write a mask or click a button" class="input-large">
<span class="help-inline" ng-show="x.length">Model value: <code>{{ x }}</code></span>
<span class="help-inline" ng-hide="x.length">Model value: <code>undefined</code></span>
<input name="masked" ui-mask="{{y}}" ng-model="x" placeholder="Write a mask or click a button" class="input-large" style="vertical-align: top;">
<span class="help-inline">

<div ng-show="x.length">Model value: <code>{{ x }}</code></div>
<div ng-hide="x.length">Model value: <code>undefined</code></div>
<div ng-show="demo.masked.$viewValue.length">NgModelController.$viewValue: <code>{{ demo.masked.$viewValue }}</code></div>
<div ng-hide="demo.masked.$viewValue.length">NgModelController.$viewValue: <code>undefined</code></div>
</span>
</div>
</div>
<div class="control-group">
Expand All @@ -48,15 +51,7 @@
<p><button class="btn" ng-click="y = '(**: AAA-999)'">Set to (**: AAA-999)</button></p>
</div>
</div>


</form>

</div>


<p><br></p>
<p><br></p>

</body>
</html>
115 changes: 63 additions & 52 deletions modules/directives/mask/mask.js
Expand Up @@ -50,57 +50,23 @@ angular.module('ui.directives').directive('uiMask', [
else
iElement.removeAttr('maxlength');

iElement.val(controller.$modelValue);
controller.$viewValue = controller.$modelValue;
return false;
}

function processRawMask(mask) {
var characterCount = 0;
maskCaretMap = [];
maskPatterns = [];
maskPlaceholder = '';

// If mask is an array, it's a complex mask!
if (mask instanceof Array) {
angular.forEach(mask, function(item, i) {
if (item instanceof RegExp) {
maskCaretMap.push(characterCount++);
maskPlaceholder += '_';
maskPatterns.push(item);
}
else if (typeof item == 'string') {
angular.forEach(item.split(''), function(chr, i) {
maskPlaceholder += chr;
characterCount++;
});
}
});
}
// Otherwise it's a simple mask
else if (typeof mask === 'string') {
angular.forEach(mask.split(''), function(chr, i) {
if (maskDefinitions[chr]) {
maskCaretMap.push(characterCount);
maskPlaceholder += '_';
maskPatterns.push(maskDefinitions[chr]);
}
else
maskPlaceholder += chr;
characterCount++;
});
}
// Caret position immediately following last position is valid.
maskCaretMap.push(maskCaretMap.slice().pop() + 1);
maskProcessed = maskCaretMap.length > 1 ? true : false;
}

function initializeElement() {
value = oldValueUnmasked = unmaskValue(controller.$viewValue || '');
valueMasked = oldValue = maskValue(value);
isValid = validateValue(value);
value = oldValueUnmasked = unmaskValue(controller.$modelValue || '');
valueMasked = oldValue = maskValue(value);
isValid = validateValue(value);
viewValue = isValid && value.length ? valueMasked : '';
if (iAttrs.maxlength) // Double maxlength to allow pasting new val at end of mask
iElement.attr('maxlength', maskCaretMap[maskCaretMap.length-1]*2);
iElement.attr('placeholder', maskPlaceholder);
iElement.val(isValid && value.length ? valueMasked : '');
iElement.val(viewValue);
controller.$viewValue = viewValue;
// Not using $setViewValue so we don't clobber the model value and dirty the form
// without any kind of user interaction.
}

function bindEventListeners() {
Expand All @@ -123,21 +89,26 @@ angular.module('ui.directives').directive('uiMask', [
eventsBound = false;
}

function formatter(modelValue) {
function formatter(fromModelValue) {
if (!maskProcessed)
return modelValue;
value = unmaskValue(modelValue || '');
return fromModelValue;
value = unmaskValue(fromModelValue || '');
isValid = validateValue(value);
controller.$setValidity('mask', isValid);
return isValid ? maskValue(value) : undefined;
}

function parser(viewValue) {
function parser(fromViewValue) {
if (!maskProcessed)
return viewValue;
value = unmaskValue(viewValue || '');
isValid = validateValue(value);
controller.$viewValue = maskValue(value);
return fromViewValue;
value = unmaskValue(fromViewValue || '');
isValid = validateValue(value);
viewValue = value.length ? maskValue(value) : '';
// We have to set viewValue manually as the reformatting of the input
// value performed by eventHandler() doesn't happen until after
// this parser is called, which causes what the user sees in the input
// to be out-of-sync with what the controller's $viewValue is set to.
controller.$viewValue = viewValue;
controller.$setValidity('mask', isValid);
if (value === '' && controller.$error.required !== undefined)
controller.$setValidity('required', false);
Expand Down Expand Up @@ -175,6 +146,46 @@ angular.module('ui.directives').directive('uiMask', [
return valueMasked;
}

function processRawMask(mask) {
var characterCount = 0;
maskCaretMap = [];
maskPatterns = [];
maskPlaceholder = '';

// If mask is an array, it's a complex mask!
if (mask instanceof Array) {
angular.forEach(mask, function(item, i) {
if (item instanceof RegExp) {
maskCaretMap.push(characterCount++);
maskPlaceholder += '_';
maskPatterns.push(item);
}
else if (typeof item == 'string') {
angular.forEach(item.split(''), function(chr, i) {
maskPlaceholder += chr;
characterCount++;
});
}
});
}
// Otherwise it's a simple mask
else if (typeof mask === 'string') {
angular.forEach(mask.split(''), function(chr, i) {
if (maskDefinitions[chr]) {
maskCaretMap.push(characterCount);
maskPlaceholder += '_';
maskPatterns.push(maskDefinitions[chr]);
}
else
maskPlaceholder += chr;
characterCount++;
});
}
// Caret position immediately following last position is valid.
maskCaretMap.push(maskCaretMap.slice().pop() + 1);
maskProcessed = maskCaretMap.length > 1 ? true : false;
}

function blurHandler(e) {
oldCaretPosition = 0;
oldSelectionLength = 0;
Expand Down

0 comments on commit ed41c89

Please sign in to comment.