Question

I'm learning AngularJS. I've come across something I can't explain, nor can I find any explanation for (or solution).

I have a simple AngularJS app and I am attempting to bind a <span contenteditable="true"> to a value, but it doesn't work. EG:

<!-- Works as expected -->
<input data-ng-model="chunk.value"></input>

<!-- Shows value, but doesn't bind - changes not reflected in model -->
<span contenteditable="true">{{chunk.value}}</span>

<!-- This is empty -->
<span contenteditable="true" data-ng-model="chunk.value"></span>

How can I make the last span use 2-way binding, such that editing its value updates chunk.value and vice versa?

Était-ce utile?

La solution

ng-bind! Use ng-bind for one-way binding in 'span'.

Please refer here for an example: https://docs.angularjs.org/api/ng/directive/ngBind

So your line would be: <span contenteditable="true" ng-bind="chunk.value"></span>

Hope this help

Autres conseils

To make ng-model work with contenteditable <span> elements, use a custom directive:

app.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);
        }
      }
    };
}]);

Usage:

<span contenteditable ng-model="userContent">Change me!</span>
<p>{{userContent}}</p>

For more infomation, see


The DEMO

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);
        }
      }
    };
  }]);
[contenteditable] {
  border: 1px solid black;
  background-color: white;
  min-height: 20px;
}
<script src="//unpkg.com/angular/angular.js"></script>
<script src="//unpkg.com/angular-sanitize/angular-sanitize.js"></script>
<body ng-app="customControl">
 <span contenteditable ng-model="userContent">Change me!</span>
 <hr>
 Content={{userContent}}
</body>

The ngModel won't work as @VtoCorleone pointed out. ngModel docs

The ngModel directive binds an input,select, textarea (or custom form control) to a property on the scope using NgModelController, which is created and exposed by this directive.

You may have a look at the contenteditable directive.

Otherwise, Potential workaround: have a function that gets called. That function then updates the $scope.chunk.value within your controller. And it would take care of the other elements' contents as the binding gets updated.

I'm not sure the exact look or functionality that you're going for, but just put it inside of a <textarea> and style it to look like a <span> (no border or background, etc). Then when it is in focus, you add additional styling to know that it can be edited. This way would allow you to use the ng-model as it is intended to be used. Here is a basic implementation of this approach: Plunker

ng-model is not meant to be used with span. If you absolutely need that you can write a custom directive for this. This directive will set up a keydown,keyup listener on the contentEditable span and update the scope model (within $apply()). This will bind span content to model.

I quickly created a plunker for you. Check it out. It syncs the <span> content to scope model. Open up the browser console to see the scope model update whenever you type something.

By adding ng-model-options="{ getterSetter: true }" behavior to an element that has ng-model attached to it. You can also add ng-model-options="{ getterSetter: true }" to a <form>, which will enable this behavior for all <input>s within it.

Example shows how to use ngModel with a getter/setter: demo page

Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top