AngularJS和contentEditable双向绑定不能按预期工作

Mis*_*hko 42 contenteditable angularjs angularjs-directive

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

这里有实例

HTML:

<body ng-controller="MyCtrl">
  <div contenteditable="true" ng-model="person.name">{{ person.name }}</div>
  <pre ng-bind="person.name"></pre>
</body>
Run Code Online (Sandbox Code Playgroud)

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());
    }
  };
});
Run Code Online (Sandbox Code Playgroud)

asg*_*oth 45

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

所以删除

// load init value from DOM
ctrl.$setViewValue(element.html());
Run Code Online (Sandbox Code Playgroud)

或者替换它

ctrl.$render();
Run Code Online (Sandbox Code Playgroud)

将解决问题.


Van*_*uan 14

简短的回答

您正在使用以下行从DOM初始化模型:

ctrl.$setViewValue(element.html());
Run Code Online (Sandbox Code Playgroud)

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

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

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

请参阅此处的官方文档示例

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>
Run Code Online (Sandbox Code Playgroud)

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);
        }
      }
    };
  });
Run Code Online (Sandbox Code Playgroud)

Plunkr