angularjs textarea с цветами (с поддержкой html5)
Я пытаюсь создать редактор, который делает "подсветку синтаксиса", это довольно просто:
yellow -> <span style="color:yellow">yellow</span>
Я также использую <code contenteditable>
html5 тег для замены <textarea>
и имеют цветной вывод.
Я начал с документации angularjs и создал следующую простую директиву. Это работает, кроме как не обновлять contenteditable
область с созданным HTML. Если я использую element.html(htmlTrusted)
вместо ngModel.$setViewValue(htmlTrusted)
, все работает, кроме того, что курсор переходит в начало при каждом нажатии клавиши.
директива:
app.directive("contenteditable", function($sce) {
return {
restrict: "A", // only activate on element attribute
require: "?ngModel", // get ng-model, if not provided in html, then null
link: function(scope, element, attrs, ngModel) {
if (!ngModel) {return;} // do nothing if no ng-model
element.on('blur keyup change', function() {
console.log('app.directive->contenteditable->link->element.on()');
//runs at each event inside <div contenteditable>
scope.$evalAsync(read);
});
function read() {
console.log('app.directive->contenteditable->link->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 = '';
}
html = html.replace(/</, '<');
html = html.replace(/>/, '>');
html = html.replace(/<span\ style=\"color:\w+\">(.*?)<\/span>/g, "$1");
html = html.replace('yellow', '<span style="color:yellow">yellow</span>');
html = html.replace('green', '<span style="color:green">green</span>');
html = html.replace('purple', '<span style="color:purple">purple</span>');
html = html.replace('blue', '<span style="color:yellow">blue</span>');
console.log('read()-> html:', html);
var htmlTrusted = $sce.trustAsHtml(html);
ngModel.$setViewValue(htmlTrusted);
}
read(); // INITIALIZATION, run read() when initializing
}
};
});
HTML:
<body ng-app="MyApp">
<code contenteditable
name="myWidget" ng-model="userContent"
strip-br="true"
required>This <span style="color:purple">text is purple.</span> Change me!</code>
<hr>
<pre>{{userContent}}</pre>
</body>
plunkr: demo (тип yellow
, green
или же blue
в область ввода изменить меня)
Я старался scope.$apply()
, ngModel.$render()
но не имеет никакого эффекта. Я должен пропустить что-то действительно очевидное...
Ссылки, которые я уже прочитал:
- демо чужой плункер 1
- демоверсия чужого плунжера 2
- Пример документации angularjs
- $ sce.trustAsHtml stackru вопрос
- setViewValue stackru вопрос
- setViewValue не обновляет вопрос stackru
Буду признателен за любую оказанную помощь. Пожалуйста, посмотрите демо плунжера выше.
1 ответ
Спустя почти год я наконец-то остановился на Codemirror, и я никогда не был счастливее. Я делаю параллельное редактирование исходного кода уценки с живым обновлением (с подсветкой синтаксиса, так что даже немного более продвинутый, чем страница редактирования stackru).
Я создал простую угловую директиву codeEditor, которая требует codeMirror и использует ее.
Для полноты вот исходный код компонента:
$ cat components/codeEditor/code-editor.html
<div class="code-editor"></div>
$ cat codeEditor.js
'use strict';
angular.module('myApp')
.directive('codeEditor', function($timeout, TextUtils){
return {
restrict: 'E',
replace: true,
require: '?ngModel',
transclude: true,
scope: {
syntax: '@',
theme: '@'
},
templateUrl: 'components/codeEditor/code-editor.html',
link: function(scope, element, attrs, ngModelCtrl, transclude){
// Initialize Codemirror
var option = {
mode: scope.syntax || 'xml',
theme: scope.theme || 'default',
lineNumbers: true
};
if (option.mode === 'xml') {
option.htmlMode = true;
}
scope.$on('toedit', function () { //event
//This is required to correctly refresh the codemirror view.
// otherwise the view stuck with 'Both <code...empty.' initial text.
$timeout(function() {
editor.refresh();
});
});
// Require CodeMirror
if (angular.isUndefined(window.CodeMirror)) {
throw new Error('codeEditor.js needs CodeMirror to work... (o rly?)');
}
var editor = window.CodeMirror(element[0], option);
// Handle setting the editor when the model changes if ngModel exists
if(ngModelCtrl) {
// Timeout is required here to give ngModel a chance to setup. This prevents
// a value of undefined getting passed as the view is rendered for the first
// time, which causes CodeMirror to throw an error.
$timeout(function(){
ngModelCtrl.$render = function() {
if (!!ngModelCtrl.$viewValue) {
// overwrite <code-editor>SOMETHING</code-editor>
// if the $scope.content.code (ngModelCtrl.$viewValue) is not empty.
editor.setValue(ngModelCtrl.$viewValue); //THIRD happening
}
};
ngModelCtrl.$render();
});
}
transclude(scope, function(clonedEl){
var initialText = clonedEl.text();
if (!!initialText) {
initialText = TextUtils.normalizeWhitespace(initialText);
} else {
initialText = 'Both <code-editor> tag and $scope.content.code is empty.';
}
editor.setValue(initialText); // FIRST happening
// Handle setting the model if ngModel exists
if(ngModelCtrl){
// Wrap these initial setting calls in a $timeout to give angular a chance
// to setup the view and set any initial model values that may be set in the view
$timeout(function(){
// Populate the initial ng-model if it exists and is empty.
// Prioritize the value in ngModel.
if(initialText && !ngModelCtrl.$viewValue){
ngModelCtrl.$setViewValue(initialText); //SECOND happening
}
// Whenever the editor emits any change events, update the value
// of the model.
editor.on('change', function(){
ngModelCtrl.$setViewValue(editor.getValue());
});
});
}
});
// Clean up the CodeMirror change event whenever the directive is destroyed
scope.$on('$destroy', function(){
editor.off('change');
});
}
};
});
Существует также внутри components/codeEditor/vendor
каталог с полным исходным кодом.
Я очень рекомендую codeMirror. Это надежный компонент, работающий в любой комбинации браузеров (Firefox, Firefox для Android, Chromium).