-2

I have a Font Awesome icon picker, which I use for my application so the employees can easily access the different icons without searching for the codes online.

Now when I click on an icon, it does not update the ng-model. I first have to enter another character or a space for AngularJS to identify the changes made in the input.

https://i.gyazo.com/21d80e370726166a200f1165e169a0cf.gif, hereby the example of what's happening.

As you can see, in the bottom left I have made a label showing the data that's in the same ng-model. Now when I choose an icon, the model doesn't update. Only after I enter a space, or another character it's updating.

The code I have:

editCellTemplate: function (container, options) {
    container.html('<input class="icon-picker-input edit icp icp-auto" type="text" ng-model="iconInput" /><script>$(".icp-auto").iconpicker();</script>');
    options.setValue($scope.iconInput);
    $compile(container)($scope);
}

I'm using a gridview from DevExtreme with a custom editCellTemplate.

The binding:

enter image description here

Which is declared here:

enter image description here

Does anyone have a clue on how to fix this? Thanks in advance!

peer
  • 1,001
  • 4
  • 13
  • 29
  • Could you please add relevant code? – Deblaton Jean-Philippe Jun 16 '16 at 08:59
  • @DeblatonJean-Philippe I've added some code, that should be enough to understand what's going on. – peer Jun 16 '16 at 09:01
  • This is a template used for every cell in the grid, right? Please share the code for `iconpicker()`. And show the binding you used for the label at the bottom. – Cobus Kruger Jun 16 '16 at 09:11
  • @CobusKruger This template is used for every cell in the grid indeed. The iconpicker() is a library: https://github.com/mjolnic/fontawesome-iconpicker/, the binding I will add to the question. – peer Jun 16 '16 at 09:21

2 Answers2

2

The main reason your code isn't working as intended is that Angular only watches for user interaction events in order to update the model. Your icon picker completely bypasses Angular by directly setting the value in the input.

In order to update the model you need to set a hook on the icon picker updating process : whenever you select an icon, either overwrite the iconInput scope variable yourself (and wrap this in a $scope.$apply call) or much more simply, trigger the 'input' event on the input element, which will cause Angular to pick up the new value (see here).

I suggest you do something like this :

editCellTemplate: function (container, options) {
    container.html('<input class="icon-picker-input edit icp icp-auto" type="text" ng-model="iconInput" />');
    options.setValue($scope.iconInput);        
    $compile(container)($scope);

    // Avoid inline script tags, you can make the iconpicker here
    $(".icp-auto").iconpicker();

    // Watch for icon picker selections
    $(".icp-auto").on('iconpickerSelected', function(e) {
        // Fire the "input changed" event
        $(e.currentTarget).trigger('input');
    });
}

Notice I removed the script tag inside your compiled html because you can perfectly instantiate the icon picker in your main code. Script tags evaluate in the global context which is often not what you want.

Update : The compile-time error you reported in your comment is due to the fact that your Typescript setup has a definition for the JQuery class (probably a jquery.d.ts file) which does not include an iconPicker() method. At runtime the content of the <script> tag compiled in Angular is directly interpreted as plain Javascript, avoiding Typescript's type checking.

See this answer for a simple way to enable additional methods on the JQuery interface. I strongly recommend you stop putting logic in compiled <script> elements, there is a high chance it will come back to bite you.

Community
  • 1
  • 1
ttzn
  • 2,543
  • 22
  • 26
  • First off, when I try loading it with JQuery, I'm getting this error: https://gyazo.com/82326d20be3b8f655c010907a8c3157a, `>> Scripts/App/intranet/controllers/manageintranetcontroller.ts(31,48): error TS2339: Property 'iconpicker' does not exist on type 'JQuery'.` – peer Jun 16 '16 at 12:03
  • 1
    Ok I overlooked the fact that you were using Typescript. To resolve that compile-time error you'd need to define the `iconpicker` method on the JQuery type. But just to get your thing working, just jam the `iconpicker()` call back in the script tag. – ttzn Jun 16 '16 at 12:31
  • It's working a lot better now indeed, it updates the ng-model immediately. Thank you, still having a little bit of trouble.. but this is a big step. Thanks! – peer Jun 16 '16 at 12:35
  • 1
    @Peurr you're welcome, I'll update my answer later to account for the Typescript subtlety. – ttzn Jun 16 '16 at 12:48
0

Your problem is that iconpicker() updates the input without Angular noticing. The way to fix that, is to call $scope.$apply() directly after. The problem here is the weird way you're including your script. That jQuery syntax will call the iconpicker() for only the first element matched anyway, so I don't think it will work at all if you edit the second row in your demo.

Instead, generate a numeric id and change to this:

editCellTemplate: function (container, options) {
    container.html('<input class="icon-picker-input edit icp icp-auto"' +
      ' type="text" ng-model="iconInput" id="uniqueID"/>' +
      '<script>$("#uniqueID").iconpicker();$scope.$apply();</script>');
    options.setValue($scope.iconInput);
    $compile(container)($scope);
}

...where uniqueID is obviously a unique ID. I'll leave that as an exercise for the reader.

Cobus Kruger
  • 8,338
  • 3
  • 61
  • 106
  • After adding the ID and the `$scope.apply();`, I'm receiving this error: `:10966/#/intranet:1 Uncaught ReferenceError: $scope is not defined`. **The code now:** `editCellTemplate: function (container, options) { container.html(''); options.setValue($scope.iconInput); $compile(container)($scope); }` – peer Jun 16 '16 at 10:01
  • This can't work. `$scope.$apply` needs to be called at every update. – ttzn Jun 16 '16 at 10:05
  • 1
    @Peurr Yes, this is the problem with the way you're attaching the script. The correct way would be to just attach it in the controller where $scope is defined. Amine's answer effectively does this and then does the $apply call. That's a good answer to go with. – Cobus Kruger Jun 16 '16 at 17:45