【问题标题】:AngularJS and contentEditable two way binding doesn't work as expectedAngularJS 和 contentEditable 两种方式绑定无法按预期工作
【发布时间】:2013-01-28 12:06:50
【问题描述】:

为什么在下面的例子中初始渲染的值是{{ person.name }}而不是David?你会如何解决这个问题?

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());
    }
  };
});

【问题讨论】:

  • 我只是借用了类似于你的代码来让我的页面正常工作(我也在使用 contenteditable div 进行编辑模式)。我遇到的一个问题是我有一个部分正在使用 ng-bind-html-unsafe 而不是 ng-model ...我将如何调整您的代码以在任何一种情况下工作?
  • @KevinHoffman,这段代码不需要 ng-bind-html-unsafe,只需使用 ng-model。

标签: angularjs contenteditable angularjs-directive


【解决方案1】:

问题是您在插值尚未完成时更新视图值。

所以删除

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

或替换为

ctrl.$render();

将解决问题。

【讨论】:

    【解决方案2】:

    简答

    您正在使用这一行从 DOM 初始化模型:

    ctrl.$setViewValue(element.html());
    

    您显然不需要从 DOM 初始化它,因为您在控制器中设置值。只需删除此初始化行。

    长答案(可能针对不同的问题)

    这实际上是一个已知问题:https://github.com/angular/angular.js/issues/528

    查看官方文档示例here

    HTML:

    <!doctype html>
    <html ng-app="customControl">
      <head>
        <script src="http://code.angularjs.org/1.2.0-rc.2/angular.min.js"></script>
        <script src="script.js"></script>
      </head>
      <body>
        <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>
      </body>
    </html>
    

    JavaScript:

    angular.module('customControl', []).
      directive('contenteditable', function() {
        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(ngModel.$viewValue || '');
            };
    
            // Listen for change events to enable binding
            element.on('blur keyup change', function() {
              scope.$apply(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);
            }
          }
        };
      });
    

    Plunkr

    【讨论】:

    • 这为我节省了很多时间。谢谢。
    • 您的回答对我也有帮助。如果我想将占位符与 ng-model 一起添加到 div,你能建议任何方法吗,因为文本 Change me! 会自动绑定到 ng-model
    【解决方案3】:

    这是我对自定义指令的理解。

    下面的代码是两种方式绑定的基本概述。

    你也可以在这里看到它的工作原理。

    http://plnkr.co/edit/8dhZw5W1JyPFUiY7sXjo

    <!doctype html>
    <html ng-app="customCtrl">
      <head>
        <script src="http://code.angularjs.org/1.2.0-rc.2/angular.min.js"></script>
        <script>
    
      angular.module("customCtrl", []) //[] for setter
      .directive("contenteditable", function () {
    
        return {
          restrict: "A",  //A for Attribute, E for Element, C for Class & M for comment
          require: "ngModel",  //requiring ngModel to bind 2 ways.
          link: linkFunc
        }
        //----------------------------------------------------------------------//
        function linkFunc(scope, element, attributes,ngModelController) {
            // From Html to View Model
            // Attaching an event handler to trigger the View Model Update.
            // Using scope.$apply to update View Model with a function as an
            // argument that takes Value from the Html Page and update it on View Model
            element.on("keyup blur change", function () {
              scope.$apply(updateViewModel)
            })
    
            function updateViewModel() {
              var htmlValue = element.text()
              ngModelController.$setViewValue(htmlValue)
            }
                  // from View Model to Html
                  // render method of Model Controller takes a function defining how
                  // to update the Html. Function gets the current value in the View Model
                  // with $viewValue property of Model Controller and I used element text method
                  // to update the Html just as we do in normal jQuery.
                  ngModelController.$render = updateHtml
    
                  function updateHtml() {
                    var viewModelValue = ngModelController.$viewValue
                    // if viewModelValue is change internally, and if it is
                    // undefined, it won't update the html. That's why "" is used.
                    viewModelValue = viewModelValue ? viewModelValue : ""
                    element.text(viewModelValue)
                  }
        // General Notes:- ngModelController is a connection between backend View Model and the 
        // front end Html. So we can use $viewValue and $setViewValue property to view backend
        // value and set backend value. For taking and setting Frontend Html Value, Element would suffice.
    
        }
      })
    
        </script>
      </head>
      <body>
        <form name="myForm">
        <label>Enter some text!!</label>
         <div contenteditable
              name="myWidget" ng-model="userContent"
              style="border: 1px solid lightgrey"></div>
         <hr>
         <textarea placeholder="Enter some text!!" ng-model="userContent"></textarea>
        </form>
      </body>
    </html>
    

    希望,它可以帮助那里的人。!!

    【讨论】:

    • 如何保持模型中 document.execCommand 的 html 格式?喜欢内容
    • 不知道你要什么。我离开了 angular1,因为 angular2+ 出来了。
    • 抱歉解释不善。如果我使用 execCommand 操作 textarea/div/etc..,它会生成一些 html 代码。这个 html 然后被删除。模型已清除 html 代码。有什么办法可以解决这个问题?喜欢 ngBindHtml?
    • 已解决:代替 element.text() -> element.html()。感觉很笨。
    【解决方案4】:

    【讨论】:

    • 尝试包含一些示例代码 - 不鼓励仅链接的答案。
    【解决方案5】:

    如果 scope.$apply 已经在进行中,您可能会在使用 @Vanaun 的代码时遇到问题。在这种情况下,我使用 $timeout 来解决问题:

    HTML:

    <!doctype html>
    <html ng-app="customControl">
      <head>
        <script src="http://code.angularjs.org/1.2.0-rc.2/angular.min.js"></script>
        <script src="script.js"></script>
      </head>
      <body>
        <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>
      </body>
    </html>
    

    JavaScript:

    angular.module('customControl', []).
      directive('contenteditable', function($timeout) {
        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(ngModel.$viewValue || '');
            };
    
            // Listen for change events to enable binding
            element.on('blur keyup change', function() {
              $timeout(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);
            }
          }
        };
      });
    

    工作示例:Plunkr

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2021-04-23
      • 1970-01-01
      • 1970-01-01
      • 2018-08-15
      • 2014-02-13
      • 1970-01-01
      • 2016-02-27
      相关资源
      最近更新 更多