As explained by a helpful member of the Angular team, all triggers are intentionally suppressed when composing characters. More details here .
As suggested, I created a custom directive that manually updates the model when composing characters:
Directive
(function() { 'use strict'; angular.module('myApp', []) // Angular ng-change, ng-keyup and $scope.$watch don't get triggered // while composing (eg when writing Korean syllables). // See: https://github.com/angular/angular.js/issues/10588 // This custom directive uses element.on('input') instead, which gets // triggered while composing. .directive('cstInput', function() { return { restrict: 'A', require: '^ngModel', scope: { ngModel: '=', // sync model }, link: function (scope, element, attrs, ngModel) { element.on('input', function() { scope.ngModel = element.val(); }); } }; }); })();
Controller: (as suggested by ippi)
$scope.$watch('quizzesCtrl.answer', function (answer) { console.log(answer); });
HTML:
<form ng-controller="QuizzesController as quizzesCtrl"> <input cst-input name="answer" id="answer" ng-model="quizzesCtrl.answer" type="text" /> </form>
Update
I had to change the code to the following to work in FireFox (Chrome and Safari work fine with the code above).
Directive
(function() { 'use strict'; angular.module('myApp', []) // Angular ng-change, ng-keyup and $scope.$watch don't get triggered // while composing (eg when writing Korean syllables). // See: https://github.com/angular/angular.js/issues/10588 // This custom directive uses element.on('input') instead, which gets // triggered while composing. .directive('cstInput', function() { return { restrict: 'A', require: '^ngModel', link: function (scope, element, attrs, ngModel) { element.on('input', function() { scope.$apply(function(){ scope.ngModel = element.val(); scope.$eval(attrs.cstInput, {'answer': scope.ngModel}); // only works if no scope has been defined in directive }); }); } }; }); })();
Controller:
this.checkAnswer = function (answer) { if (answer === this.quiz.answer) { this.isCorrect = true; } };
HTML (note that any argument passed must be specified in cst-input-callback):
<form ng-controller="QuizzesController as quizzesCtrl"> <input cst-input="quizzesCtrl.checkAnswer(answer)" ng-model="quizzesCtrl.answer" name="answer" id="answer" type="text" /> </form>