Ckeditor5-angular: How to integrate Base64 or Simple file upload?

Created on 28 Jul 2019  路  16Comments  路  Source: ckeditor/ckeditor5-angular

First thing, it seem this issue uses another packages: https://github.com/ckeditor/ckeditor5-angular/issues/88

Packages i install:
ckeditor5-
adapter-ckfinder
angular
build-classic
build-decoupled-document
ckfinder
core
editor-decoupled
engine
ui
upload
utils

in the upload i found: base64uploadadapter.js

Another thing... i have no idea how config work, how to add simple plugin, how to manythings, how js based sample can be converted to angular.

Here's my code:

import { Component, OnInit } from '@angular/core';
import * as ClassicEditor from '@ckeditor/ckeditor5-build-classic';
import * as DecoupledEditor from '@ckeditor/ckeditor5-build-decoupled-document';
import Base64UploadAdapter from '@ckeditor/ckeditor5-upload/src/base64uploadadapter';
import { FormBuilder, Validators, FormGroup, ValidationErrors } from '@angular/forms';
import { Util } from 'leaflet';
import { environment } from 'src/environments/environment';
import { Utility } from 'src/_utility/Utility';

@Component({
  selector: 'app-story-insert',
  templateUrl: './story-insert.component.html',
  styleUrls: ['./story-insert.component.less']
})
export class StoryInsertComponent implements OnInit {
  util = Util;

  public ckEditor = DecoupledEditor; // ClassicEditor;

  insertForm: FormGroup;

  envValidators = environment.validations;

  public onReady( editor ) {
    editor.ui.getEditableElement().parentElement.insertBefore(
        editor.ui.view.toolbar.element,
        editor.ui.getEditableElement()
    );

    // tslint:disable-next-line:max-line-length
    // Only useful source: https://stackoverflow.com/questions/52052514/upload-adapter-is-not-defined-issue-with-image-uploading-in-ckeditor5-angular/55237874#55237874
    editor.plugins.get('FileRepository').createUploadAdapter = (loader) => {
      console.log(btoa(loader.file));
      return new Base64UploadAdapter(loader); // UploadAdapter(loader);
    };
  }

  constructor(private fb: FormBuilder) { }

  ngOnInit() {
    this.insertForm = this.fb.group({
      title: ['', [Validators.required, Validators.maxLength(this.envValidators.titleMaxLength)]],
      description: ['', [Validators.required, Validators.maxLength(this.envValidators.descriptionMaxLength)]],
      keywords: ['', [Validators.required, Validators.maxLength(this.envValidators.articleKeywordMaxLength)]],
      articleContent: ['']
    }, {
      validators: [this.ckEditorArticleValidator('articleContent')]
    });
  }

  public ckEditorArticleValidator(ckEditorField: string): (g: FormGroup) => ValidationErrors | null {
    const validator = (g: FormGroup): ValidationErrors | null => {
      const ckEditorCtrl = g.get(ckEditorField);
      if (Utility.isNullOrUndefinedOrWhiteSpace(ckEditorCtrl.value)) {
        return {ckeditor_required: true};
      } else if (ckEditorCtrl.value.length > 4000) {
        return {ckeditor_maxlength: true};
      }
      return null;
    };
    return validator;
  }

  insertStory(): void {

  }



  get title() {
    return this.insertForm && this.insertForm.get('title');
  }
  get description() {
    return this.insertForm && this.insertForm.get('description');
  }
  get keywords() {
    return this.insertForm && this.insertForm.get('keywords');
  }
  get articleContent() {
    return this.insertForm && this.insertForm.get('articleContent');
  }
}

I get this error:

log.js:58 ckeditor-duplicated-modules: Some CKEditor 5 modules are duplicated. Read more: https://ckeditor.com/docs/ckeditor5/latest/framework/guides/support/error-codes.html#error-ckeditor-duplicated-modules
undefined

After click and upload image, i get this error:

TypeError: this._adapter.upload is not a function

solved question

Most helpful comment

why do i have to clone your code and do a custom build just to add that plugin... this is ridiculous...

edit: ok i just copy pasted your code into my project... just 1 hour with your library and i already want to throw it and do it myself. why such complexity? i don't want to read your doc totally before starting to work... that's not how it works in 2020. in 2020 the getting start guide should be enough to cover 90% of the cases. then if the library is great we go deep to the doc, to customize as we want. i just started to read your doc and i already want to give up. "in order to do a simple thing start cloning our code first" why?! "read our 5000 pages of doc to understand how our textarea works" ... you don't like developers dude. you are just like the people who created so many bad games on NES and master system when we were kids, just to make us suffer for nothing.

