2

I've implemented the foo component whose template has an input (binded to fooValue property) with a directive attribute that calls that component controller function onEnter when "enter" key is pressed when the input has focus.

The issue that I'm getting is, after inserting a value in the input (i.e. "foo3") and pressing "enter" key, fooValue is not update with the input value.

Here is the snippet:

var app = angular.module('app', []);

app.component('foo', {
  template: '<input ng-model="$ctrl.fooValue" enter-key="$ctrl.onEnter()" />',
  controller: function() {
    this.onEnter = function() {
      // this.fooValue is not updated with input value
    };
  },
  bindings: {
    fooValue: '@'
  }
});

app.directive('enterKey', function() {
  return {
    restrict: 'A',
    link: function(scope, element, attrs) {
      element.bind("keydown keypress", function(event) {
        var keyCode = event.which || event.keyCode;

        // If enter key is pressed
        if (keyCode === 13) {
          scope.$apply(function() {
            // Evaluate the expression
            scope.$eval(attrs.enterKey);
          });
        }
      });
    }
  };
});

describe('test', function() {
  describe('foo', function() {
    beforeEach(module('app'));

    var element,
      inputElem;

    beforeEach(inject(function($compile, $rootScope) {
      var fooScope = $rootScope.$new();

      element = angular.element('<foo foo-value="foo1"></foo>');
      element = $compile(element)(fooScope);

      fooScope.$digest();
    }));

    it('should set fooValue with foo3', function() {
      var controller = element.controller('foo');
      inputElem = element.find('input');
      inputElem.val('foo3');
      inputElem.triggerHandler({
        type: 'keydown',
        which: 13
      });

      expect(controller.fooValue).toBe('foo3');
    });
  });
});
<!DOCTYPE html>
<html ng-app="app">

<head>
  <link rel="stylesheet" href="//cdn.jsdelivr.net/jasmine/2.0.0/jasmine.css" />
  <script src="//cdn.jsdelivr.net/jasmine/2.0.0/jasmine.js"></script>
  <script src="//cdn.jsdelivr.net/jasmine/2.0.0/jasmine-html.js"></script>
  <script src="//cdn.jsdelivr.net/jasmine/2.0.0/boot.js"></script>
  <script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.6.1/angular.js"></script>
  <script src="https://code.angularjs.org/1.6.1/angular-mocks.js"></script>
</head>

<body>
</body>

</html>

What's missing to make fooValue updated?

Rui Lopes
  • 106
  • 1
  • 12
  • The directive executes functions on `portal-enter-key` and the component puts the function on `enter-key` – georgeawg May 27 '18 at 10:22
  • @georgeawg, thank you answering, just updated the snipped, "attrs.enterKey" is what I want. However, this isn't the problem, has the issue still persists. – Rui Lopes May 27 '18 at 13:52
  • 1
    The AngularJS `` directive uses the [`input` event](https://developer.mozilla.org/en-US/docs/Web/Events/input) to update the model. – georgeawg May 27 '18 at 14:03
  • Thank you @georgeawg, that was it. Meanwhile, because I also want to support IE11 I'll have to use `change` instead, otherwise `input` would do. – Rui Lopes May 27 '18 at 18:15

1 Answers1

0

To update fooValue the input event is required to be fired. In case IE11 support is needed, use the change event.

The following snipped shows it working:

var app = angular.module('app', []);

app.component('foo', {
  template: '<input ng-model="$ctrl.fooValue" enter-key="$ctrl.onEnter()" />',
  controller: function() {
    this.onEnter = function() {
      // this.fooValue is now updated with input value
    };
  },
  bindings: {
    fooValue: '@'
  }
});

app.directive('enterKey', function() {
  return {
    restrict: 'A',
    link: function(scope, element, attrs) {
      element.bind("keydown keypress", function(event) {
        var keyCode = event.which || event.keyCode;

        // If enter key is pressed
        if (keyCode === 13) {
          scope.$apply(function() {
            // Evaluate the expression
            scope.$eval(attrs.enterKey);
          });
        }
      });
    }
  };
});

describe('test', function() {
  describe('foo', function() {
    beforeEach(module('app'));

    var element,
      inputElem;

    beforeEach(inject(function($compile, $rootScope) {
      var fooScope = $rootScope.$new();

      element = angular.element('<foo foo-value="foo1"></foo>');
      element = $compile(element)(fooScope);

      fooScope.$digest();
    }));

    it('should set fooValue with foo3', function() {
      var controller = element.controller('foo');
      inputElem = element.find('input');
      inputElem.val('foo3');
      inputElem.triggerHandler('input');  // fire the input event to update fooValue with 'foo3'
      // inputElem.triggerHandler('change'); for IE11 
      inputElem.triggerHandler({
        type: 'keydown',
        which: 13
      });

      expect(controller.fooValue).toBe('foo3');
    });
  });
});
<!DOCTYPE html>
<html ng-app="app">

<head>
  <link rel="stylesheet" href="//cdn.jsdelivr.net/jasmine/2.0.0/jasmine.css" />
  <script src="//cdn.jsdelivr.net/jasmine/2.0.0/jasmine.js"></script>
  <script src="//cdn.jsdelivr.net/jasmine/2.0.0/jasmine-html.js"></script>
  <script src="//cdn.jsdelivr.net/jasmine/2.0.0/boot.js"></script>
  <script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.6.1/angular.js"></script>
  <script src="https://code.angularjs.org/1.6.1/angular-mocks.js"></script>
</head>

<body>
</body>

</html>
Rui Lopes
  • 106
  • 1
  • 12