5

I have the following on a page - full code in this Plunker

There is a custom onEnter directive that calls a function when enter is pressed on a chat form input. Code snippet below

//**HTML View**
<div ng-controller="mainCtrl">
    <ul>
      <li ng-repeat="chat in chatMessages">
        {{chat.username}}<br/>
           {{chat.message}}
        </li>
    </ul>
</div>
<form id="chatForm" name="chatForm" ng-controller="formCtrl">
    <label for="chat-username">User: </label>
    <input type="text" id="chat-username" class="chat__username" ng-model="chat.username" required>
    <label for="chat-input">Chat: </label> 
    <input type="text" id="chat-input" class="chat__input" on-enter="sendChat()" ng-model="chat.message" required>
    <a href="#" class="chat__submit icon-comments" id="chat-submit"  ng-click="sendChat()" ng-disabled="isChatValid()">Chatme</a>
</form>


//**Javascript**
app.controller('formCtrl',function($scope,Chats){
  $scope.sendChat = function() {
    if($scope.isChatValid()) {
      return;
    }
    console.log(JSON.stringify($scope.chat));
    var msg = {};
    angular.copy($scope.chat,msg);
    Chats.data.push(msg);       
  };

  $scope.isChatValid = function() {
    return $scope.chatForm.$invalid;
  };
});

Problem is the value of the input (message) is not saved into the scope model (chat). If I remove the onenter directive it works. What am I missing here? Any help will be great

Aliaksei Kliuchnikau
  • 13,589
  • 4
  • 59
  • 72
Mike E.
  • 2,163
  • 1
  • 15
  • 13

4 Answers4

12

Ok figured it out. Turns out when you define a scope object in your directive you create a new child scope. Details found here:

Directive with scope breaking ngmodel of an input

To fix this I removed the scope declaration and used the value of the function from attrs object.

Directive looks like this now:

app.directive('onEnter',function() {

  var linkFn = function(scope,element,attrs) {
    element.bind("keypress", function(event) {
      if(event.which === 13) {
        scope.$apply(function() {
      scope.$eval(attrs.onEnter);
        });
        event.preventDefault();
      }
    });
  };

  return {
    link:linkFn
  };
});

Full code in plunker

Community
  • 1
  • 1
Mike E.
  • 2,163
  • 1
  • 15
  • 13
  • ah didn't see you also got it – shaunhusain Aug 22 '13 at 01:12
  • 1
    personally I don't like this solution because it uses $eval – Iamisti Sep 02 '15 at 07:23
  • 1
    At the beginning I had the same opinion as @lamisti . However, I realized that this solution has big advantage - it does not create isolate scope, so there'll be no conflicts with other potential directives with isolate scope, that might be applied to the same DOM element. – Klon Dec 01 '16 at 10:54
3

For angular 1.2 i wrote like:

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

app.directive('onEnter', function() {
    return {
        scope: {onEnter: '&'},
        link: function(scope, element) {
            console.log(scope);
            element.bind("keydown keypress", function(event) {
                if(event.which === 13) {
                    scope.onEnter();
                    scope.$apply();
                }
            });
        }
    }
});
zool
  • 116
  • 3
1

You should use $parent to refer to the chat object like this

$parent.chat.message

Because you use the directive which declares a child scope. Here is the full html:

<input type="text" id="chat-input" class="chat__input" on-enter="sendChat()" ng-model="$parent.chat.message" required>
zs2020
  • 53,766
  • 29
  • 154
  • 219
0

In reply to @zool:

I had to modify your answer as follows to avoid my onEnter function being called twice:

[...]
if(event.which === 13) {
    event.preventDefault();
    scope.onEnter();
    scope.$apply();
}
[...]

Also see AngularJS form not submitting when pressing enter

Community
  • 1
  • 1
bac
  • 226
  • 3
  • 10