在AngularJS指令中集成Redactor所见即所得编辑器

10

我试图将美观的所见即所得编辑器Redactor (http://imperavi.com/redactor/) 集成到一个自定义的AngularJS指令中。

在视觉上它能够工作,但是我的自定义指令无法与ng-model兼容(我不知道为什么)。

以下是您可以使用我的指令的方式:

<wysiwyg ng-model="edited.comment" id="contactEditCom" content="{{content}}" required></wysiwyg>

这是指令代码:

var myApp = angular.module('myApp', []);
myApp.directive("wysiwyg", function(){

var linkFn = function(scope, el, attr, ngModel) {

    scope.redactor = null;

    scope.$watch('content', function(val) {
        if (val !== "")
        {
            scope.redactor = $("#" + attr.id).redactor({
                focus : false,
                callback: function(o) {
                    o.setCode(val);
                    $("#" + attr.id).keydown(function(){
                        scope.$apply(read);
                    });
                }
            });
        }
    });

    function read() {
        var content = scope.redactor.getCode();
        console.log(content);
        if (ngModel.viewValue != content)
        {
            ngModel.$setViewValue(content);
            console.log(ngModel);
        }
    }

};

 return {
     require: 'ngModel',
     link: linkFn,
     restrict: 'E',
     scope: {
         content: '@'
     },
     transclude: true
 };
});

最后,这是 jsfiddle -> http://fiddle.jshell.net/MyBoon/STLW5/


在read()函数中,ngModel.viewValue应该改为ngModel.$viewValue。 "我的自定义指令与ng-model不兼容"是什么意思? - Mark Rajcok
我想要的是我的自定义指令能像其他输入框一样工作。例如,当你写下这个: <input type="text" ng-model="edited.input" /> 如果输入了文本,那么作用域变量'edited.input'将被填充。我试图让我的指令有相同的行为。 - MyBoon
5个回答

4

更新 --- Rails 4.2 --- Angular-Rails 1.3.14

大家好,经过大量研究和其他成员在stackoverflow上的帮助,这里有一个解决方案,它可以直接传递到控制器$scope中并应用到你应用于文本区域的ng-model中:

**呈现原始HTML**

# Filter for raw HTML
app.filter "unsafe", ['$sce', ($sce) ->
    (htmlCode) ->
        $sce.trustAsHtml htmlCode
]

Credit for Filter

指令:

# For Redactor WYSIWYG
app.directive "redactor", ->
require: "?ngModel"
link: ($scope, elem, attrs, controller) ->
    controller.$render = ->
        elem.redactor
            changeCallback: (value) ->
                $scope.$apply controller.$setViewValue value
            buttons: ['html', '|', 'formatting', '|',
                'fontcolor', 'backcolor', '|', 'image', 'video', '|',
                'alignleft', 'aligncenter', 'alignright', 'justify', '|',
                'bold', 'italic', 'deleted', 'underline', '|',
                'unorderedlist', 'orderedlist', 'outdent', 'indent', '|',
                'table', 'link', 'horizontalrule', '|']
            imageUpload: '/modules/imageUpload'
        elem.redactor 'insert.set', controller.$viewValue

最后一行更新原因

在HTML视图中:

<div ng-controller="PostCtrl">  
    <form ng-submit="addPost()">
        <textarea ng-model="newPost.content" redactor required></textarea>
        <br />
        <input type="submit" value="add post">
    </form>

    {{newPost.content}} <!-- This outputs the raw html with tags -->
    <br />
    <div ng-bind-html="newPost.content | unsafe"></div> <!-- This outputs the html -->
</div>

控制器:

$scope.addPost = ->     
    post = Post.save($scope.newPost)
    console.log post
    $scope.posts.unshift post
    $scope.newPost.content = "<p>Add a new post...</p>"

为了避免TypeError错误,使用Redactor之前需要在调用操作之前将一个值填充到文本区域中,这对我来说是最好的方法,以保留格式。
# Set the values of Reactor to prevent error
    $scope.newPost = {content: '<p>Add a new post...</p>'}

如果您遇到CSRF错误,这将解决该问题:

如果您遇到CSRF错误,可以执行以下操作:

# Fixes CSRF Error OR: https://github.com/xrd/ng-rails-csrf
app.config ["$httpProvider", (provider) ->
    provider.defaults.headers.common['X-CSRF-Token'] = angular.element('meta[name=csrf-token]').attr('content')

非常感谢:AngularJS & Redactor插件

最后....

如果您正在使用ng-repeat创建这些redactor文本区域,并且无法访问作用域,请查看此答案:在ng-repeat中访问模型


1
现在您可以使用新的“changeCallback”函数。不再使用“execCommandCallback”。参见:changeCallback: function(html) {} - benske

4

我基于Angular-UI的TinyMCE指令创建了一个。这个指令还能监听格式按钮的点击事件。它也处理了模型在指令之外被更改的情况。

directive.coffee (抱歉使用了coffeescript)

angular.module("ui.directives").directive "uiRedactor", ["ui.config", (uiConfig) ->

  require: "ngModel"
  link: (scope, elm, attrs, ngModelCtrl) ->
    redactor = null

    getVal = -> redactor?.getCode()

    apply = ->
      ngModelCtrl.$pristine = false
      scope.$apply()

    options =
      execCommandCallback: apply
      keydownCallback: apply
      keyupCallback: apply

    scope.$watch getVal, (newVal) ->
      ngModelCtrl.$setViewValue newVal unless ngModelCtrl.$pristine


    #watch external model change
    ngModelCtrl.$render = ->
      redactor?.setCode(ngModelCtrl.$viewValue or '')

    expression = if attrs.uiRedactor then scope.$eval(attrs.uiRedactor) else {}

    angular.extend options, expression

    setTimeout ->
      redactor = elm.redactor options
]  

html

<textarea ui-redactor='{minHeight: 500}' ng-model='content'></textarea>

@MichaelCalkins,这个脚本还存在吗?它在链接的位置上找不到了。 - Rhys van der Waerden
@RhysvanderWaerden 可以在谷歌上搜索或尝试 https://github.com/TylerGarlick/angular-redactor。 - Michael J. Calkins

1

看看这个fiddle是否符合您的要求。

ng-model可以设置为内容:

<wysiwyg ng-model="content" required></wysiwyg>

在链接函数中,el已经设置为指令定义的元素,因此不需要id。而且el已经是一个包装过的jQuery元素。
链接函数:
var linkFn = function (scope, el, attr, ngModel) {
    scope.redactor = el.redactor({
        focus: false,
        callback: function (o) {
            o.setCode(scope.content);
            el.keydown(function () {
                console.log(o.getCode());
                scope.$apply(ngModel.$setViewValue(o.getCode()));

注意,不要将其作为元素,因为这会破坏Redactor的CSS。将其作为属性限制:'A'。 - Michael J. Calkins

1

我的解决方案是

1)克隆https://github.com/dybskiy/redactor-js.git 2)包含jquery,redactor.js,redactor.css 3)在您的html主体中添加标签:<textarea wysiwyg ng-model="post.content" cols="18" required></textarea> 4)添加指令:

yourapp.directive('wysiwyg', function () {
  return {
    require: 'ngModel',
    link: function (scope, el, attrs, ngModel) {
      el.redactor({
        keyupCallback: function(obj, e) {
            scope.$apply(ngModel.$setViewValue(obj.getCode()));
        }
      });
      el.setCode(scope.content);
    }
  };
});

最好的问候,Jeliuc Alexandr


1
上述解决方案对于我并非适用于所有情况,因此我使用它们创建了以下指令,可以保持模型和redactor同步。
angular.module('redactor', [])

.指令('redactor', function () { 返回 { 需要: '?ngModel', 链接: function (范围,el,attrs,ngModel) {

  // Function to update model
  var updateModel = function() {
        scope.$apply(ngModel.$setViewValue(el.getCode()));
    };

  // Get the redactor element and call update model
  el.redactor({
    keyupCallback: updateModel,
    keydownCallback: updateModel,
    execCommandCallback: updateModel,
    autosaveCallback: updateModel
  });

  // Call to sync the redactor content
          ngModel.$render = function(value) {
        el.setCode(ngModel.$viewValue);
    };
  }
};

只需将redactor模块添加为依赖项,并在您的html中添加以下内容:

});

注意:我在升级到9.1.1版本后不得不更新代码

以下是新版本:

    .directive('redactor', function () {
        return {
          require: '?ngModel',
          link: function (scope, el, attrs, ngModel) {

            var updateModel, errorHandling;
            // Function to update model
            updateModel = function() {
              if(!scope.$$phase) {
                  scope.$apply(ngModel.$setViewValue(el.redactor('get')));
                }
              };

            uploadErrorHandling = function(response)
              {
                console.log(response.error);
                alert("Error: "+ response.error);
              }; 

            // Get the redactor element and call update model
            el.redactor({
              minHeight: 100,
              buttons: ['formatting', '|', 'bold', 'italic', 'deleted', '|',
              'unorderedlist', 'orderedlist', 'outdent', 'indent', '|',
              'image', 'video', 'file', 'table', 'link', '|', 'alignment', '|', 'horizontalrule'],
              keyupCallback: updateModel,
              keydownCallback: updateModel,
              changeCallback: updateModel,
              execCommandCallback: updateModel,
              autosaveCallback: updateModel,
              imageUpload: '/file/upload/image',
              imageUploadErrorCallback: uploadErrorHandling,
              imageGetJson: '/api/v1/gallery'
              });

            // Call to sync the redactor content
              ngModel.$render = function(value) {
                  el.redactor('set', ngModel.$viewValue);
              };
          }
        };
      });

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接