【问题标题】:AngularJS - ng-model fails on contenteditable <span>AngularJS - ng-model 在 contenteditable <span> 上失败
【发布时间】:2014-06-25 01:27:26
【问题描述】:

我正在学习 AngularJS。我遇到了一些我无法解释的事情,也找不到任何解释(或解决方案)。

我有一个简单的 AngularJS 应用程序,我试图将 &lt;span contenteditable="true"&gt; 绑定到一个值,但它不起作用。例如:

<!-- 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>

如何使最后一个 span 使用 2-way 绑定,以便编辑其值更新 chunk.value ,反之亦然?

【问题讨论】:

  • span 元素是只读的。您需要某种输入来与 ng-model 进行 2 路绑定。您打算如何在第二个跨度中更改内容?
  • html中怎么会有只读的东西?我计划通过模型更改它,或者通过用户单击它并输入 - 因此contenteditable="true"
  • 如何在跨度或标签上输入数据?您可以更改它们的值,但它们会读取您给它们的任何值。
  • 1) 按 F12 调出开发控制台,现在就在此页面上。 2) 输入$('span').text('potato'); 并回车。 3) 通知更新内容。
  • 您希望您的用户使用开发人员工具来更改文本吗?用户可以编辑某些 DOM 元素,span 不是其中之一。这就是他所说的只读。

标签: javascript angularjs html contenteditable angularjs-ng-model


【解决方案1】:

ng-绑定!在 'span' 中使用 ng-bind 进行单向绑定。

请参考这里的例子:https://docs.angularjs.org/api/ng/directive/ngBind

所以你的行是: &lt;span contenteditable="true" ng-bind="chunk.value"&gt;&lt;/span&gt;

希望有帮助

【讨论】:

  • ng-bind 是单向绑定。使用 contenteditable 更改 span 中的内容不会更改底层模型。
【解决方案2】:

要使 ng-model 与 contenteditable &lt;span&gt; 元素一起使用,请使用自定义指令:

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>

有关详细信息,请参阅


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>

【讨论】:

    【解决方案3】:

    ngModel 不会像@VtoCorleone 指出的那样工作。 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.
    

    你可以看看contenteditable directive

    否则,潜在的解决方法:有一个被调用的函数。然后该函数会更新控制器中的$scope.chunk.value。随着绑定的更新,它会处理其他元素的内容。

    我不确定您想要的确切外观或功能,但只需将其放在&lt;textarea&gt; 中并将其样式设置为看起来像&lt;span&gt;(无边框或背景等)。然后当它在focus 中时,您添加额外的样式以知道它可以被编辑。这种方式将允许您使用ng-model,因为它打算被使用。这是此方法的基本实现:Plunker

    【讨论】:

    • 对不起,我没有关注 - 这个函数将如何被调用?我应该使用 jQuery .on('blur', ...) 还是什么?
    • 我的目标是没有任何样式 - 这就是我尝试使用跨度的原因。我正在寻找可编辑的文本段落,例如看起来像记事本的东西。 “块”是段落中的一段简单文本。
    • 我可能会将其作为附加到某种保存或更新按钮的点击事件
    • 我认为这就是AngularJS 擅长的地方吗?我不能强迫我的用户每次更新都点击一次——这太可笑了!
    • 如果你想要一个记事本之类的东西,为什么不使用 textarea 而不是 span?要么我完全误解了你想要做什么,要么你需要在进入 Angular 之前更多地了解 HTML 和 JS 基础知识。
    【解决方案4】:

    ng-model 不能与span 一起使用。如果您绝对需要,您可以为此编写自定义指令。该指令将在contentEditablespan 上设置keydown,keyup 侦听器并更新作用域模型(在$apply() 内)。这会将 span 内容绑定到模型。

    我很快为你创建了一个plunker。看看这个。它将&lt;span&gt; 内容同步到范围模型。每当您键入内容时,打开浏览器控制台以查看范围模型更新。

    【讨论】:

    • 你为什么要做这一切?跨度是从示波器的模型中读取的。为什么会有另一个作用域属性监听跨度的模型属性?
    • @VtoCorleone 我们当然不需要从模型绑定到跨度(它已经被处理好了)。只需在 span 内容更改时更新范围模型即可解决我们的问题。
    【解决方案5】:

    通过将ng-model-options="{ getterSetter: true }" 行为添加到附加了ng-model 的元素。您还可以将ng-model-options="{ getterSetter: true }" 添加到&lt;form&gt;,这将为其中的所有&lt;input&gt;s 启用此行为。

    示例展示了如何将ngModelgetter/setter 一起使用: demo page

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2017-11-19
      • 2013-03-13
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2015-04-19
      • 2015-03-11
      • 2015-12-12
      相关资源
      最近更新 更多