All 16 comments

~If you want to add plugins to the editor, you should create a custom build, then import it to your project instead of ckeditor5-build-classic. Please, see our~ ~Installing plugins guide~~.~

Update:

The easiest way to set the upload adapter or to add / remove plugin from the build is by creating a custom build via the CKEditor 5 Online Builder that includes your own set of plugins. Then, you should follow the CKEditor 5 Angular integration - custom build section.

The issue i have is i'm using angular, and i do not know how to perform these configurations inside angular. and no page about it.... also

was this right thing to do?

    // tslint:disable-next-line:max-line-length
    // Only useful source: https://stackoverflow.com/questions/52052514/upload-adapter-is-not-defined-issue-with-image-uploading-in-ckeditor5-angular/55237874#55237874
    editor.plugins.get('FileRepository').createUploadAdapter = (loader) => {
      console.log(btoa(loader.file));
      return new Base64UploadAdapter(loader); // UploadAdapter(loader);
    };

Are my errors because i use decoupled-document ? isn't that a custom build?

The docs for the Base64 image upload adapter are available here: https://ckeditor.com/docs/ckeditor5/latest/features/image-upload/base64-upload-adapter.html.

So you should add this plugin to the array of plugins instead of manually setting the upload adapter like this: editor.plugins.get('FileRepository').createUploadAdapter....

But the other thing is what @Mgsy noted, you shouldn't add a plugin to the existing build (because of module duplication, PostCSS errors, invalid instanceof checks and so on...) and you should create a build using the https://ckeditor.com/docs/ckeditor5/latest/builds/guides/integration/installing-plugins.html guide. Then with this customized build, you should have the upload adapter already customized.

If you don't want to rebuild CKEditor5 every release, then you can set:

editor.plugins.get( FileRepository ).createUploadAdapter = loader => new Adapter( loader );

