From 49dfdf8f0238ef8c473fcb44694f6b5696ecde70 Mon Sep 17 00:00:00 2001 From: Igor Minar Date: Sun, 6 May 2012 09:27:10 -0700 Subject: [PATCH 1/2] fix(ngModel): use keydown/change events on IE9 instead of input On IE9 the input event is not fired when backspace or delete key are pressed or when cut is performed. This makes listening on the input event unreliable and therefore it's better for us to just use keydown/change events instead. Closes #879 --- src/ng/directive/input.js | 3 ++- src/ng/sniffer.js | 5 +++++ test/ng/directive/inputSpec.js | 2 +- test/ng/snifferSpec.js | 20 ++++++++++++++------ 4 files changed, 22 insertions(+), 8 deletions(-) diff --git a/src/ng/directive/input.js b/src/ng/directive/input.js index dadf07c4c37b..6e394afd379f 100644 --- a/src/ng/directive/input.js +++ b/src/ng/directive/input.js @@ -379,7 +379,8 @@ function textInputType(scope, element, attr, ctrl, $sniffer, $browser) { } }; - // if the browser does support "input" event, we are fine + // if the browser does support "input" event, we are fine - except on IE9 which doesn't fire the + // input event on backspace, delete or cut if ($sniffer.hasEvent('input')) { element.bind('input', listener); } else { diff --git a/src/ng/sniffer.js b/src/ng/sniffer.js index 5389dc863a50..b19e7ccf4832 100644 --- a/src/ng/sniffer.js +++ b/src/ng/sniffer.js @@ -22,6 +22,11 @@ function $SnifferProvider() { // IE8 compatible mode lies (!$window.document.documentMode || $window.document.documentMode > 7), hasEvent: function(event) { + // IE9 implements 'input' event it's so fubared that we rather pretend that it doesn't have + // it. In particular the event is not fired when backspace or delete key are pressed or + // when cut operation is performed. + if (event == 'input' && msie == 9) return false; + if (isUndefined(eventSupport[event])) { var divElm = $window.document.createElement('div'); eventSupport[event] = 'on' + event in divElm; diff --git a/test/ng/directive/inputSpec.js b/test/ng/directive/inputSpec.js index 6cecf125e48f..45e3e1bf918b 100644 --- a/test/ng/directive/inputSpec.js +++ b/test/ng/directive/inputSpec.js @@ -254,7 +254,7 @@ describe('ngModel', function() { expect(element.hasClass('ng-invalid-email')).toBe(true); element.val('invalid-again'); - browserTrigger(element, $sniffer.hasEvent('input') ? 'input' : 'change'); + browserTrigger(element, ($sniffer.hasEvent('input')) ? 'input' : 'change'); expect(element).toBeInvalid(); expect(element).toBeDirty(); expect(element.hasClass('ng-valid-email')).toBe(false); diff --git a/test/ng/snifferSpec.js b/test/ng/snifferSpec.js index 4e5f50ec8fd9..81a0db5e54fe 100644 --- a/test/ng/snifferSpec.js +++ b/test/ng/snifferSpec.js @@ -45,10 +45,10 @@ describe('$sniffer', function() { }); - it('should return true if "oninput" is present in a div element', function() { - mockDivElement = {oninput: noop}; + it('should return true if "onchange" is present in a div element', function() { + mockDivElement = {onchange: noop}; - expect($sniffer.hasEvent('input')).toBe(true); + expect($sniffer.hasEvent('change')).toBe(true); }); @@ -62,11 +62,19 @@ describe('$sniffer', function() { it('should only create the element once', function() { mockDivElement = {}; - $sniffer.hasEvent('input'); - $sniffer.hasEvent('input'); - $sniffer.hasEvent('input'); + $sniffer.hasEvent('change'); + $sniffer.hasEvent('change'); + $sniffer.hasEvent('change'); expect(mockDocument.createElement).toHaveBeenCalledOnce(); }); + + + it('should claim that IE9 doesn\'t have support for "oninput"', function() { + // IE9 implementation is fubared, so it's better to pretend that it doesn't have the support + mockDivElement = {oninput: noop}; + + expect($sniffer.hasEvent('input')).toBe((msie == 9) ? false : true); + }); }); }); From b24cc63bcbd45741d21757653f05d54db09e0f20 Mon Sep 17 00:00:00 2001 From: Igor Minar Date: Sun, 6 May 2012 16:30:24 -0700 Subject: [PATCH 2/2] fix(ngSrc,ngHref): binding should set element prop as well as attr IE9 ignores setAttribute('src', val) calls on img if "ng:src" attribute is present. It only fetches the image if element property is updated as well. Closes #935 --- src/ng/directive/booleanAttrs.js | 6 ++++++ test/ng/directive/booleanAttrsSpec.js | 24 +++++++++++++++++++++++- 2 files changed, 29 insertions(+), 1 deletion(-) diff --git a/src/ng/directive/booleanAttrs.js b/src/ng/directive/booleanAttrs.js index 5da98ef425b0..1468008ccdd2 100644 --- a/src/ng/directive/booleanAttrs.js +++ b/src/ng/directive/booleanAttrs.js @@ -310,10 +310,16 @@ forEach(['src', 'href'], function(attrName) { attr.$$observers[attrName] = []; attr.$observe(normalized, function(value) { attr.$set(attrName, value); + + // on IE, if "ng:src" directive declaration is used and "src" attribute doesn't exist + // then calling element.setAttribute('src', 'foo') doesn't do anything, so we need + // to set the property as well to achieve the desired effect + if (msie) element.prop(attrName, value); }); } else { // value present means that no interpolation, so copy to native attribute. attr.$set(attrName, value); + element.prop(attrName, value); } }; } diff --git a/test/ng/directive/booleanAttrsSpec.js b/test/ng/directive/booleanAttrsSpec.js index f398441e7df3..435ffcb9727f 100644 --- a/test/ng/directive/booleanAttrsSpec.js +++ b/test/ng/directive/booleanAttrsSpec.js @@ -80,7 +80,8 @@ describe('boolean attr directives', function() { describe('ngSrc', function() { it('should interpolate the expression and bind to src', inject(function($compile, $rootScope) { - var element = $compile('
')($rootScope) + var element = $compile('
')($rootScope); + $rootScope.$digest(); expect(element.attr('src')).toEqual('some/'); @@ -91,6 +92,27 @@ describe('ngSrc', function() { dealoc(element); })); + + if (msie) { + it('should update the element property as well as the attribute', inject( + function($compile, $rootScope) { + // on IE, if "ng:src" directive declaration is used and "src" attribute doesn't exist + // then calling element.setAttribute('src', 'foo') doesn't do anything, so we need + // to set the property as well to achieve the desired effect + + var element = $compile('
')($rootScope); + + $rootScope.$digest(); + expect(element.prop('src')).toEqual('some/'); + + $rootScope.$apply(function() { + $rootScope.id = 1; + }); + expect(element.prop('src')).toEqual('some/1'); + + dealoc(element); + })); + } });