VueJS 和 tinyMCE,自定义指令

VueJS and tinyMCE, custom directives

我一直在努力让 VueJS 和 TinyMCE 一起工作。我得出的结论是使用指令是可行的方法。

到目前为止,我已经能够将主体作为指令参数传递,tinyMCE 设置内容。但是,我无法使两种方式绑定工作。我也担心我基于 tinyMCE api.

做的事情完全错误

我认为相关的 tinyMCE 函数是:

http://community.tinymce.com/wiki.php/api4:method.tinymce.Editor.setContent

// Sets the content of a specific editor (my_editor in this example)
tinymce.get('my_editor').setContent(data);

http://community.tinymce.com/wiki.php/api4:method.tinymce.Editor.getContent

// Get content of a specific editor:
tinymce.get('content id').getContent()

HTML

<div id="app">
  <h3>This is the tinyMCE editor</h3>
  <textarea id="editor" v-editor :body="body"></textarea>

  <hr>
  <p>This input field is properly binded</p>
  <input v-model="body">

  <hr>
  <pre>data binding: {{ body }} </pre>
</div>

JS

tinymce.init({
    selector:'#editor',
});

Vue.directive('editor', {
    twoWay: true,
    params: ['body'],

    bind: function () {
        tinyMCE.get('editor').setContent(this.params.body);
        tinyMCE.get('editor').on('change', function(e) {
            alert("changed");
        });
    },
    update: function (value) {
        $(this.el).val(value).trigger('change')
    },
});

var editor = new Vue({
    el: '#app',
    data: {
        body: 'The message'
    }
})

Fiddle

https://jsfiddle.net/nf3ftm8f/

试试这个:

Vue.directive('editor', {
  twoWay: true,
  params: ['body'],

  bind: function () {
    tinyMCE.get('editor').setContent(this.params.body);
    var that = this;
    tinyMCE.get('editor').on('change', function(e) {
      that.vm.body = this.getContent();
    });
  }
});

诀窍是将指令存储在临时变量 "that" 中,以便您可以从更改事件回调中访问它。

在 Vue.js 2.0 中,指令仅用于应用 low-level 直接 DOM 操作。他们不再有 this 对 Vue 实例数据的引用。 (参考:https://vuejs.org/v2/guide/migration.html#Custom-Directives-simplified

因此我建议改用Component

TinymceComponent:

// Use JSPM to load dependencies: vue.js 2.1.4, tinymce: 4.5.0
import Vue from 'vue/dist/vue';
import tinymce from 'tinymce';

// Local component
var TinymceComponent = {
    template: `<textarea class="form-control">{{ initValue }}</textarea>`,
    props: [ 'initValue', 'disabled' ],
    mounted: function() {
        var vm = this,
            tinymceDict = '/lib/jspm_packages/github/tinymce/tinymce-dist@4.5.1/';

        // Init tinymce
        tinymce.init({
            selector: '#' + vm.$el.id,
            menubar: false,
            toolbar: 'bold italic underline | bullist numlist',
            theme_url: tinymceDict + 'themes/modern/theme.js,
            skin_url: tinymceDict + 'skins/lightgray',
            setup: function(editor) {
                // If the Vue model is disabled, we want to set the Tinymce readonly
                editor.settings.readonly = vm.disabled;

                if (!vm.disabled) {
                    editor.on('blur', function() {
                        var newContent = editor.getContent();

                        // Fire an event to let its parent know
                        vm.$emit('content-updated', newContent);
                    });
                }
            }
        });
    },
    updated: function() {
        // Since we're using Ajax to load data, hence we have to use this hook because when parent's data got loaded, it will fire this hook.
        // Depends on your use case, you might not need this
        var vm = this;

        if (vm.initValue) {
            var editor = tinymce.get(vm.$el.id);
            editor.setContent(vm.initValue);
        }
    }
};

// Vue instance
new Vue({
    ......
    components: {
        'tinymce': TinymceComponent
    }
    ......
});

Vue 实例(简体)

new Vue({
    el: '#some-id',
    data: {
        ......
        description: null
        ......
    },
    components: {
        'tinymce': TinymceComponent
    },
    methods: {
        ......
        updateDescription: function(newContent) {
            this.description = newContent;
        },
        load: function() {
            ......
            this.description = "Oh yeah";
            ......
        }
        ......
    },
    mounted: function() {
        this.load();
    }
});

HTML(MVC 视图)

<form id="some-id">
    ......
    <div class="form-group">
        <tinymce :init-value="description"
                 v-on:content-updated="updateDescription"
                 :id="description-tinymce"
                 :disabled="false">
        </tinymce>
    </div>
    ......
</form>

流量

  1. 首先通过远程资源加载数据,即AJAX。 description 已设置。
  2. description 通过 props: initValue 传递给组件。
  3. 安装组件时,tinymce 使用初始描述进行初始化。
  4. 它还设置了 on blur 事件来获取更新的内容。
  5. 只要用户在编辑器上失去焦点,就会捕获新内容并且组件会发出事件 content-updated,让 parent 知道发生了什么事。
  6. 在 Html 你有 v-on:content-updated。由于 parent 正在监听 content-updated 事件,因此 parent 方法 updateDescription 将在事件发出时被调用。

!!重要说明!!

  • 根据设计,组件有 1 种绑定方式,从 parent 到组件。因此,当 description 从 Vue 实例更新时,组件的 initValue 属性 也应该自动更新。
  • 如果我们可以将用户在 tinymce 编辑器中键入的任何内容传递回 parent Vue 实例,那就太好了,但是不应该有 2 种绑定方式。那时您需要使用 $emit 来启动事件并从组件通知 parents。
  • 您不必在 parent 中定义函数并执行 v-on:content-updated="updateDescription"。您可以通过 v-on:content-updated="description = $event" 直接更新数据。 $event 具有您为组件内的函数定义的参数 - newContent 参数。

希望我解释清楚了。这整件事花了我 2 周的时间才弄明白!!

这是 Vue 的 Tinymce 组件。 http://jsbin.com/pucubol/edit?html,js,output

了解 v-model 和自定义输入组件也很好: http://vuejs.org/v2/guide/components.html#Form-Input-Components-using-Custom-Events

Vue.component('tinymce', {
 props: ['value'],
 template: `<div><textarea rows="10" v-bind:value="value"></textarea></div>`,
 methods: {
  updateValue: function (value) {
          console.log(value);
   this.$emit('input', value.trim());
  }
 },
    mounted: function(){
      var component = this;
      tinymce.init({ 
        target: this.$el.children[0],
        setup: function (editor) {
          editor.on('Change', function (e) {
            component.updateValue(editor.getContent());
          })
        }
      });
    }
});
<tinymce v-model="whatever"></tinymce>

现在有一个 npm package,它是 TinyMCE 的薄包装,使其更容易在 Vue 应用程序中使用。

它是开源的,代码在 GitHub

安装:

$ npm install @tinymce/tinymce-vue

用法:

 import Editor from '@tinymce/tinyme-vue';

模板:

<editor api-key="API_KEY" :init="{plugins: 'wordcount'}"></editor>

其中 API_KEY 是来自 tiny. The init section is the same as the default init statement except you do not need the selector. For an example see the documentation 的 API 密钥。