I'm building a Korean vocabulary trainer and I want to compare user input as people type.
In Korean and some other Asian languages, you compose letters with multiple keyup events. In Chrome, $scope.$watch, ng-keyup and ng-change only get triggered after the letter has been fully composed and either a new letter or a space has been entered. I don't mind AngularJS not triggering anything until the last letter has been fully composed but once the letter has been completed, it should trigger without having to add a whitespace or starting the next word.
HTML:
<form name="forms.vocabularyForm">
<input name="answer" id="answer" ng-model="vocabularyCtrl.answer" ng-change="vocabularyCtrl.checkChange()" ng-keyup="vocabularyCtrl.checkKeyUp($event)" type="text" />
</form>
Controller:
.controller('VocabularyCtrl', [
'$scope',
'$location',
function($scope, $location) {
this.checkChange = function () {
console.log("answer change: " + this.answer);
};
this.checkKeyUp = function ($event) {
console.log("answer keyUp: " + this.answer);
};
$scope.$watch('vocabularyCtrl.answer', function (answerNew, answerOld) {
console.log('answerOld: ' + answerOld + ', answerNew: ' + answerNew);
}, true);
};
]);
Example:
Input: ㄱ
Console:
answerOld: , answerNew:
answer keyUp:
Input: 가
Console:
answerOld: , answerNew:
answer keyUp:
Input: 감 (character is now fully composed)
Console:
answerOld: , answerNew:
answer keyUp:
Input: 감ㅅ (starting the next character, same behaviour with space bar)
Console:
answerOld: 감, answerNew:
answer change: 감
answer keyUp: 감
As explained by a helpful member of the Angular team, all triggers are intentionally suppressed while composing characters. More details here.
As suggested, I created a custom directive that manually updates the model while composing characters:
Directive:
(function() {
'use strict';
angular.module('myApp', [])
// Angular's ng-change, ng-keyup and $scope.$watch don't get triggered
// while composing (e.g. 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 make it work in FireFox (Chrome & Safari work fine with the code above).
Directive:
(function() {
'use strict';
angular.module('myApp', [])
// Angular's ng-change, ng-keyup and $scope.$watch don't get triggered
// while composing (e.g. 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 passed in argument needs to be listed 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>
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With