JSFiddle - React, Tailwind, and code Playground
by Dogbert
HTML
<script src="https://cdnjs.cloudflare.com/ajax/libs/angular.js/1.5.9/angular.js"></script>
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/bootswatch/3.3.7/cosmo/bootstrap.min.css">
<script src="https://cdnjs.cloudflare.com/ajax/libs/angular-sanitize/1.5.9/angular-sanitize.min.js"></script>
<div class="container-fluid" ng-app="blocks" ng-controller="MainCtrl">
<div class="row">
<div class="col-xs-6">
<div id="editor" contenteditable>
<div ng-repeat="block in blocks">
<div ng-switch on="block.type">
<div ng-switch-when="textblock" class="block textblock" editable ng-model="block.data"></div>
<ul ng-switch-when="list" class="block list">
<li ng-repeat="datum in block.data" class="block listitem" editable ng-model="datum.data"></li>
</ul>
</div>
</div>
</div>
</div>
<div class="col-xs-6">
<pre>{{blocks | json}}</pre>
</div>
</div>
</div>
CSS
pre {
font-size: 12px;
}
JavaScript
var app = angular.module('blocks', ['ngSanitize']);
app.directive('editable', ['$sce', function($sce) {
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
element.html(ngModel.$viewValue);
// Specify how UI should be updated
ngModel.$render = function() {
element.html($sce.getTrustedHtml(ngModel.$viewValue || ''));
};
// Listen for change events to enable binding
element.on('blur keyup change', function() {
scope.$evalAsync(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);
}
}
};
}]);
app.controller('MainCtrl', function($scope) {
$scope.blocks = [{
type: "textblock",
data: "<b>this is</b> some content"
}, {
type: "list",
data: [{
type: "listitem",
data: "<i>This is</i> a list item"
}, {
type: "listitem",
data: "<i>This is</i> also a list item"
}]
}];
});