and copy the base64 Adapter definition (https://github.com/ckeditor/ckeditor5-upload/blob/master/src/adapters/base64uploadadapter.js#L56-L108).

Feel free to ask any questions.

Hi, sorry, my answer was a bit imprecise.

Using ClassicEditor, is the base64 image plugin included?

No, it's present in the ckeditor5-upload package, but the ClassicEditor build doesn't ship it by default. That's why you need to customize the build or add your own plugin to the existing build.

Where does someone write this
editor.plugins.get( FileRepository ).createUploadAdapter = loader => new Adapter( loader );

Here goes a sample implementation:

<ckeditor (ready)="onReady($event)"...>
@Component(...)
export class MyEditor {
  onReady( editor ) {
    editor.plugins.get( 'FileRepository' ).createUploadAdapter = loader => new Adapter( loader );
  }
}

class Adapter {
  // Use https://github.com/ckeditor/ckeditor5-upload/blob/78424289e89834d3e3b722523e3c326acde925e5/src/adapters/base64uploadadapter.js#L56-L108
}

Where does Adapter come from?

Like I mentioned in the previous post the implementation can be similar to the https://github.com/ckeditor/ckeditor5-upload/blob/78424289e89834d3e3b722523e3c326acde925e5/src/adapters/base64uploadadapter.js#L56-L108

How do I this Base64UploadAdapter without getting the duplicate modules error?

Following the above steps, you won't get this message because you won't duplicate CKEditor 5 packages.

why do i have to clone your code and do a custom build just to add that plugin... this is ridiculous...

edit: ok i just copy pasted your code into my project... just 1 hour with your library and i already want to throw it and do it myself. why such complexity? i don't want to read your doc totally before starting to work... that's not how it works in 2020. in 2020 the getting start guide should be enough to cover 90% of the cases. then if the library is great we go deep to the doc, to customize as we want. i just started to read your doc and i already want to give up. "in order to do a simple thing start cloning our code first" why?! "read our 5000 pages of doc to understand how our textarea works" ... you don't like developers dude. you are just like the people who created so many bad games on NES and master system when we were kids, just to make us suffer for nothing.

why do i have to clone your code and do a custom build just to add that plugin... this is ridiculous...

edit: ok i just copy pasted your code into my project... just 1 hour with your library and i already want to throw it and do it myself. why such complexity? i don't want to read your doc totally before starting to work... that's not how it works in 2020. in 2020 the getting start guide should be enough to cover 90% of the cases. then if the library is great we go deep to the doc, to customize as we want. i just started to read your doc and i already want to give up. "in order to do a simple thing start cloning our code first" why?! "read our 5000 pages of doc to understand how our textarea works" ... you don't like developers dude. you are just like the people who created so many bad games on NES and master system when we were kids, just to make us suffer for nothing.

  1. The thing is complicated because of the specific Angular ecosystem and our plugin separation. We can't currently compile CKEditor 5 sources with Angular sources because the Angular webpack plugin and loaders conflict with CKEditor 5 set of webpack plugin and loaders needed to attach. Notabene, CKEditor5 has introduced CSS and SVG imports to provide a way for customizing the output and easily maintaining it (like extracting CSS from added plugins to the separate files instead of the bundle, etc). With old Angular's extracting webpack config this would be solvable at this point.
  2. The CKEditor 5 architecture is very modular. You don't get everything bundled into one package because it would weight a couple of MB and nobody would be happy to attach such file in the web. BTW - what to do with conflicting packages? Like Base64 upload adapter and Simple upload adapter, easy image adapter, CKFinder adapter and so on...
  3. There're predefined CKEditor 5 builds. They're a little bit pain in the ass as they are limited and everybody has different needs and rebuilding with new plugins aren't easy. I have to agree with you that they produce highly negative DX.
  4. There's an online builder tool that was created to ease the whole process. Use it if you don't want to customize the editor deeply. https://ckeditor.com/ckeditor-5/online-builder/ It will take less than 1 minute ot get ready to use CKEditor 5 build with your plugins.

I'll hide my previous response as it's outdated a little bit. Use CKEditor 5 Online builder instead.

Thank you for the quick reply. I switched to quill, and it works as I want so far (base64 images out of the box):

image

Also the buttons are responsive out of the box( had an issue with that with ckEditor 5 ):

image

I understand your reasons but imo it should be different. First of all, I took the "Document" build, and the upload image icon is there by default in the toolbox. So I imagine that the plugin should be there as well.

I'm a backend developer, so I'm not an expert with webpack, I have to admit it. But for other libraries, I'm used to do stuff like:
npm install @ckEditor/angular-plugin-file-base-64

then in my imports: ImageBase64Module.

That makes more sense imo. I will have a look to the online builder, but I don't like this solution, because then I have to manage your project somewhere on a git repo. This would produce issue:

-> how do I align my version with the default one, in case of bug fixing for instance?

Another solution would be to define interfaces that we can implement. I couldn't find an interface like "Plugin" in the document build, althought it would be very useful. Then I know what's expected from the plugin management system.

The toolbar for each build should be minimalistic if you don't bundle the according plugins, at least a default one. Because we think if it's there, then it's part of the build. but it's not.

Thanks for the reply again, CkEditor looks like a great product, but for proof of concept like I'm doing now, it doesn't fit. Developer experience can definitely be improved.

Thanks for the feedback!

I understand your reasons but imo it should be different. First of all, I took the "Document" build, and the upload image icon is there by default in the toolbox. So I imagine that the plugin should be there as well

The plugin for handling image inside the editor is built-in to all builds. However the "upload" part which requires the Image plugin can be defined in various ways. Thus why there's an image button, but there's no image adapter set. Actually, the base64 could be the default one, but uploading a few images would blow up your data and while it makes sense for PoCs it doesn't make sense for larger apps.

I'm a backend developer, so I'm not an expert with webpack, I have to admit it. But for other libraries, I'm used to do stuff like:
npm install ckEditor/angular-plugin-file-base-64

Yes... It works this way in our React and Vue integrations. But because of the mentioned issue with Angular loaders incompatibility and no way to configure the webpack for the Angular's app there's currently no way to do that. I know it's a big pain. But the CKEditor 5 wasn't developed for Angular ecosystem. It's a pure Vanilla JS editor. Unfortunately, we added two loaders needed to compile our sources - SVG loader and (Post)CSS loaders to not write everything in the JS files and to be able to reuse some parts of the UI, plus be able to configure the output. That's actually a bit PITA in the JS ecosystem. There're many "standards" that are conflicting. with only one default loader for CSS and SVG that problem wouldn't exist.

That makes more sense imo. I will have a look to the online builder, but I don't like this solution, because then I have to manage your project somewhere on a git repo. This would produce issue:

-> how do I align my version with the default one, in case of bug fixing for instance?

There're a few tools like `npm-check-updates` and similar ones which can update picked dependencies to latest ones. We'll also provide a tool to download an updated version of your packages from the CKEditor 5 Online Builder tool.

Another solution would be to define interfaces that we can implement. I couldn't find an interface like "Plugin" in the document build, althought it would be very useful. Then I know what's expected from the plugin management system.

Have you seen the https://ckeditor.com/docs/ckeditor5/latest/framework/guides/creating-simple-plugin.html and Plugin interface? There's also the Plugin base class, which you can use to inherit some useful methods from it.

Thanks for the reply again, CkEditor looks like a great product, but for proof of concept like I'm doing now, it doesn't fit. Developer experience can definitely be improved.

I think that the core of the problem is that it doesn't work out of the box. But there're a few reasons why it currently behaves differently.

Also the buttons are responsive out of the box( had an issue with that with ckEditor 5 ):

Could you tell more about it? And could you create an issues in the CKEditor 5 issue tracker?

Hey,
Sorry I didn't create a issue on github because I didn't go further but here it is:

image

As you see, the toolbar goes outside the window in case of mobile device; maybe it's not a bug, I am not sure; also there is no borders on the textarea part;

Step to reproduce:

 npm install --save @ckeditor/ckeditor5-angular
 npm i @ckeditor/ckeditor5-build-decoupled-document

app.module.ts:


  imports: [
    ...,
    CKEditorModule
  ]

document.component.ts:


 public Editor = DecoupledEditor;

  public onReady( editor ) {
    editor.ui.getEditableElement().parentElement.insertBefore(
      editor.ui.view.toolbar.element,
      editor.ui.getEditableElement()
    );
  }

document.component.html:


<ckeditor [editor]="Editor" data="<p>Hello, world!</p>" (ready)="onReady($event)"></ckeditor>

if you confirm it's a bug i'll create a ticket np, btw it is the hello world example, i didn't try to customize anything

For the base64 images I can live with it because I'm storing the document as an html page on the disk (server side), I just have a reference to the file in the database (mongodb); I could use GridFS to store it with mongo but I was too lazy yesterday haha. Also I don't think the document will contains tons of images, so I can live with my solution. For the Plugin interface, I couldn't find it in the npm packages I've installed (same as the hello world example), I think I had to make a custom build anyway to get that interface; Managing an external dependency is not something I did often during my carreer , 7y doing backend dev. I did it two times yes, it wasn't my idea and i didn't like it (and we end up having issues with that). I see external deps as black boxes. Having it on a private repo means that if a developer starts working on my project, he has to clone two projects and not one. or I could have my own npm registry, but all those steps equals less productive work imo. I'll try it again once my POC is over, lots of nice plugins there, maybe i'll switch to react or vue (the nice thing when being backend dev is to be completely open for changes on the frontend framework :D i like vuejs for its simplicity, maybe the experience will be better with it)

I assume people are still ending up here trying to figure out how to integrate this all into an Angular Application, specifically with the simpleFileUpload plugin. I made a sample repo of this being done using the custom build tool CKEditor offers and added the steps in the README, cheers!

CKEditor5 with Angular and SimpleFileUploader Plugin

Getting this error when using online builder to build with custom plugins.

core.js:5980 ERROR Error: Uncaught (in promise): CKEditorError: ckeditor-duplicated-modules
Read more: https://ckeditor.com/docs/ckeditor5/latest/framework/guides/support/error-codes.html#error-ckeditor-duplicated-modules
CKEditorError: ckeditor-duplicated-modules
Read more: https://ckeditor.com/docs/ckeditor5/latest/framework/guides/support/error-codes.html#error-ckeditor-duplicated-modules
    at Object.<anonymous> (ckeditor.js:5)
    at Object.push.+z1p (ckeditor.js:5)
    at n (ckeditor.js:5)
    at Module.<anonymous> (ckeditor.js:5)
    at n (ckeditor.js:5)
    at push.+z1p (ckeditor.js:5)
    at ckeditor.js:5
    at ckeditor.js:5
    at Object.+z1p (ckeditor.js:5)
    at __webpack_require__ (bootstrap:84)
    at resolvePromise (zone-evergreen.js:798)
    at resolvePromise (zone-evergreen.js:750)
    at zone-evergreen.js:860
    at ZoneDelegate.invokeTask (zone-evergreen.js:399)
    at Object.onInvokeTask (core.js:28289)
    at ZoneDelegate.invokeTask (zone-evergreen.js:398)
    at Zone.runTask (zone-evergreen.js:167)
    at drainMicroTaskQueue (zone-evergreen.js:569)

@ma2ciek

I followed @ecolara3 instruction and hit a brick wall.

the issue is that the custom build tool for ckeditor 5 (https://ckeditor.com/ckeditor-5/online-builder/) results in an ES6 build.

according to: https://github.com/ckeditor/ckeditor5-angular/issues/20 this ES6 build is using some uglifier resulted in throwing compilation error root is undefined for angular projects.

Please add ES5 build option to the custom build tool.

it's already a huge among of pain to customize ckeditor 5 for angular, please do not make it even worse.

Was this page helpful?
0 / 5 - 0 ratings