43

为什么在下面的示例中初始渲染值是{{ 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>

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

5 回答 5

46

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

所以删除

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

或将其替换为

ctrl.$render();

将解决问题。

于 2013-01-28T12:37:51.160 回答
14

简短的回答

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

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

您显然不需要从 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>

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

于 2013-09-10T10:50:27.187 回答
2

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

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

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

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>

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

于 2015-12-17T20:33:48.527 回答
0

检查这个 angularjs 指令 https://github.com/clofus/angular-inputnlabel http://clofus.com/viewarticles/109

于 2015-09-29T09:53:10.250 回答
0

如果 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

于 2017-06-07T14:26:29.217 回答