Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Assign editable element in iframe #2255

Closed
VincentClair opened this issue Mar 29, 2017 · 9 comments
Closed

Assign editable element in iframe #2255

VincentClair opened this issue Mar 29, 2017 · 9 comments
Labels
package:editor-inline type:question This issue asks a question (how to...).

Comments

@VincentClair
Copy link

I have an application made with React. It includes inline version of ckeditor5 (the problem is the same with the classic version).
The application display html web pages with iframes. Those web pages contains editable elements set with ckeditor.

It seems that ckeditor does not identify the good document element to create buttons, as they are created in main document.

I have a JS error too in console: "Uncaught (in promise) Error: view-renderer-cannot-find-filler: Cannot find filler node by its position."

As a work around, i could inject ckeditor-inline in a standalone script in the iframed web page and use the script once loaded, to apply ckeditor to editable elements. Is there an already build version available ? Or should i generate it from my webpack script, something as a new entry like index.js, vendor.js and ckeditor-inline.js ? (Classic version will be used too in a "classical" way in forms).

There is another problem, and i could create a distinct ticket if needed, juste tell me: all elements content are encapsulated by a <P>, ie. if I apply ckeditor to a <h1>title</h1>, i will get <h1><p>title</p></h1>.

Thanks for your help

@oleq
Copy link
Member

oleq commented Mar 31, 2017

The application display html web pages with iframes. Those web pages contains editable elements set with ckeditor.

At this moment CKEditor 5 does not support instances inside another window scope (like iframe). We are aware of the problem but we don't have any ETA for the issue yet.

As a work around, i could inject ckeditor-inline in a standalone script in the iframed web page and use the script once loaded, to apply ckeditor to editable elements. Is there an already build version available ? Or should i generate it from my webpack script, something as a new entry like index.js, vendor.js and ckeditor-inline.js ?

CKEditor is under heavy (and I mean – really heavy) development and there's no stable build available. Even if we did provide some, things change so often that maintaining them would be pointless at this stage. You can do it the Webpack way though if you want to.

There is another problem, and i could create a distinct ticket if needed, juste tell me: all elements content are encapsulated by a...

ATM both ClassicEditor and InlineEditor assume that you're editing content in a div so it's not gonna work out of the box. We work on this to bring the editing experience to any element but, as I said before, there's no clear ETA at this moment.

@Reinmar
Copy link
Member

Reinmar commented Mar 31, 2017

There is another problem, and i could create a distinct ticket if needed, juste tell me: all elements content are encapsulated by a...

ATM both ClassicEditor and InlineEditor assume that you're editing content in a div so it's not gonna work out of the box. We work on this to bring the editing experience to any element but, as I said before, there's no clear ETA at this moment.

It's this ticket: https://github.com/ckeditor/ckeditor5-editor-inline/issues/5. It's a tricky thing – we haven't yet need to think about it, but we'll have to at some point. The problem for us is that we need to automate it somehow... make it generic enough to play well out of the box or with minimal configuration. But it's our problem.

@VincentClair, you can simply create a similar editor class to InlineEditor but which will create a different root element in the model. To be precise, you need to change this line:

https://github.com/ckeditor/ckeditor5-editor-inline/blob/fc99afc995f7b53298bc618c1356bf0d22ae9a86/src/inline.js#L32

Check out what this method does:

https://github.com/ckeditor/ckeditor5-engine/blob/d8ee5fa11b8aba4a186fec0ce770bdd308a2f451/src/model/document.js#L184-L193

