Search code examples
angularjscontenteditableangular-ngmodel

Contenteditable with ng-model doesn't work


I'm trying to store the value of a contenteditable to my JS code. But I can't find out why ng-model doesn't work in this case.

<div ng-app="Demo" ng-controller="main">
    <input ng-model="inputValue"></input>
    <div>{{inputValue}}</div> // Works fine with an input
    <hr/>
    <div contenteditable="true" ng-model="contentValue"></div>
    <div>{{contentValue}}</div> // Doesn't work with a contenteditable
</div>

Is there a workaround to do that ?

See : JSFiddle

Note: I'm creating a Text editor, so the user should see the result, while I'm storing the HTML behind it. (ie. user see: "This is an example !", while I store: This is an <b>example</b> !)


Solution

  • contenteditable tag will not work directly with angular's ng-model because the way contenteditable rerender the dom element on every change.

    You have to wrap it with a custom directive for that:

    JS:

    angular.module('customControl', ['ngSanitize']).
    directive('contenteditable', ['$sce', function($sce) {
      return {
        restrict: 'A', // only activate on element attribute
        require: '?ngModel', // get a hold of NgModelController
        link: function(scope, element, attrs, ngModel) {
          if (!ngModel) return; // do nothing if no ng-model
    
          // Specify how UI should be updated
          ngModel.$render = function() {
            element.html($sce.getTrustedHtml(ngModel.$viewValue || ''));
          };
    
          // Listen for change events to enable binding
          element.on('blur keyup change', function() {
            scope.$evalAsync(read);
          });
          read(); // initialize
    
          // Write data to the model
          function read() {
            var html = element.html();
            // When we clear the content editable the browser leaves a <br> behind
            // If strip-br attribute is provided then we strip this out
            if ( attrs.stripBr && html == '<br>' ) {
              html = '';
            }
            ngModel.$setViewValue(html);
          }
        }
      };
    }]);
    

    HTML

    <form name="myForm">
     <div contenteditable
          name="myWidget" ng-model="userContent"
          strip-br="true"
          required>Change me!</div>
      <span ng-show="myForm.myWidget.$error.required">Required!</span>
     <hr>
     <textarea ng-model="userContent"></textarea>
    </form>
    

    Source it from the original docs