AngularJS and contentEditable two way binding doesn't work as expected

Misha Moroshko picture Misha Moroshko · Jan 28, 2013 · Viewed 27.1k times · Source

Why in the following example the initial rendered value is {{ person.name }} rather than David? How would you fix this?

Live example here

HTML:

<body ng-controller="MyCtrl">
  <div contenteditable="true" ng-model="person.name">{{ person.name }}</div>
  <pre ng-bind="person.name"></pre>
</body>

JS:

app.controller('MyCtrl', function($scope) {
  $scope.person = {name: 'David'};
});

app.directive('contenteditable', function() {
  return {
    require: 'ngModel',
    link: function(scope, element, attrs, ctrl) {
      // view -> model
      element.bind('blur', function() {
        scope.$apply(function() {
          ctrl.$setViewValue(element.html());
        });
      });

      // model -> view
      ctrl.$render = function() {
        element.html(ctrl.$viewValue);
      };

      // load init value from DOM
      ctrl.$setViewValue(element.html());
    }
  };
});

Answer

asgoth picture asgoth · Jan 28, 2013

The problem is that you are updating the view value when the interpolation is not finished yet.

So removing

// load init value from DOM
ctrl.$setViewValue(element.html());

or replacing it with

ctrl.$render();

will resolve the issue.