You can skip the second argument so the root name will be 'main' (it's the name under which is stored in the Document#roots map, not the element name), but you need to pass a different elementName. The element name is then used to check in the Schema what type of content is allowed in it. These are the default rules which schema introduces:

https://github.com/ckeditor/ckeditor5-engine/blob/d8ee5fa11b8aba4a186fec0ce770bdd308a2f451/src/model/schema.js#L76-L90

So, since the InlineEditor class calls createRoot() it creates root with element name of '$root'. Element names starting with $ can be understood as generic types, so this is a generic root type. As you can see in the Schema, $block (another generic type – paragraphs and list items and headings inherit from the block) is allowed in $root and $text is allowed in $block. Just like in:

<div> // '$root'
    <p> // 'paragraph' which inherits from '$block'
        foo // '$text'
    </p>
</div>

So, if you want your root element to behave like a paragraph, you can do it like this:

document.createRoot( '$block' );

So the model will then create an element called <$block> which will be the root of your content.

PS. As @oleq mentioned, CKEditor is still under heavy development and, even though many things have stabilised, including most of the features, the API is still a subject of change. We focus first on features and stability, to learn as much as we need about the requirements (which turned many things upside down for us) and with that knowledge, we'll be refactoring some pieces of the API later on. E.g. Schema waits for a full rewrite: https://github.com/ckeditor/ckeditor5-engine/issues/532, and we also plan significant change in the model "entry points": https://github.com/ckeditor/ckeditor5-engine/issues/858.

@Reinmar
Copy link
Member

Reinmar commented Mar 31, 2017

You've also mentioned one more thing, guys – building the code.

This is totally doable. Olek was a bit imprecise. I'm right now reviewing the first build: ckeditor/ckeditor5-build-classic#3, so soon there will be ready-to-use code on npm for that. However, a build is a preconfigured editor. A final, concrete implementation. So it satisfies some use cases, but it's not meant to satisfy all.

The power of CKEditor 5 is that it's a framework. A set of packages which expose highly composable libraries. As I explained in my previous comment you'll need to change one or more things in the inline editor implementation proposed here, in this package. Of course, one day we'll support things that you need, but it's just easier to create your own editor class, tailored for your use case.

So, how to use the framework? It's exposed as a set of npm source packages and you can build it today into your app using Webpack and Rollup. For Rollup I don't have an example yet, but for Webpack we have.

Check:

If you use Webpack in your project you'll simply build CKEditor into your code base, just like other libraries. If you don't use Webpack, you need to first bundle CKEditor to UMD format, e.g. using an entry file like here: https://github.com/ckeditor/ckeditor5-build-classic/pull/3/files#diff-d5981cf1f2675e4edd4704b1a4608a86 and then include it in some other way to your app.

@Reinmar
Copy link
Member

Reinmar commented Mar 31, 2017

Finally, I come to reply to this:

I have an application made with React. It includes inline version of ckeditor5 (the problem is the same with the classic version).
The application display html web pages with iframes. Those web pages contains editable elements set with ckeditor.

It seems that ckeditor does not identify the good document element to create buttons, as they are created in main document.

I have a JS error too in console: "Uncaught (in promise) Error: view-renderer-cannot-find-filler: Cannot find filler node by its position."

TBH, I don't understand what you mean. We haven't yet tested editor with iframed content, but it was, of course, anticipated by us. So, there may happen that there's a problem with using the right documents in the engine, but we'd need a more concrete example to understand this.

Also, TBH, it's not really our goal yet to polish CKEditor 5 with iframes. It's must have for the future, that's for sure, but at the moment there are more important issues, so we may not be able to help much now.

@VincentClair
Copy link
Author

VincentClair commented Mar 31, 2017

Thank you both for such explications. I really appreciate that and your work in general.
I fully understand that CKEditor is still under heavy development. I just hope that my use cases for my future application and my feedbacks could help improve your product.

For elementName detection, I will try something similar to the following code. I will reply here it all works as expected.

    /**
     * Creates an instance of the inline editor.
     *
     * @param {HTMLElement} element The DOM element that will be the source for the created editor.
     * @param {Object} config The editor configuration.
     */
    constructor( element, config ) {
        super( element, config );

        // Something similar to $blockLimit in http://docs.ckeditor.com/source/dtd.html
        this.rootElements = ['a', 'article', 'aside', 'body', 'div', 'footer', 'heading', 'li', 'main', 'nav', 'section'];
        this.blockElements = ['h1', 'h2', 'h3', 'h4', 'h5', 'h6', 'p'];
        this.inlineElements = ['b', 'blockquote', 'code', 'em', 'i', 'pre', 'span', 'strong', 'sub', 'sup', 'u'];

        this.document.createRoot( this.getSchemaElementNameFromElement( element ) );
        this.data.processor = new HtmlDataProcessor();
        this.ui = new InlineEditorUI( this, new InlineEditorUIView( this.locale, element ) );
    }

    /**
     * Get schema element name from element.
     *
     * @param element
     * @returns {String}
     * @throws an exception if the DOM element name is not supported.
     */
    getSchemaElementNameFromElement(element) {
        if (this.blockElements.indexOf( element.nodeName )) {
            return '$block';
        } else if (this.rootElements.indexOf( element.nodeName )) {
            return '$root';
        } else if (this.inlineElements.indexOf( element.nodeName )) {
            return '$inline';
        }

        throw 'Element of type ' + element.nodeName + ' not supported.';
    }

For iFrame, not a big deal, i could apply my workaround, by including directly an entry point in the iframed web page. I need to just find the right way to build it with the others things/entry points in my app (I'm not fully mastered Webpack right now).

@VincentClair
Copy link
Author

I tried to compile Inline Editor by checkout ckeditor5-build-classic and made those changes:

  • In ckeditor.js, I replaced
    import ClassicEditorBase from '@ckeditor/ckeditor5-editor-classic/src/classic';
    by
    import InlineEditorBase from '@ckeditor/ckeditor5-editor-inline/src/inline';
  • In ckeditor.js and index.html, ClassicEditor by InlineEditor.
  • Adapt package.json to:
  "dependencies": {
    "@ckeditor/ckeditor5-autoformat": "*",
    "@ckeditor/ckeditor5-basic-styles": "*",
    "@ckeditor/ckeditor5-clipboard": "*",
    "@ckeditor/ckeditor5-core": "*",
    "@ckeditor/ckeditor5-editor-inline": "ckeditor/ckeditor5-editor-inline",
    "@ckeditor/ckeditor5-engine": "*",
    "@ckeditor/ckeditor5-enter": "*",
    "@ckeditor/ckeditor5-heading": "*",
    "@ckeditor/ckeditor5-image": "*",
    "@ckeditor/ckeditor5-link": "*",
    "@ckeditor/ckeditor5-list": "*",
    "@ckeditor/ckeditor5-paragraph": "*",
    "@ckeditor/ckeditor5-typing": "*",
    "@ckeditor/ckeditor5-ui": "ckeditor/ckeditor5-ui",
    "@ckeditor/ckeditor5-undo": "*"
  },

After deactivated Babiliplugin (as it killed webpack build), i have launched node_modules/.bin/webpack (otherwise, webpack is not found: not installed globally I think).

I could view index.html in browser and full content example is replaced by a BR element.
And if I tried to input some text, nothing happens. There is absolutely no errors in console, everything seems to be loaded.

I have no idea where to go to debug the problem (I have not applied yet my class to test other elements as root than div). Any idea ?

@Reinmar
Copy link
Member

Reinmar commented Apr 6, 2017

I'm not entirely sure, but what you may be missing is actually enabling these plugins in the editor. Installing packages is one step, enabling (in a specific editor instance) plugins provided by this packages is the second step. Nearly everything in the editor is a plugin, so if you don't enable them you get a readonly, blank element. Even the text may not appear if the paragraph feature is not enabled because the text is not allowed directly in the root element.

See e.g. manual tests:

https://github.com/ckeditor/ckeditor5-heading/blob/1c5675f3da28630600de658ef99795d8791ffac2/tests/manual/heading.js

import ClassicEditor from '@ckeditor/ckeditor5-editor-classic/src/classic';
import Enter from '@ckeditor/ckeditor5-enter/src/enter';
import Typing from '@ckeditor/ckeditor5-typing/src/typing';
import Heading from '../../src/heading';
import Paragraph from '@ckeditor/ckeditor5-paragraph/src/paragraph';
import Undo from '@ckeditor/ckeditor5-undo/src/undo';

ClassicEditor.create( document.querySelector( '#editor' ), {
	plugins: [ Enter, Typing, Undo, Heading, Paragraph ],
	toolbar: [ 'headings', 'undo', 'redo' ]
} )
.then( editor => {
	window.editor = editor;
} )
.catch( err => {
	console.error( err.stack );
} );

You can simplify the above code by using the Essentials preset instead of clipboard, enter, typing and undo directly. There's also the Article preset with most of our current features. So the shortest editor creation code may look like this:

import ClassicEditor from '@ckeditor/ckeditor5-editor-classic/src/classic';
import ArticlePreset from '@ckeditor/ckeditor5-presets/src/article';

ClassicEditor.create( document.querySelector( '#editor' ), {
	plugins: [ ArticlePreset ],
	toolbar: [ 'headings', 'undo', 'redo' ]
} )
.then( editor => {
	window.editor = editor;
} )
.catch( err => {
	console.error( err.stack );
} );

So, one of the options is to pass plugins to create(). The other option is to build them into the editor class just like the builds are doing. This means that the builds generated for such an entry file will load those built-in plugins automatically when you call create(). You can also then use config.removePlugins to disable some plugins which were built-in (you do this by plugin names – look for pluginName properties in plugin classes).

Which way to choose? If you're using Webpack to build your app, use the first method – import source modules directly, pass plugins to create() and you'll get the most optimised build. If you don't use Webpack and would like to prepare a bundle to be included in your app (via e.g. a script tag), then use the other way.

@Reinmar
Copy link
Member

Reinmar commented Apr 6, 2017

Warning: I've just noticed that bug in our release tool caused an invalid release of some the packages. If you install them via npm the build won't work ;/ There will be an error that some plugins are not plugins.

I'm going to fix the releases today, but make sure to reinstall entire node_modules/@ckeditor/.

@Reinmar
Copy link
Member

Reinmar commented Apr 6, 2017

OK, I released a new version of ckeditor5-presets and now there should be no version conflicts.

@Reinmar Reinmar closed this as completed Jul 22, 2019
@mlewand mlewand transferred this issue from ckeditor/ckeditor5-editor-inline Oct 8, 2019
@mlewand mlewand added resolution:solved type:question This issue asks a question (how to...). package:editor-inline labels Oct 8, 2019
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
package:editor-inline type:question This issue asks a question (how to...).
Projects
None yet
Development

No branches or pull requests

4 participants