0

Я пытаюсь создать редактор, который делает «подсветку синтаксиса», это довольно просто:angularjs с цветами текстового поля (с html5 contenteditable)

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(/&lt;/, '<'); 
     html = html.replace(/&gt;/, '>'); 
     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() но имеет нет эффекта. Я должен пропустить что-то действительно очевидное ...

Ссылки я уже прочитал:

Любая помощь очень ценится. См. Демонстрацию plunker выше.

ответ

0

Спустя почти год я наконец обосновался до Codemirror, и я никогда не был счастливее. Я делаю бок о бок с редактированием исходного кода с живым обновлением (с подсветкой синтаксиса, поэтому даже немного более продвинутый, чем страница редактирования stackoverflow.)

Я создал простую кодовую директиву codeEditor, которая требует кодаMirror и использует его.

Для полноты здесь компонент Исходный код:

$ 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 исходный код.

Я очень рекомендую codeMirror. Он является компонентом rockolid, работает в каждой комбинации браузеров (firefox, firefox для android, chromium).