0

我正在尝试创建一个执行“语法突出显示”的编辑器,它相当简单:

yellow -> <span style="color:yellow">yellow</span>

我也使用<code contenteditable>html5 标签来替换<textarea>,并有颜色输出。

我从 angularjs 文档开始,并创建了以下简单指令。它确实有效,除了它不contenteditable使用生成的 html 更新区域。如果我使用 aelement.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:演示(输入yellowgreenblue进入更改我输入区域)

我试过了scope.$apply()ngModel.$render()但没有效果。我必须错过一些非常明显的东西......

我已经阅读的链接:

任何帮助深表感谢。请参阅上面的 plunker 演示。

4

1 回答 1

1

差不多一年后,我终于安顿下来了Codemirror,我从未如此快乐过。我正在使用实时更新进行并行降价源编辑(带有语法突出显示,因此甚至比 stackoverflow 的编辑页面更高级。)

我创建了一个简单的 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 源代码。

我强烈推荐codeMirror。它是一个坚如磐石的组件,适用于每种浏览器组合(firefox、firefox for android、chromium)。

于 2016-12-14T09:45:34.037 回答