为什么在以下示例中,初始渲染值为{{ 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());
}
};
});
答案 0 :(得分:45)
问题是当插值尚未完成时,您正在更新视图值。
删除
// load init value from DOM
ctrl.$setViewValue(element.html());
或替换为
ctrl.$render();
将解决问题。
答案 1 :(得分:14)
简短回答
您正在使用以下行从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);
}
}
};
});
答案 2 :(得分:1)
以下是我对Custom指令的理解。
以下代码是双向绑定的基本概述。
你也可以看到它在这里工作。
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>
希望,它可以帮助那些人。!!
答案 3 :(得分:0)
答案 4 :(得分:0)
如果范围。$ apply已在进行中,您可能会遇到使用@Vanaun代码的问题。在这种情况下,我使用$ timeout来解决问题:
HTML:
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);
}
}
};
});
JavaScript的:
{{1}}
工作示例:Plunkr