AngularJS - ng-model在contenteditable <span>上失效

16
我正在学习AngularJS。我遇到了一些无法解释的问题,也找不到任何解释或解决方案。
我有一个简单的AngularJS应用程序,我试图将<span contenteditable="true">绑定到一个值,但它不起作用。例如:
<!-- 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>

如何使最后一个标签使用双向绑定?这样,编辑它的值可以更新chunk.value,反之亦然。


span元素是只读的。您需要某种输入来进行双向绑定,使用ng-model。您打算如何更改第二个span中的内容? - VtoCorleone
在HTML中怎么可能有只读的内容?我计划通过模型或用户点击并输入来更改它 - 因此使用contenteditable="true" - Alex McMillan
你如何在 span 或 label 上输入数据?你可以更改它们的值,但它们会读取你给它们的任何值。 - VtoCorleone
  1. 按下F12键打开开发控制台,就在这个页面上,在此刻。
  2. 输入$('span').text('potato');并按回车键。
  3. 注意更新后的内容。
- Alex McMillan
你是否期望用户使用开发者工具来更改文本?用户可以编辑某些DOM元素,但span不是其中之一。这就是他所说的只读。 - JonK
@JonK 请阅读有关 contentEditable 的相关资料。 - Alex McMillan
5个回答

23

ng-bind!在标签中使用ng-bind进行单向绑定。

请参考此处的示例:https://docs.angularjs.org/api/ng/directive/ngBind

因此,您的代码应该是:<span contenteditable="true" ng-bind="chunk.value"></span>

希望这可以帮助到你。


1
ng-bind 是单向绑定。使用 contenteditable 更改 span 中的内容不会更改底层模型。 - Will

1
为了使 ng-model 与可编辑的 <span> 元素配合使用,请使用自定义指令:
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);
        }
      }
    };
}]);

使用方法:

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

更多信息,请参见


演示

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>


0

ngModel 不起作用,正如 @VtoCorleone 指出的那样。ngModel 文档

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.

您可以查看contenteditable指令

另外,可能的解决方法是创建一个函数并调用它。该函数会更新您控制器中的$scope.chunk.value,并且会在绑定更新时处理其他元素的内容。

我不确定您想要的确切外观或功能,但只需将其放入<textarea>中并将其样式设置为类似于<span>(无边框或背景等)。然后,当它处于focus状态时,您可以添加额外的样式来知道它可以进行编辑。这种方法可以让您按照预期使用ng-model。以下是此方法的基本实现:Plunker


我可能会将其作为单击事件,附加到某种保存或更新按钮上。 - EnigmaRM
如果你想要像记事本一样的东西,为什么不使用textarea而不是span?我可能完全误解了你想做什么,或者在跳入Angular之前,你需要更好地理解HTML和JS基础知识。 - VtoCorleone
你不能简单地用 textarea 替换你的 span 吗? - EnigmaRM
@AlexMcMillan,我添加了一个基本的plunker,展示了我的建议。http://plnkr.co/edit/EsZfXaRqNMOVLh7WToVi?p=preview - EnigmaRM
页面上会有很多这样的元素,我认为span被认为更加“轻量级”?或者说在页面上有500个span和500个textarea会减少同样的性能吗? - Alex McMillan
显示剩余10条评论

0

ng-model 不应与 span 一起使用。如果您确实需要这样做,可以编写自定义指令来实现。该指令将在 contentEditablespan 上设置一个 keydown,keyup 监听器,并更新作用域模型(在 $apply() 内)。这将绑定 span 内容到模型。

我为您快速创建了一个 plunker。请查看它。它将<span>内容同步到作用域模型。打开浏览器控制台,可以看到每次输入时作用域模型的更新。


你为什么要做这些呢?span正在从作用域模型中读取。为什么还要有另一个作用域属性监听span的模型属性呢? - VtoCorleone
@VtoCorleone,我们确实不需要从模型到span的绑定(这已经得到了处理)。只需在span内容更改时更新作用域模型即可解决我们的问题。 - Sandeep Panda

0
通过将ng-model-options="{ getterSetter: true }"行为添加到已附加ng-model的元素上,您还可以将ng-model-options="{ getterSetter: true }"添加到<form>中,这将使其在其中的所有<input>中启用此行为。
示例显示了如何使用带有getter/setterngModel演示页面

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接