angular-contenteditable
https://github.com/akatov/angular-contenteditable
HTML
<script src="http://code.angularjs.org/1.2.5/angular.min.js"></script>
<div ng-controller="Ctrl">
<div contenteditable="true"
ng-model="model"
moveCaretToEndOnChange = 'true'
strip-br="true"
select-non-editable="true">
</div>
</div>
{{model}}
CSS
div, span {width:300px;height:200px;border:1px solid black;}
JavaScript
angular.module('myapp', ['contenteditable'])
.controller('Ctrl', ['$scope', function($scope) {
$scope.model="<i>interesting</i> stuff"
}]);
/**
* @see http://docs.angularjs.org/guide/concepts
* @see http://docs.angularjs.org/api/ng.directive:ngModel.NgModelController
* @see https://github.com/angular/angular.js/issues/528#issuecomment-7573166
*/
angular.module('contenteditable', [])
.directive('contenteditable', ['$timeout', function($timeout) { return {
restrict: 'A',
require: '?ngModel',
link: function(scope, element, attrs, ngModel) {
// don't do anything unless this is actually bound to a model
if (!ngModel) {
return
}
// options
var opts = {}
angular.forEach([
'stripBr',
'noLineBreaks',
'selectNonEditable',
'moveCaretToEndOnChange',
], function(opt) {
var o = attrs[opt]
opts[opt] = o && o !== 'false'
})
// view -> model
element.bind('input', function(e) {
scope.$apply(function() {
var html, html2, rerender
html = element.html()
rerender = false
if (opts.stripBr) {
html = html.replace(/<br>$/, '')
}
if (opts.noLineBreaks) {
html2 = html.replace(/<div>/g, '').replace(/<br>/g, '').replace(/<\/div>/g, '')
if (html2 !== html) {
rerender = true
html = html2
}
}
ngModel.$setViewValue(html)
if (rerender) {
ngModel.$render()
}
if (html === '') {
// the cursor disappears if the contents is empty
// so we need to refocus
$timeout(function(){
element[0].blur()
element[0].focus()
})
}
})
})
// model -> view
var oldRender = ngModel.$render
ngModel.$render = function() {
var el, el2, range, sel
...