VueJS和tinyMCE,自定义指令

问题描述:

我一直在努力让VueJS和TinyMCE一起工作。我得出的结论是,使用指令将是最佳选择。

I've been struggling hard with getting VueJS and TinyMCE to work together. I've come to the conclusion that using directives would be the way to go.

到目前为止,我已经能够将主体作为指令参数传入,并且tinyMCE设置内容。但是,我不能让双向绑定工作。基于tinyMCE api,我也害怕我做错了。

So far I've been able to pass in the body as a directive parameter, and tinyMCE sets the content. However, I can't get the two way binding to work. I'm also afraid that I'm doing things completely wrong based on the tinyMCE api.

我认为相关的tinyMCE函数将是:

The relevant tinyMCE functions I assume would be:

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




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



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'
    }
})



小提琴



https://jsfiddle.net/nf3ftm8f/

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

With Vue.js 2.0, the directives are only used for applying low-level direct DOM manipulations. They don't have this reference to Vue instance data anymore. (Ref: https://vuejs.org/v2/guide/migration.html#Custom-Directives-simplified)

因此我建议改用 Component 。 / p>

TinymceComponent:



Hence I recommend to use Component instead.

// 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实例(简化)



Vue Instance (simplified)

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视图)



HTML (MVC view)

<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>



流量



The flows


  1. 首先,数据通过远程资源加载,即AJAX。已设置说明

  2. 说明传递给组件通过 props:initValue

  3. 安装组件时, tinymce 使用初始描述进行初始化。

  4. 它还会在模糊事件上设置以获取更新的内容。

  5. 每当用户失去对编辑器的关注时,就会捕获一个新内容,并且该组件会发出一个事件 content-updated ,让父母知道某些事情已经发生了发生了。

  6. 在Html上你有 v-on:content-updated 。由于父级正在侦听内容更新事件,因此在事件发生时将调用父方法 updateDescription

  1. First the data is loaded through remote resources, i.e., AJAX. The description got set.
  2. The description got passed down to the component via props: initValue.
  3. When the component is mounted, the tinymce is initialized with the initial description.
  4. It also sets up the on blur event to get the updated content.
  5. Whenever the user loses focus on the editor, a new content is captured and the component emits an event content-updated, letting the parent know that something has happened.
  6. On Html you have v-on:content-updated. Since the parent is listening to the content-updated event, the parent method updateDescription will be called when the event is emited.



!!情侣重要提示!!




  • 按照设计,组件具有从父到组件的单向绑定。因此,当从Vue实例更新 description 时,组件的 initValue 属性也应该自动更新。

  • 如果我们可以将 tinymce 编辑器中的任何用户类型传递回父Vue实例,那将是很好的但是不应该有2种方式绑定。那时你需要使用 $ emit 来启动事件并通过组件通知父母。

  • 你不必定义一个父项中的函数并执行 v-on:content-updated =updateDescription。您可以通过 v-on:content-updated =description = $ event直接更新数据。 $ event 具有为组件内部函数定义的参数 - newContent 参数。

  • !!Couple Important Notes!!

    • By design, the component has 1 way binding, from parent to component. So when the description gets updated from Vue instance, the component's initValue property should be updated as well, automatically.
    • It would be nice if we can pass whatever the user types in tinymce editor back to the parent Vue instance but 2 ways bindings is not supposed. That's when you need to use $emit to fire up events and notify parents from components.
    • You don't have to define a function in parent and do v-on:content-updated="updateDescription". You can just directly update the data by doing v-on:content-updated="description = $event". The $event has the parameter you defined for the function inside the component - the newContent parameter.
    • 希望我能清楚地解释一下。这整件事花了我两个星期的时间来弄明白!!

      Hope I explained things clearly. This whole thing took me 2 weeks to figure it out!!