Search code examples
angularfile-uploadckeditor

Adding simple upload adapter to ckeditor in Angular


I did started classic ckeditor in my angular project by installing...

npm install --save @ckeditor/ckeditor5-angular
npm install --save @ckeditor/ckeditor5-build-classic

In my component I've imported it...

import * as ClassicEditor from '@ckeditor/ckeditor5-build-classic';

initiated object...

  public Editor = ClassicEditor;

and in template started like this

        <ckeditor [config]="{ toolbar: [ 'heading', '|', 'bold', 'italic' ] }" name="Blog" placeholder="About your ride" [(ngModel)]="formModel.Blog" required [editor]="Editor" data="<p>Blog!</p>"></ckeditor>

Now I want to add Simple upload adapter to this editor.

I did install

npm install --save @ckeditor/ckeditor5-upload

Then there is written

And I'm stuck here. I don't know what's and where is that plugin list.

Any ideas how to add image upload to my ckeditor ?

Update In my component did this:

  public Editor = ClassicEditor;
  public config = { plugins: [SimpleUploadAdapter]};

In my HTML calling the same

<ckeditor [config]="{ toolbar: [ 'heading', '|', 'bold', 'italic' ] }" name="Blog" placeholder="About your ride" [(ngModel)]="formModel.Blog" required [editor]="Editor" data="<p>Blog!</p>"></ckeditor>

getting error

Uncaught CKEditorError: ckeditor-duplicated-modules: Some CKEditor 5 modules are duplicated


Solution

  • I got this working by using a custom upload adapter instead of SimpleUploadAdapter - i.e.

    1. Copy the class only from here: https://ckeditor.com/docs/ckeditor5/latest/framework/guides/deep-dive/upload-adapter.html#the-complete-implementation
      (I adapted it slightly to avoid TypeScript errors, and changed the URL)
    2. Import the class into your Angular component
    3. Add a ready handler that provides an implementation for createUploadAdapter

    So my code is:

    my-upload-adapter.ts:

    export class MyUploadAdapter {
        loader: any;
        xhr: any;
        constructor( loader ) {
            this.loader = loader;
        }
        upload() {
            return this.loader.file
                .then( file => new Promise( ( resolve, reject ) => {
                    this._initRequest();
                    this._initListeners( resolve, reject, file );
                    this._sendRequest( file );
                } ) );
        }
        abort() {
            if ( this.xhr ) {
                this.xhr.abort();
            }
        }
        _initRequest() {
            const xhr = this.xhr = new XMLHttpRequest();
            xhr.open( 'POST', 'http://yourserver/upload', true ); // TODO change the URL
            xhr.responseType = 'json';
        xhr.setRequestHeader("Accept", "application/json");
        }
        _initListeners( resolve, reject, file ) {
            const xhr = this.xhr;
            const loader = this.loader;
            const genericErrorText = `Couldn't upload file: ${ file.name }.`;
            xhr.addEventListener( 'error', () => reject( genericErrorText ) );
            xhr.addEventListener( 'abort', () => reject() );
            xhr.addEventListener( 'load', () => {
                const response = xhr.response;
                if ( !response || response.error ) {
                    return reject( response && response.error ? response.error.message : genericErrorText );
                }
                resolve( {
                    default: response.url
                } );
            } );
            if ( xhr.upload ) {
                xhr.upload.addEventListener( 'progress', evt => {
                    if ( evt.lengthComputable ) {
                        loader.uploadTotal = evt.total;
                        loader.uploaded = evt.loaded;
                    }
                } );
            }
        }
        _sendRequest( file ) {
            const data = new FormData();
            data.append( 'upload', file );
            this.xhr.send( data );
        }
    }
    

    my.component.html:

    <ckeditor ... (ready)="onReady($event)"></ckeditor>
    

    my.component.ts:

    import * as ClassicEditor from '@ckeditor/ckeditor5-build-classic';
    import { MyUploadAdapter } from '../my-upload-adapter';
    
    //...
    
    export class MyComponent implements OnInit {
        Editor = ClassicEditor;
    
    //...
    
        onReady(editor: ClassicEditor): void {
            editor.plugins.get( 'FileRepository' ).createUploadAdapter = ( loader ) => {
                return new MyUploadAdapter( loader );
            };
        }
    }