CKEditor5 1.1.0-alpha
success to init editor
editor can not be inited, the error in the console:
Plugin cannot be invoked without 'new'
@Mgsy Can you confirm this issue, please?
Hi, @superkoh. Unfortunately, I'm not able to reproduce your issue. I followed Framework Quick Start guide from scratch and I've finished with fully initialized editor with the custom plugin.
Could you provide us more details about your case? I.e. what browser do you use, your node and npm version, do you try to implement custom plugin to existing editor or building everything from scratch?
Hi, @Mgsy , below is my react component
my node version is 8.4.0, npm version is 4.6.0
import React, { Component } from 'react';
import PropTypes from 'prop-types';
import ClassicEditor from '@ckeditor/ckeditor5-editor-classic/src/classiceditor';
import Essentials from '@ckeditor/ckeditor5-essentials/src/essentials';
import Paragraph from '@ckeditor/ckeditor5-paragraph/src/paragraph';
import Bold from '@ckeditor/ckeditor5-basic-styles/src/bold';
import Italic from '@ckeditor/ckeditor5-basic-styles/src/italic';
import Image from '@ckeditor/ckeditor5-image/src/image';
import ImageCaption from '@ckeditor/ckeditor5-image/src/imagecaption';
import Plugin from '@ckeditor/ckeditor5-core/src/plugin';
import ButtonView from '@ckeditor/ckeditor5-ui/src/button/buttonview';
import ModelElement from '@ckeditor/ckeditor5-engine/src/model/element';
import imageIcon from '@ckeditor/ckeditor5-core/theme/icons/image.svg';
class InsertImage extends Plugin {
init() {
const editor = this.editor;
editor.ui.componentFactory.add( 'insertImage', locale => {
const view = new ButtonView( locale );
view.set( {
label: 'Insert image',
icon: imageIcon,
tooltip: true
} );
view.on( 'execute', () => {
const imageUrl = prompt( 'Image URL' );
editor.document.enqueueChanges( () => {
const imageElement = new ModelElement( 'image', {
src: imageUrl
} );
editor.data.insertContent( imageElement, editor.document.selection );
} );
} );
return view;
} );
}
}
class RichEditor extends Component {
static propTypes = {
onChange: PropTypes.func,
initHtml: PropTypes.string,
};
componentDidMount() {
// ClassicEditor.build.plugins.map( (plugin) => { console.log(plugin.pluginName); } );
const { onChange } = this.props;
ClassicEditor
.create(this.editor, {
plugins: [Essentials, Paragraph, Bold, Italic, Image, InsertImage, ImageCaption],
toolbar: ['bold', 'italic', 'insertImage'],
})
.then((editor) => {
editor.document.on('change', () => {
if (onChange) {
onChange({}, editor.getData());
}
});
})
.catch((error) => {
console.error(error);
});
}
render() {
return (
<textarea
ref={(editor) => {
this.editor = editor;
}}
/>
);
}
}
export default RichEditor;
@Mgsy I use create-react-app to start a fresh project and follow the ckeditor5 framework quick start, it has the same error~~you can try it
Thanks for additional information. I am able to reproduce this issue. Here is full error:
index.js:2177 plugincollection-load: It was not possible to load the plugin. Read more: https://ckeditor5.github.io/docs/nightly/ckeditor5/latest/framework/guides/support/error-codes.html#plugincollection-load.
index.js:2177 TypeError: Class constructor Plugin cannot be invoked without 'new'
at new InsertImage (App.js:17)
at resolve (plugincollection.js:175)
at new Promise (<anonymous>)
at instantiatePlugin (plugincollection.js:149)
at loadPlugin (plugincollection.js:134)
at Array.map (<anonymous>)
at PluginCollection.load (plugincollection.js:121)
at loadPlugins (editor.js:148)
at ClassicEditor.initPlugins (editor.js:137)
at resolve (classiceditor.js:130)
at new Promise (<anonymous>)
at Function.create (classiceditor.js:126)
at RichEditor.componentDidMount (App.js:56)
at commitLifeCycles (react-dom.development.js:11505)
at commitAllLifeCycles (react-dom.development.js:12294)
at HTMLUnknownElement.callCallback (react-dom.development.js:1299)
at Object.invokeGuardedCallbackDev (react-dom.development.js:1338)
at invokeGuardedCallback (react-dom.development.js:1195)
at commitAllWork (react-dom.development.js:12415)
at workLoop (react-dom.development.js:12687)
at HTMLUnknownElement.callCallback (react-dom.development.js:1299)
at Object.invokeGuardedCallbackDev (react-dom.development.js:1338)
at invokeGuardedCallback (react-dom.development.js:1195)
at performWork (react-dom.development.js:12800)
at scheduleUpdateImpl (react-dom.development.js:13185)
at scheduleUpdate (react-dom.development.js:13124)
at scheduleTopLevelUpdate (react-dom.development.js:13395)
at Object.updateContainer (react-dom.development.js:13425)
at react-dom.development.js:17105
at Object.unbatchedUpdates (react-dom.development.js:13256)
at renderSubtreeIntoContainer (react-dom.development.js:17104)
at Object.render (react-dom.development.js:17129)
at Object../src/index.js (index.js:7)
at __webpack_require__ (bootstrap d43d2ee54696c17718eb:678)
at fn (bootstrap d43d2ee54696c17718eb:88)
at Object.0 (registerServiceWorker.js:108)
at __webpack_require__ (bootstrap d43d2ee54696c17718eb:678)
at bootstrap d43d2ee54696c17718eb:724
at bundle.js:728
As code from Quick Start guide works correctly, this issue is related to React integration. It might be caused by way how create-react-app builds whole application. What do you think about it @Reinmar?
It seems to be native class inherit problem with babel. If I don't extends Plugin, but add construct my self, it will work.
I'm not familiar with webpack or babel config, so may be you can give some solution when integrate with react especially with officially create-react-app
I've made some research and indeed, this is a problem related to Babel and classes inheritance.
Temporary solution could be excluding InsertImage Plugin from the entry file and putting it to node_modules (note that you won't be able to use this plugin after rm -rf node_modules && npm i). So you can create following directory inside node_modules:
|- @ckeditor
|- ckeditor5-insert-image
|- src
|- insertimage.js
Just put the InsertImage code inside the insertimage.js. Plugin will be imported in the main app file, so remember to export your class. Your code should be something like this:
import Plugin from '@ckeditor/ckeditor5-core/src/plugin';
import ButtonView from '@ckeditor/ckeditor5-ui/src/button/buttonview';
import ModelElement from '@ckeditor/ckeditor5-engine/src/model/element';
import imageIcon from '@ckeditor/ckeditor5-core/theme/icons/image.svg';
export default class InsertImage extends Plugin {
// your code
}
Right now you can import your plugin inside the main file. Just add the import at the beginning of the file:
import InsertImage from '@ckeditor/ckeditor5-insert-image/insertimage';
InsertImage should also be included to Plugins array and the toolbar configuration.
There is one more thing you have to do. Go to the following directory inside node_modules:
|- react-scripts
|- configs
|- webpack.dev.config.js
Like Quick start guide says, you have to add loaders for icons and for theme. Inside webpack.dev.config.js scroll to css loaders and add following rules:
{
// Or /ckeditor5-[^/]+\/theme\/icons\/[^/]+\.svg$/ if you want to limit this loader
// to CKEditor 5's icons only.
test: /\.svg$/,
use: [ 'raw-loader' ]
},
{
// Or /ckeditor5-[^/]+\/theme\/[^/]+\.scss$/ if you want to limit this loader
// to CKEditor 5's theme only.
test: /\.scss$/,
use: [
'style-loader',
'css-loader',
'sass-loader'
]
},
/* other loaders go here */
Now application is ready to build and run.
This solution works for me, guys if you see any mistakes please let me know.
@Mgsy, your solution works, but isn't portable as all changes inside the node_modules can't be tracked.
I'd go with a little bit different approach.
The above error exists because the configuration of the babel-loader makes babel transpiling only the files in src directory. So you end up with the compiled class that inherits not-compiled one.
npm run eject. This will export all react scripts and configs to the files inside the project. Note, that this can't be undone.Edit config/wbepack.config.dev.js if you want to try it locally. Don't forget to edit config/wbepack.config.prod.js later.
You can include both by changing the include ~L145:
include: [
paths.appSrc,
path.resolve( __dirname, '../node_modules/@ckeditor/' )
],
Or exclude both by moving the custom editor-related stuff to the src/editor path and exclude it from the babel transformations by adding exclude: path.resolve( __dirname, '../src/editor/' ),. Note, that with this approach Editor won't be ES5-compatible and will work only on the modern browsers.
{
test: /\.scss$/,
use: [
require.resolve( 'style-loader' ),
require.resolve( 'css-loader' ),
require.resolve( 'sass-loader' )
]
},
Add raw-loader for SVGs as @Mgsy suggested.
Install all loaders and node-sass locally and run npm start.
Thanks for @Mgsy and @ma2ciek .
My solution is to not extends Plugin, but implement construct and destroy my self. It seems ok.
But it is a compromise.
Note that plugins do not need to extend Plugin class, see: https://github.com/ckeditor/ckeditor5-core/issues/78. But the bug is strange, indeed.
Note that plugins do not need to extend Plugin class
Sure, but you'll end up with parts of the code in ES6 and some later possible errors when joining transpiled and not transpiled code.
Maybe we could provide another build (as many libs do) alongside the main build? E.g. import Plugin from '@ckeditor/ckeditor5-core/es5/plugin'? Providing such a build might be a part of releasing tool.
Hi there, trying to reproduce the tutorial as well.. in my case i'm using angular and trying to include the import imageIcon from '@ckeditor/ckeditor5-core/theme/icons/image.svg'; but says that does not exists .
Core is installed because this line works import Plugin from '@ckeditor/ckeditor5-core/src/plugin'; .. kind lost..
If i remove the extends Plugin it runs.. but says that this.editor is undefines
Other thing is, with typescript we need to create the public editor in the InsertImage class. I didnt get how this editor is injected by ckeditor.
I'm closing this due to lack of activity. If there are any problems still, please report a new, more detailed ticket.
Also, I should mention that we're working on a CKEditor 5 React component: https://github.com/ckeditor/ckeditor5-react
Hi, bug seems confirmed (probably not a bug, but lack of docs about this): I tried publishing a plugin for inserting tokens (placeholder) into the editor. Was about to ask you a couple of things about it in another issue, tried to settle a fiddle, but https://codesandbox.io/s/4wj9zz3ppw here's the result. My plugin code is transpiled with babel, but something is wrong, and can't find info on how to fix this
Thanks
Andrea
Hi, i have the same bug.
Thanks to @superkoh, this solution works.
But i think this should be fixed or at least covered by docs.
Same problem, it should definitely be fixed or added to the documentation.
Discovered that previously provided solution is not working when you try to register command/button for custom plugin.
My final workaround was to create Plugin class in your project and extend it in your custom plugin classes.
import ObservableMixin from '@ckeditor/ckeditor5-utils/src/observablemixin';
import mix from '@ckeditor/ckeditor5-utils/src/mix';
export class Plugin {
editor: Object;
constructor(editor: Object) {
this.editor = editor;
}
destroy() {
}
}
mix(Plugin, ObservableMixin);
Also if you need to create custom command for your plugin you will have to create Command class in your project and to extend it in your custom command:
import ObservableMixin from '@ckeditor/ckeditor5-utils/src/observablemixin';
import mix from '@ckeditor/ckeditor5-utils/src/mix';
export default class Command {
constructor( editor ) {
this.editor = editor;
this.set( 'value', undefined );
this.set( 'isEnabled', false );
this._disableStack = new Set();
this.decorate( 'execute' );
this.listenTo( this.editor.model.document, 'change', () => {
this.refresh();
} );
this.on( 'execute', evt => {
if ( !this.isEnabled ) {
evt.stop();
}
}, { priority: 'high' } );
this.listenTo( editor, 'change:isReadOnly', ( evt, name, value ) => {
if ( value ) {
this.forceDisabled( 'readOnlyMode' );
} else {
this.clearForceDisabled( 'readOnlyMode' );
}
} );
}
refresh() {
this.isEnabled = true;
}
forceDisabled( id ) {
this._disableStack.add( id );
if ( this._disableStack.size == 1 ) {
this.on( 'set:isEnabled', forceDisable, { priority: 'highest' } );
this.isEnabled = false;
}
}
clearForceDisabled( id ) {
this._disableStack.delete( id );
if ( this._disableStack.size == 0 ) {
this.off( 'set:isEnabled', forceDisable );
this.refresh();
}
}
execute() {}
destroy() {
this.stopListening();
}
}
mix( Command, ObservableMixin );
function forceDisable( evt ) {
evt.return = false;
evt.stop();
}
Thanks guys for all the comments. Since this is a recurring thing, I'm reopening this ticket.
We'll take a look at it again. But from a quick look at this discussion, I think that @ma2ciek's proposals are the only real solutions. Combining transpiled and non-transpiled code together won't work. You either need to exclude all code that uses CKE5's libs from transpilation. Or the opposite โ transpile everything.
The other thing is that this touches https://github.com/ckeditor/ckeditor5/issues/667. It may happen that the solution to #667 will also resolve this issue.
Most helpful comment
Thanks for @Mgsy and @ma2ciek .
My solution is to not extends Plugin, but implement construct and destroy my self. It seems ok.
But it is a compromise.