diff options
| author | Vojta Jina | 2012-03-30 12:07:19 -0700 | 
|---|---|---|
| committer | Vojta Jina | 2012-04-03 10:10:44 -0700 | 
| commit | 06d0955074f79de553cc34fbf945045dc458e064 (patch) | |
| tree | 131c952906c145bcd9c6e39721d42e2bce82cee5 /src | |
| parent | a22e0699bef61a7083b0b628fb6043531c0ca1c0 (diff) | |
| download | angular.js-06d0955074f79de553cc34fbf945045dc458e064.tar.bz2 | |
feat(ngModel): update model on each key stroke (revert ngModelInstant)
It turns out that listening only on "blur" event is not sufficient in many scenarios,
especially when you use form validation you always had to use ngModelnstant
e.g. if you want to disable a button based on valid/invalid form.
The feedback we got from our apps as well as external apps is that the
ngModelInstant should be the default.
In the future we might provide alternative ways of suppressing updates
on each key stroke, but it's not going to be the default behavior.
Apps already using the ngModelInstant can safely remove it from their
templates. Input fields without ngModelInstant directive will start propagating
the input changes into the model on each key stroke.
Diffstat (limited to 'src')
| -rw-r--r-- | src/AngularPublic.js | 1 | ||||
| -rw-r--r-- | src/ng/directive/input.js | 121 | ||||
| -rw-r--r-- | src/ngScenario/Scenario.js | 2 | ||||
| -rw-r--r-- | src/ngScenario/dsl.js | 3 | 
4 files changed, 48 insertions, 79 deletions
| diff --git a/src/AngularPublic.js b/src/AngularPublic.js index 8597de9c..1326bf8c 100644 --- a/src/AngularPublic.js +++ b/src/AngularPublic.js @@ -98,7 +98,6 @@ function publishExternalAPI(angular){              ngModel: ngModelDirective,              ngList: ngListDirective,              ngChange: ngChangeDirective, -            ngModelInstant: ngModelInstantDirective,              required: requiredDirective,              ngRequired: requiredDirective,              ngValue: ngValueDirective diff --git a/src/ng/directive/input.js b/src/ng/directive/input.js index 3d53f7ca..aab12e34 100644 --- a/src/ng/directive/input.js +++ b/src/ng/directive/input.js @@ -367,12 +367,43 @@ function isEmpty(value) {  } -function textInputType(scope, element, attr, ctrl) { -  element.bind('blur', function() { -    scope.$apply(function() { -      ctrl.$setViewValue(trim(element.val())); +function textInputType(scope, element, attr, ctrl, $sniffer, $browser) { + +  var listener = function() { +    var value = trim(element.val()); + +    if (ctrl.$viewValue !== value) { +      scope.$apply(function() { +        ctrl.$setViewValue(value); +      }); +    } +  }; + +  // if the browser does support "input" event, we are fine +  if ($sniffer.hasEvent('input')) { +    element.bind('input', listener); +  } else { +    var timeout; + +    element.bind('keydown', function(event) { +      var key = event.keyCode; + +      // ignore +      //    command            modifiers                   arrows +      if (key === 91 || (15 < key && key < 19) || (37 <= key && key <= 40)) return; + +      if (!timeout) { +        timeout = $browser.defer(function() { +          listener(); +          timeout = null; +        }); +      }      }); -  }); + +    // if user paste into input using mouse, we need "change" event to catch it +    element.bind('change', listener); +  } +    ctrl.$render = function() {      element.val(isEmpty(ctrl.$viewValue) ? '' : ctrl.$viewValue); @@ -448,8 +479,8 @@ function textInputType(scope, element, attr, ctrl) {    }  }; -function numberInputType(scope, element, attr, ctrl) { -  textInputType(scope, element, attr, ctrl); +function numberInputType(scope, element, attr, ctrl, $sniffer, $browser) { +  textInputType(scope, element, attr, ctrl, $sniffer, $browser);    ctrl.$parsers.push(function(value) {      var empty = isEmpty(value); @@ -510,8 +541,8 @@ function numberInputType(scope, element, attr, ctrl) {    });  } -function urlInputType(scope, element, attr, ctrl) { -  textInputType(scope, element, attr, ctrl); +function urlInputType(scope, element, attr, ctrl, $sniffer, $browser) { +  textInputType(scope, element, attr, ctrl, $sniffer, $browser);    var urlValidator = function(value) {      if (isEmpty(value) || URL_REGEXP.test(value)) { @@ -527,8 +558,8 @@ function urlInputType(scope, element, attr, ctrl) {    ctrl.$parsers.push(urlValidator);  } -function emailInputType(scope, element, attr, ctrl) { -  textInputType(scope, element, attr, ctrl); +function emailInputType(scope, element, attr, ctrl, $sniffer, $browser) { +  textInputType(scope, element, attr, ctrl, $sniffer, $browser);    var emailValidator = function(value) {      if (isEmpty(value) || EMAIL_REGEXP.test(value)) { @@ -709,13 +740,14 @@ function checkboxInputType(scope, element, attr, ctrl) {        </doc:scenario>      </doc:example>   */ -var inputDirective = [function() { +var inputDirective = ['$browser', '$sniffer', function($browser, $sniffer) {    return {      restrict: 'E',      require: '?ngModel',      link: function(scope, element, attr, ctrl) {        if (ctrl) { -        (inputType[lowercase(attr.type)] || inputType.text)(scope, element, attr, ctrl); +        (inputType[lowercase(attr.type)] || inputType.text)(scope, element, attr, ctrl, $sniffer, +                                                            $browser);        }      }    }; @@ -1004,69 +1036,6 @@ var ngChangeDirective = valueFn({  }); -/** - * @ngdoc directive - * @name angular.module.ng.$compileProvider.directive.ng-model-instant - * - * @element input - * - * @description - * By default, Angular udpates the model only on `blur` event - when the input looses focus. - * If you want to update after every key stroke, use `ng-model-instant`. - * - * @example - * <doc:example> - *   <doc:source> - *     First name: <input type="text" ng-model="firstName" /><br /> - *     Last name: <input type="text" ng-model="lastName" ng-model-instant /><br /> - * - *     First name ({{firstName}}) is only updated on `blur` event, but the last name ({{lastName}}) - *     is updated immediately, because of using `ng-model-instant`. - *   </doc:source> - *   <doc:scenario> - *     it('should update first name on blur', function() { - *       input('firstName').enter('santa', 'blur'); - *       expect(binding('firstName')).toEqual('santa'); - *     }); - * - *     it('should update last name immediately', function() { - *       input('lastName').enter('santa', 'keydown'); - *       expect(binding('lastName')).toEqual('santa'); - *     }); - *   </doc:scenario> - * </doc:example> - */ -var ngModelInstantDirective = ['$browser', function($browser) { -  return { -    require: 'ngModel', -    link: function(scope, element, attr, ctrl) { -      var handler = function() { -        scope.$apply(function() { -          ctrl.$setViewValue(trim(element.val())); -        }); -      }; - -      var timeout; -      element.bind('keydown', function(event) { -        var key = event.keyCode; - -        //    command            modifiers                   arrows -        if (key === 91 || (15 < key && key < 19) || (37 <= key && key <= 40)) return; - -        if (!timeout) { -          timeout = $browser.defer(function() { -            handler(); -            timeout = null; -          }); -        } -      }); - -      element.bind('change input', handler); -    } -  }; -}]; - -  var requiredDirective = [function() {    return {      require: '?ngModel', diff --git a/src/ngScenario/Scenario.js b/src/ngScenario/Scenario.js index cd3c335f..7335d5de 100644 --- a/src/ngScenario/Scenario.js +++ b/src/ngScenario/Scenario.js @@ -327,7 +327,7 @@ function browserTrigger(element, type, keys) {  (function(fn){    var parentTrigger = fn.trigger;    fn.trigger = function(type) { -    if (/(click|change|keydown|blur)/.test(type)) { +    if (/(click|change|keydown|blur|input)/.test(type)) {        var processDefaults = [];        this.each(function(index, node) {          processDefaults.push(browserTrigger(node, type)); diff --git a/src/ngScenario/dsl.js b/src/ngScenario/dsl.js index 8a1bccb1..7f399394 100644 --- a/src/ngScenario/dsl.js +++ b/src/ngScenario/dsl.js @@ -198,12 +198,13 @@ angular.scenario.dsl('binding', function() {   */  angular.scenario.dsl('input', function() {    var chain = {}; +  var supportInputEvent = 'oninput' in document.createElement('div');    chain.enter = function(value, event) {      return this.addFutureAction("input '" + this.name + "' enter '" + value + "'", function($window, $document, done) {        var input = $document.elements('[ng\\:model="$1"]', this.name).filter(':input');        input.val(value); -      input.trigger(event || 'blur'); +      input.trigger(event || supportInputEvent && 'input' || 'change');        done();      });    }; | 
