tinymce-vue icon indicating copy to clipboard operation
tinymce-vue copied to clipboard

Cannot read property 'init' of null

Open ligne13 opened this issue 6 years ago • 10 comments

Hi, I've imported the Editor in my app and registered it as a global component :

import Editor from '@tinymce/tinymce-vue';
Vue.component('tinymce', Editor);

Then I have :

<tinymce inline v-model="content">
    Hello !
</tinymce>

But Chrome display the error below :

Editor.js?f88d:46 Uncaught TypeError: Cannot read property 'init' of null at eval (Editor.js?f88d:46) at eval (ScriptLoader.js?0fea:34) at Array.forEach () at HTMLScriptElement.eval (ScriptLoader.js?0fea:34)

I'm sure I missed something, but what ?

thanks !!

ligne13 avatar Jan 14 '19 09:01 ligne13

I can't reproduce this in my testing, can you share some more code on how you are using the component? One thing I see in your code though is that you shouldn't have any children inside of the editor component, you should add content via the v-model or initialContent.

fyrkant avatar Jan 14 '19 11:01 fyrkant

Thanks @fyrkant. I think this is because I am loading the editor in a component that is loaded dynamically in a loop, whose syntax is :

<div v-for="(item, index) in structure" :key="index">
    <component :is="item.component"></component>
 </tr>

where item.component is the name of the component I want to load. The code of one of this possible dynamic component is :

<template>
    <!-- section wrap START -->
    <td align="center" valign="top">
        <tinymce inline v-model="content"></tinymce>
    </td>
    <!-- section wrap END -->
</template>

<script>
  import Editor from '@tinymce/tinymce-vue';

  export default {
    components: {
      'tinymce': Editor,
    },
    data () {
      return {
        content: '<p>Hello</p>',
      };
    },
  };
</script>

It seems the editor is not initialized correctly in this pattern of loading components dynamically. What do you think ?

ligne13 avatar Jan 14 '19 13:01 ligne13

Yes that is probably why it's not working, but I don't really have any idea on how to fix that I'm afraid. What do you gain from loading components in a loop like that?

fyrkant avatar Jan 14 '19 13:01 fyrkant

Finally I found that it's because I load the component in a parent component which renders to an

TestPage.vue (component loaded by the main Vue App on route /test):

<template>
    <i-frame>
        <div v-for="index in [0,1,2]" :key="index">
            <component :is="type"></component>
        </div>
    </i-frame>
</template>

<script>
  import Vue from 'vue';
  import Editor from '@tinymce/tinymce-vue';
  import Section from '@/components/template/Section';

  Vue.component('i-frame', {
    render (createElement) {
      return createElement('iframe', {
        on: {
          load: this.renderChildren,
        }
      });
    },
    beforeUpdate () {
      // freezing to prevent unnecessary reactivity of vNodes
      this.previewApp.children = Object.freeze(this.$slots.default);
    },
    methods: {
      renderChildren () {
        const children = this.$slots.default;
        const body = this.$el.contentDocument.body;
        const el = document.createElement('div');
        body.appendChild(el);

        const previewApp = new Vue({
          name: 'previewApp',
          data: {
            // freezing to prevent unnecessary reactivity of vNodes
            children: Object.freeze(children),
          },
          render (createElement) {
            return createElement('div', this.children);
          },
        });
        previewApp.$mount(el);
        this.previewApp = previewApp;
      },
    }
  });

  export default {
    data () {
      return {
        type: 'mysection',
      };
    },
    components: {
      'mysection': Section,
    }
  };
</script>

Section.vue :

<template>
    <div>
        <tinymce v-model="content"></tinymce>
    </div>
</template>

<script>
  export default {
    data () {
      return {
        content: '<p>Hello</p>',
      };
    },
  };
</script>

I have tried to add the Editor as a component of the iframe Vue App but it does not work, by adding :

components: {
  'tinymce': Editor,
},

below Vue.component('i-frame', { but it still does not work, same error.

ligne13 avatar Jan 14 '19 13:01 ligne13

I don't know enough about how Vue works to be able to help you, I'm afraid. I'll set a help wanted tag on this issue and maybe someone else can help.

fyrkant avatar Jan 14 '19 14:01 fyrkant

Thanks. I keep searching... I'll post the solution when I find one... hopefully.

ligne13 avatar Jan 14 '19 14:01 ligne13

I have created a codepen : https://codepen.io/ligne13/pen/PXVoKx?editors=1010 The error does not always show up in the Console (probably because of the codepen environment) but you can see that the editor is not loaded inside the iframe.

ligne13 avatar Jan 14 '19 15:01 ligne13

I think I've understood what the problem is now, will try to push out a fix tomorrow.

fyrkant avatar Jan 16 '19 14:01 fyrkant

Thank you @fyrkant. In the mean time, I've asked the tiny team about this and here is what they answered :

THEM : For our Cloud platform to work the request for the editor code needs to have the Referer header included. When you create an iframe with no src attribute the browser does not appear to send a Referer header so our platform does not deliver the editor code as we cannot confirm that the domain is valid for the API key. ME: OK. So, in order to work within an iframe, should I use a self-hosted distribution ? (import via npm). THEM : That would resolve the issue you are seeing - yes. Our Cloud platform uses domains to ensure that your key is used when and where you want it to be used so any scenario where the domain cannot be determined will lead to issues with the Cloud platform.

But I'm not sure this is the real problem. Because I can see in the console that the tinymce script is loaded correctly in the window.

If you can find what is causing the issue, with the help of my codepen, that would be great !

ligne13 avatar Jan 16 '19 15:01 ligne13

As far as I can understand the problem is that the tinymce global is not available in the inner iframe due to how the component loads the tinymce script in the page. To fix this was more complicated then first expected so I'm sorry but I don't have the time to do it now, seeing how you are using the component is very much an edge case. I'll leave this issue open if somebody else want's to take a stab at it.

fyrkant avatar Jan 17 '19 10:01 fyrkant