8

I'm building a web app for a touch screen computer which needs an on-screen keyboard and am attempting to use this excellent (or the least the only one I was able to find that wasn't terrible) keyboard. https://github.com/Mottie/Keyboard/

The problem is, as you might have guessed already, that the model doesn't get updated when using the on-screen keyboard. This is my code, which sort of works but it all sorts of ugly:

The partitial HTML:

<input type="text" class="keyboard" ng-model="newUser.name">
<input type="text" class="keyboard" ng-model="newUser.email>

Initializing the keyboard, from the partitial page controller:

$('.keyboard')
.keyboard({
    stickyShift: false,
    usePreview: false,
    autoAccept: true,

    change: function(e, kb, el) {
        $scope.newUser.name = el.value;
    }
});

So on change triggered by the jQuery plugin I can run something. Obviously this only works updating a single field/model, the name one (while the email one doesn't work at all and will overwrite the name field), I need any number of fields to be updated when used with the keyboard, and the correct one. How do I solve this in a less terrible, not hardcoded (if possible and not too complex) way?

not amused
  • 313
  • 2
  • 4
  • 7
  • Use a directive to call excellent keyboard's keyboard method. In that case u can have the ngModel of the element. – Abilash Mar 21 '13 at 12:18
  • Suggesting something similar to ui-select2 of angular-ui.js – Abilash Mar 21 '13 at 12:19
  • Is Angular-UI necessary or even helpful for this? Anything more than a simple text field is overkill for this particular problem, unless it also includes a better on-screen keyboard already. – not amused Mar 21 '13 at 12:25
  • Not telling you to include `angular-ui.js`. I'm telling you to implement `keyboard` directive like how `ui-select2` has been implemented. Probably you can submit your code to `angular-ui.js`. – Abilash Mar 21 '13 at 12:29

2 Answers2

12

The way to write this in Angular is to actually write a directive. You can assosciate a directive with a particular class name.

So, your directive would look something like

app.directive('keyboard',function(){
  return {
    require : '?ngModel',
    restrict : 'C',
    link : function(scope,element,attrs,ngModelCtrl){
      if(!ngModelCtrl){
        return;
      }
      $(element).keyboard({
        stickyShift: false,
        usePreview: false,
        autoAccept: true,

        change: function(e, kb, el) {
            ngModelCtrl.$setViewValue(el.value);
        }
    });
    }
  };
});

Now if any element has both a class of keyboard and ng-Model defined then, your keyboard should pop-up. Hope this helps.

ganaraj
  • 26,841
  • 6
  • 63
  • 59
  • It sure does, that worked great. I'm new to AngularJS and don't have a background in web programming but I can tell this is nice framework to work with. I'm going to have to try figuring out when to use directives. – not amused Mar 21 '13 at 12:55
  • Wow.. I wrote that blind.. So, dint expect it to work without any modifications. If you need to manipulate the DOM or query the DOM and get hold of the element, you would generally need a directive. – ganaraj Mar 21 '13 at 13:03
  • I expect that you don't need to wrap `element` as `$(element)` as it is already a `jQuery` object. – Scotty.NET Jun 24 '13 at 14:29
  • For future googlers, I found the "accepted" event a better fit here than "change." The Mottie keyboard didn't seem to commit changes to the textbox until the user pressed the "Accept" button. It's possible the change function works OK if usePreview is set to false, as shown in the example here. – Jared Phelps Aug 01 '13 at 00:45
2

I made modification of ganraj directive. Now the model is updating on every click of keyboard button.

app.directive('keyboard',function(){
  return {
    require : '?ngModel',
    restrict : 'C',
    link : function(scope,element,attrs,ngModelCtrl){
      if(!ngModelCtrl){
        return;
      }

      $(element).keyboard({
        stickyShift: false,
        usePreview: false,
        autoAccept: true,

        change: function(e, kb, el) {
            if(!scope.$$phase && !scope.$root.$$phase)
            {
                scope.$apply(function(){
                    ngModelCtrl.$setViewValue(el.value);
                }); 
            }
        }
    });
    }
  };
});
Wojtek
  • 233
  • 1
  • 4
  • 16