diff --git a/src/ng/directive/input.js b/src/ng/directive/input.js index 228f5fb2366a..03e94674b816 100644 --- a/src/ng/directive/input.js +++ b/src/ng/directive/input.js @@ -1248,6 +1248,16 @@ function baseInputType(scope, element, attr, ctrl, $sniffer, $browser) { composing = true; }); + // Support: IE9+ + element.on('compositionupdate', function(ev) { + // End composition when ev.data is empty string on 'compositionupdate' event. + // When the input de-focusses (e.g. by clicking away), IE triggers 'compositionupdate' + // instead of 'compositionend'. + if (isUndefined(ev.data) || ev.data === '') { + composing = false; + } + }); + element.on('compositionend', function() { composing = false; listener(); diff --git a/src/ngMock/browserTrigger.js b/src/ngMock/browserTrigger.js index 196772d1e3e9..eb80e15964dc 100644 --- a/src/ngMock/browserTrigger.js +++ b/src/ngMock/browserTrigger.js @@ -132,6 +132,24 @@ evnt.keyCode = eventData.keyCode; evnt.charCode = eventData.charCode; evnt.which = eventData.which; + } else if (/composition/.test(eventType)) { + try { + evnt = new window.CompositionEvent(eventType, { + data: eventData.data + }); + } catch (e) { + // Support: IE9+ + evnt = window.document.createEvent('CompositionEvent', {}); + evnt.initCompositionEvent( + eventType, + eventData.bubbles, + eventData.cancelable, + window, + eventData.data, + null + ); + } + } else { evnt = window.document.createEvent('MouseEvents'); x = x || 0; diff --git a/test/ng/directive/inputSpec.js b/test/ng/directive/inputSpec.js index 9c58807345d3..57b91d989233 100644 --- a/test/ng/directive/inputSpec.js +++ b/test/ng/directive/inputSpec.js @@ -134,6 +134,20 @@ describe('input', function() { browserTrigger(inputElm, 'compositionend'); expect($rootScope.name).toEqual('caitp'); }); + + + it('should end composition on "compositionupdate" when event.data is ""', function() { + // This tests a bug workaround for IE9-11 + // During composition, when an input is de-focussed by clicking away from it, + // the compositionupdate event is called with '', followed by a change event. + var inputElm = helper.compileInput(''); + browserTrigger(inputElm, 'compositionstart'); + helper.changeInputValueTo('caitp'); + expect($rootScope.name).toBeUndefined(); + browserTrigger(inputElm, 'compositionupdate', {data: ''}); + browserTrigger(inputElm, 'change'); + expect($rootScope.name).toEqual('caitp'); + }); });