ckeditor5-angular
ckeditor5-angular copied to clipboard
How to build editor from source?
Since the ng eject
task for version 6 still isn't available, it's a more complicated task. Fortunately, the new CLI comes with builders, so we can use some predefined builder, which can have different build tasks and i.e. it can use different webpack config. Here comes the angular-builders package, which can append, replace or prepend specific part of webpack config to the angular's default one - see art without ejecting.
I gave it a try and the result was pretty good:
First, install additional dev dependencies (webpack loaders, CKEditor5 theme, CKEditor5 helpers and webpack builders):
npm i -D \
css-loader \
html-loader \
raw-loader@1 \
postcss-loader \
style-loader \
awesome-typescript-loader \
angular2-template-loader \
@ckeditor/ckeditor5-dev-utils \
@ckeditor/ckeditor5-theme-lark \
@angular-builders/custom-webpack \
@angular-builders/dev-server
And dependencies (CKEditor5 core features):
npm i -S \
@ckeditor/ckeditor5-editor-classic \
@ckeditor/ckeditor5-essentials \
@ckeditor/ckeditor5-basic-styles \
@ckeditor/ckeditor5-heading \
@ckeditor/ckeditor5-paragraph
Secondly, update your angular.json
(and remember to fill with your project name):
"architect": {
"build": {
"builder": "@angular-builders/custom-webpack:browser",
"options": {
"customWebpackConfig": {
"path": "./extra-webpack-config.js",
"mergeStrategies": {
"module.rules": "replace"
}
},
...
}
}
},
"serve": {
"builder": "@angular-builders/dev-server:generic",
"options": {
"browserTarget": "your-project-name:build"
},
"configurations": {
"production": {
"browserTarget": "your-project-name:build:production"
}
}
},
...
}
And add simple webpack config under the ./extra-webpack-config.js
path:
const { styles } = require( '@ckeditor/ckeditor5-dev-utils' );
module.exports = {
module: {
rules: [
{
test: /\.svg$/,
use: [ 'raw-loader' ]
},
{
test: /ckeditor5-[^/\\]+[/\\]theme[/\\].+\.css/,
use: [
{
loader: 'style-loader',
options: {
singleton: true
}
},
{
loader: 'postcss-loader',
options: styles.getPostCssConfig( {
themeImporter: {
themePath: require.resolve( '@ckeditor/ckeditor5-theme-lark' )
},
minify: true
} )
}
]
},
{
test: /\.ts$/,
exclude: /\.(spec|e2e)\.ts$/,
use: [
'awesome-typescript-loader',
'angular2-template-loader'
],
},
{
test: /\.css/,
exclude: /ckeditor5-[^/\\]+[/\\]theme[/\\].+\.css/,
use: 'raw-loader'
},
{
test: /\.html/,
use: [
'html-loader'
]
}
]
}
}
Then you can create your component that can use CKEditor5 plugins (app.component.ts
):
import { Component } from '@angular/core';
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 Heading from '@ckeditor/ckeditor5-heading/src/heading';
@Component( {
selector: 'app-root',
templateUrl: './app.component.html',
styleUrls: [ './app.component.css' ]
} )
export class AppComponent {
public Editor = ClassicEditor;
public config = {
plugins: [ Essentials, Paragraph, Bold, Italic, Heading ],
toolbar: [ 'heading', '|', 'bold', 'italic', '|', 'undo', 'redo', ]
};
}
And write a simple template (app.component.html
):
<ckeditor [editor]="Editor" [config]="config"></ckeditor>
After that step, you'll be able to run ng serve
/ ng build
.
There're a lot of steps, but for now, I don't think it could be done in some easier way. And I didn't check how tests work, but there is the jest builder
too - https://github.com/meltedspark/angular-builders/tree/master/packages/jest.
The working example you can find here: https://github.com/ma2ciek/ckeditor5-angular-test/tree/t/26
Hello, thanks for the documentation how to use it with a build from source. However, it does not work for me as expected. I am building the Angular application without the angular-cli
package, which actually works very good. I am using webpack 4.12 to to build the application with Angular 6.0.6 right now.
Here the part of my webpack configuration I am using responsible for the editor integration:
...
{
test: /ckeditor5-[^\/]+\/theme\/icons\/[^\/]+\.svg$/,
use: ['raw-loader']
},
{
test: /ckeditor5-[^\/]+\/theme\/[\w-\/]+\.css$/,
use: [
{
loader: 'style-loader',
options: {
singleton: true
}
},
{
loader: 'postcss-loader',
options: styles.getPostCssConfig({
themeImporter: {
themePath: require.resolve('@ckeditor/ckeditor5-theme-lark')
},
minify: true
})
},
]
},
...
Please note that I am also using the plugin like this:
new CKEditorWebpackPlugin({
// See https://ckeditor.com/docs/ckeditor5/latest/features/ui-language.html
language: 'en',
additionalLanguages: 'all'
}),
I also created a ckeditor.ts
in my core module which looks the following:
import UploadAdapterPlugin from '@ckeditor/ckeditor5-adapter-ckfinder/src/uploadadapter'
import AutoformatPlugin from '@ckeditor/ckeditor5-autoformat/src/autoformat'
import BoldPlugin from '@ckeditor/ckeditor5-basic-styles/src/bold'
import ItalicPlugin from '@ckeditor/ckeditor5-basic-styles/src/italic'
import BlockQuotePlugin from '@ckeditor/ckeditor5-block-quote/src/blockquote'
import EasyImagePlugin from '@ckeditor/ckeditor5-easy-image/src/easyimage'
import ClassicEditorBase from '@ckeditor/ckeditor5-editor-classic/src/classiceditor'
import EssentialsPlugin from '@ckeditor/ckeditor5-essentials/src/essentials'
import HeadingPlugin from '@ckeditor/ckeditor5-heading/src/heading'
import ImagePlugin from '@ckeditor/ckeditor5-image/src/image'
import ImageCaptionPlugin from '@ckeditor/ckeditor5-image/src/imagecaption'
import ImageStylePlugin from '@ckeditor/ckeditor5-image/src/imagestyle'
import ImageToolbarPlugin from '@ckeditor/ckeditor5-image/src/imagetoolbar'
import ImageUploadPlugin from '@ckeditor/ckeditor5-image/src/imageupload'
import LinkPlugin from '@ckeditor/ckeditor5-link/src/link'
import ListPlugin from '@ckeditor/ckeditor5-list/src/list'
import ParagraphPlugin from '@ckeditor/ckeditor5-paragraph/src/paragraph'
export default class ClassicEditor extends ClassicEditorBase {}
ClassicEditor.builtinPlugins = [
EssentialsPlugin,
UploadAdapterPlugin,
AutoformatPlugin,
BoldPlugin,
ItalicPlugin,
BlockQuotePlugin,
EasyImagePlugin,
HeadingPlugin,
ImagePlugin,
ImageCaptionPlugin,
ImageStylePlugin,
ImageToolbarPlugin,
ImageUploadPlugin,
LinkPlugin,
ListPlugin,
ParagraphPlugin
]
ClassicEditor.defaultConfig = {
toolbar: {
items: [
'heading',
'|',
'bold',
'italic',
'link',
'bulletedList',
'numberedList',
'imageUpload',
'blockQuote',
'undo',
'redo'
]
},
image: {
toolbar: [
'imageStyle:full',
'imageStyle:side',
'|',
'imageTextAlternative'
]
},
language: 'en'
}
and a component like
import {Component} from '@angular/core'
import ClassicEditor from '@core/ckeditor'
@Component({
selector: 'app-test',
templateUrl: './app-test.component.html',
})
export class AppTestComponent {
editor = ClassicEditor;
}
and finally I expected to see a result with the following markup:
<ckeditor [editor]="editor"></ckeditor>
The @core module obviously is importing the CKEditorModule and exporting it as well. The result is an error in my console with the following issue:
Class constructor ClassicEditor cannot be invoked without 'new'
at new ClassicEditor (ckeditor.ts:19)
at resolve (classiceditor.js:188)
at new ZoneAwarePromise (zone.js:891)
at Function.create (classiceditor.js:187)
at CKEditorComponent.push../node_modules/@ckeditor/ckeditor5-angular/fesm5/ckeditor-ckeditor5-angular.js.CKEditorComponent.createEditor (ckeditor-ckeditor5-angular.js:187)
at ckeditor-ckeditor5-angular.js:96
at ZoneDelegate.innosabi_polyfills../node_modules/zone.js/dist/zone.js.ZoneDelegate.invoke (zone.js:388)
at Zone.innosabi_polyfills../node_modules/zone.js/dist/zone.js.Zone.run (zone.js:138)
at NgZone.innosabi_main../node_modules/@angular/core/fesm5/core.js.NgZone.runOutsideAngular (core.js:3613)
at CKEditorComponent.push../node_modules/@ckeditor/ckeditor5-angular/fesm5/ckeditor-ckeditor5-angular.js.CKEditorComponent.ngAfterViewInit (ckeditor-ckeditor5-angular.js:95)
Using the way described in the first post, e.g. importong the original one and passing it with the options to the ckeditor component does not invoke any errors but gives me an empty editor without any markup. Any ideas what could be wrong?
Hi, @ezintz!
Thanks for the detailed issue.
Do you use babel or some other tool that transpiles code? The error message (Class constructor ClassicEditor cannot be invoked without 'new'
) looks like something could be wrong there.
You export the ClassicEditor
class in the core/ckeditor.ts
, so you should then import it in the component correctly. And you should import the Component
from the angular's core.
Are you sure you use @core
scoped packages so you can use @core/somePackage
import? Probably you want to use e.g. import ClassicEditor from '../ckeditor'
I guess you have many errors there.
You are right, it was just because I was trying quickly to hack some example code since my app is a bit larger. I will update the code snippets above..
Yes, I am sure it works even without scoped packages - meaning that I could also write 'core/path/to/my/module' - since the baseUrl is pointing to './src'. Yes, I see that there is actually something wrong but I am not sure why exactly.. that's why I decided to post a comment on this issue.
btw: I've updated the snippet above and no I am not using babel or anything else.
ok :)
Does the same error throw now?
I'm also thinking about the TypeScript's transpilation. But it'd be weird. You could eventually try to use target: "ES6"
It works with targeting ES6!
Good to now that there's an issue there. Glad to help you :)
Hi!
I'm facing similar issue in my project with Angular 5.2.11
.
I have the following imports:
import * as ClassicEditor from '@ckeditor/ckeditor5-build-classic';
import Bold from '@ckeditor/ckeditor5-basic-styles/src/bold';
import Italic from '@ckeditor/ckeditor5-basic-styles/src/italic';
import Underline from '@ckeditor/ckeditor5-basic-styles/src/underline';
And I'm using them like that:
public Editor = ClassicEditor;
public CKEConfig = {
plugins: [Bold, Italic, Underline],
toolbar: ['bold', 'italic', 'underline']
};
Then in my template it looks like:
<ckeditor [editor]="Editor"
[config]="CKEConfig"></ckeditor>
And at compilation time (when Angular is served) it logs the following error in the console:
ckeditor-ckeditor5-angular.js:198 TypeError: Cannot read property 'getAttribute' of null
at IconView._updateXMLContent (iconview.js:100)
at IconView.render (iconview.js:76)
at IconView.on (observablemixin.js:250)
at IconView.fire (emittermixin.js:196)
at IconView.(anonymous function) [as render] (webpack-internal:///./node_modules/@ckeditor/ckeditor5-utils/src/observablemixin.js:258:16)
at ViewCollection.on (viewcollection.js:68)
at ViewCollection.fire (emittermixin.js:196)
at ViewCollection.add (collection.js:182)
at ButtonView.render (buttonview.js:168)
at ButtonView.on (observablemixin.js:250)
I guess, when I read what @ma2ciek wrote above, that I've something to do in the .angular-cli.json
file (maybe in scripts/styles arrays or in stylePreprocessorOptions) but I don't know what. 🤔
Hi, @maximelafarie.
I guess, when I read what @ma2ciek wrote above, that I've something to do in the .angular-cli.json file (maybe in scripts/styles arrays or in stylePreprocessorOptions) but I don't know what. 🤔
The problem is that you need to tell webpack how you want to handle SVG files. This guide was written for the angular@6 and I'm not sure if it fits the angular@5 environment. For the older versions the first step would be to call ng eject
and then make some direct changes to the webpack.config.js
, similarly to how it's done in the react integration guide.
If you don't want to call reject, you can still build the editor outside of the angular project and then include it to the angular one. It's described in the #6.
The second problem you're facing is the fact that you can't mix builds and source code. See installing plugins guide. If you want to extend the default setup for the ClassicEditor
you can type import * as ClassicEditor from '@ckeditor/ckeditor5-build-classic/src/ckeditor';
instead of importing the whole bundle or import all plugins separately.
Building my own ckeditor5 is working like a charm! Thank you so much @ma2ciek! 👌😚
That's what I'm getting sadly.
CKEditorError@http://localhost:4200/vendor.js:92753:3 _setRanges/anyNewRange<@http://localhost:4200/vendor.js:75557:11 _setRanges@http://localhost:4200/vendor.js:75544:23 setTo@http://localhost:4200/vendor.js:75489:4 Selection@http://localhost:4200/vendor.js:75216:4 injectAndroidBackspaceMutationsHandling@http://localhost:4200/vendor.js:86081:25 init@http://localhost:4200/vendor.js:85277:3 ./node_modules/zone.js/dist/zone.js/</ZoneDelegate.prototype.invoke@http://localhost:4200/polyfills.js:2710:17 ./node_modules/zone.js/dist/zone.js/</Zone.prototype.run@http://localhost:4200/polyfills.js:2460:24 scheduleResolveOrReject/<@http://localhost:4200/polyfills.js:3194:29 ./node_modules/zone.js/dist/zone.js/</ZoneDelegate.prototype.invokeTask@http://localhost:4200/polyfills.js:2743:17 ./node_modules/zone.js/dist/zone.js/</Zone.prototype.runTask@http://localhost:4200/polyfills.js:2510:28 drainMicroTaskQueue@http://localhost:4200/polyfills.js:2917:25
Hi, @webpreneur, could you post the error message as well?
Dear @ma2ciek ,
I appreciate your immediate response,
To be honest, my exact challenge right now is in the following stackoverflow thread. (I'm not sure if this is the place where my issue'll be solved eventually :))
https://stackoverflow.com/questions/53188505/add-font-family-feature-to-ckeditor-toolbar-in-angular
PS.: That was the whole error message which I have found in the console.
Regards,
Zsolt
I've looked at your SO question, but I think we can discuss it here as well (Or if you mind, you can create a new issue in this repository linking to this one). The fontFamily
plugin isn't built-in into any of our builds. You should create a custom build or try to build the editor from the source like you do now.
What browser do you use? Maybe you could see if the error looks different on the other one? I'm asking because the above error doesn't mean much, it looks like some range error, but I can't tell which exactly looking at this stack trace.
The list of steps you did and the list of versions of CKEditor 5 packages you installed will also help to investigate that problem.
Kind regards, Maciek
Dear @ma2ciek !
I answered on SO. Would you like me to post my reply here as well?
Regards,
Zsolt
Hi again, @webpreneur!
I've replied to you on SO.
Back to the topic, I've just found the https://github.com/manfredsteyer/ngx-build-plus. It seems to be an unofficial, but suggested solution (https://github.com/angular/angular-cli/issues/10618#issuecomment-425506339)
I'm trying to build editor with ngx-build-plus
but I'm facing issues with postcss-loader
ERROR in ./node_modules/@ckeditor/ckeditor5-ui/theme/components/button/button.css
Module build failed (from ./node_modules/postcss-loader/src/index.js):
SyntaxError
(2:1) Unknown word
1 |
> 2 | var content = require("!!../../../../../postcss-loader/src/index.js??ref--19-1!./button.css");
| ^
3 |
4 | if(typeof content === 'string') content = [[module.id, content, '']];
I'm using webpack config from this issue and angular 7. Does anybody has working example? Thank You!
Hi @JurajKavka,
I'll try to investigate this problem soon, maybe something in the build process has changed since the last time.
I am not sure exactly what the issue is... but my first guess is that the CSS is not extracted correctly and webpack is trying to include it as Java-/TypeScript. Do you have any example configuration?
I have been switching back to CKEditor4 with Angular 7 and webpack without any Angular CLI, so I am not up to date. However this was happening, as far as I remember, when the extract css plugin was missing or wrong configured.
I have followed your instructions on how to create a custom build with angular as shown above, however I have got several problems with the loaders. I am working with fuse template. Please if you have any suggestions on how to solve this issue let me know. Thank you !
ERROR in Module parse failed: Unexpected character '@' (1:0)
You may need an appropriate loader to handle this file type, currently no loaders are configured to process this file. See https://webpack.js.org/concepts#loaders
@import "src/@fuse/scss/fuse"; | | :host { ERROR in ./src/app/app.module.ts 29:0 Module parse failed: Unexpected character '@' (29:0) You may need an appropriate loader to handle this file type, currently no loaders are configured to process this file. See https://webpack.js.org/concepts#loaders | | @NgModule({ | declarations: [ | AppComponent, ERROR in ./src/styles.scss 2:0 Module parse failed: Unexpected character '@' (2:0) You may need an appropriate loader to handle this file type, currently no loaders are configured to process this file. See https://webpack.js.org/concepts#loaders | // Import Fuse core library @import "@fuse/scss/core"; | // Import app.theme.scss | @import "app/app.theme"; ERROR in ./src/hmr.ts 4:35 Module parse failed: Unexpected token (4:35) You may need an appropriate loader to handle this file type, currently no loaders are configured to process this file. See https://webpack.js.org/concepts#loaders | import { createNewHosts } from '@angularclass/hmr'; | export const hmrBootstrap = (module: any, bootstrap: () => Promise<NgModuleRef
>) => { | let ngModule: NgModuleRef ; | module.hot.accept(); ERROR in ./src/polyfills.ts 65:8 Module parse failed: Unexpected token (65:8) You may need an appropriate loader to handle this file type, currently no loaders are configured to process this file. See https://webpack.js.org/concepts#loaders | | // Add global to window, assigning the value of window itself. (window as any).global = window; |
Hi @Sebi2909,
Sorry for the late response.
With the "mergeStrategies": { "module.rules": "replace" }
loaders in the custom webpack configuration replaces the original angular ones and loader for scss
files aren't set, probably that's why you face this issue. I'll check what is the default loader and settings for these files in the Angular ecosystem.
2 years later ... has anyone made any progress on building from source in an angular project? I didn't manage to get the loading rules right for both angular and CKEditor
@nadavsinai don't use CKEditor. Project is dead.