I am looking for an example showing an implementation of custom image uploader for CKEditor 5 using Angular 5.
I got this to work with relatively little fuss. It does not require rebuilding CKEditor. Here are the basic steps:
In your component, define an instance variable with a CKEditor configuration object if you have not already done so. It needs to have an extraPlugins element in it:
ckconfig = {
// include any other configuration you want
extraPlugins: [ this.TheUploadAdapterPlugin ]
};
Step two create the referenced function in your component that looks like this:
TheUploadAdapterPlugin(editor) {
console.log('TheUploadAdapterPlugin called');
editor.plugins.get('FileRepository').createUploadAdapter = (loader) => {
return new UploadAdapter(loader, '/image');
};
}
Step three create the new class that was referenced in the above function. You can do this in the same Typescript file after the component definition or you can make it in a separate file and import it.
class UploadAdapter {
loader; // your adapter communicates to CKEditor through this
url;
constructor(loader, url) {
this.loader = loader;
this.url = url;
console.log('Upload Adapter Constructor', this.loader, this.url);
}
upload() {
return new Promise((resolve, reject) => {
console.log('UploadAdapter upload called', this.loader, this.url);
console.log('the file we got was', this.loader.file);
resolve({ default: 'http://localhost:8080/image/1359' });
});
}
abort() {
console.log('UploadAdapter abort');
}
At the quick testing stage change the resolve call to return a URL that points to some fixed image. It can be anything. That URL string will be stuck into the editor content where user places the image.
When you go to implement, you will delete or change each of the console.log calls to whatever logic you want -- likely involving some calls to Angular's HttpClient. However note that the functions will execute outside of Angular's NgZone domain.
Your logic in resolve will have to generate the appropriate URL of course. Check the CKEditor documentation for a good example of how to interact with the loader.
Finally you need to make sure your ckeditor element is using the ckconfig configuration object something like this:
<ckeditor [editor]="Editor" [config]="ckconfig"
[(ngModel)]="doc.text" name="editcontent"></ckeditor>
Now when you use this editor you will use the tool to upload an image. You should see the output on the console log and the string in the resolve call inserted into the editor's content. If the string is a valid URL to an image somewhere you will see that image.
If this works for you there should be little problem completing the plug in.
Remember the generated Promise has a reject parameter so use it as needed.
resolve
renders img src only? –
Palatine There's no need for the Angular-specific version of that adapter.
You can use for example this: https://github.com/pourquoi/ckeditor5-simple-upload or try to integrate it with the CKFinder.
Then, all you need to do is to pass the configuration object to the <ckeditor [config]='config'>
component. Don't forget to set allowJs: true
in your tsconfig.json
file to allow bundling local JS files.
Alternatively, you can create it on your own. This should be the base skeleton of the upload adapter that should match the UploadAdapter interface:
editor.plugins.get( 'FileRepository' ).createUploadAdapter = loader => {
return new UploadAdapter( loader, editor.config.get( 'uploadUrl' ), editor.t );
}
class UploadAdapter {
constructor( loader, url, t ) {
this.t = t; // Translate function
this.loader = loader;
this.url = url; // Endpoint URL
}
upload() {
// Perform uploading and return a promise to that action.
}
abort() {
// Abort current upload process.
}
}
The whole process is described more deeply in docs: https://ckeditor.com/docs/ckeditor5/latest/framework/guides/deep-dive/upload-adapter.html. You can also look at the source code of https://github.com/pourquoi/ckeditor5-simple-upload/blob/master/src/adapter.js
Then, to get the editor
property in the ckeditor5-angular
package you need to do listen to the ready
event and obtain the editor parameter:
<editor [ready]="onReady" ...></editor>
@Component()
class EditorComponent {
public onReady( editor ) {
// Now you can acess the editor.
}
}
And that's besically it.
allowJs: true
in your tsconfig.json
to allow importing JS files using relative paths. Actually, I'm not sure if removing the .js
from the import path won't fix it too... I've updated the answer, maybe you'll find that way easier. –
Antonioantonius editor.plugins.get( 'FileRepository' ). createUploadAdapter = ...
as I've proposed above doesn't require creating any custom build. –
Antonioantonius I am using the CKEditor 5 in this example with Angular 9. Building on the code from @Maciej-Bukowski I had to make one crucial change to get this to work, otherwise the plugin would not instantiate, giving error t is not a constructor
.
After you have this working, replace the code with the example at CKEditor 5 custom upload adapter. FOr me this worked by just copy/pasting and changing the URL.
Simply replace the function in your component with a class:
class TheUploadAdapterPlugin {
constructor(editor) {
console.log('TheUploadAdapterPlugin called');
editor.plugins.get('FileRepository').createUploadAdapter = (loader) => {
return new TheUploadAdapter(loader);
};
}
}
Furthermore had some other small issues I had to figure out. So here is my complete code: In your component file (DialogEditContentComponent.ts or something alike)
class TheUploadAdapterPlugin {
constructor(editor) {
console.log('TheUploadAdapterPlugin called');
editor.plugins.get('FileRepository').createUploadAdapter = (loader) => {
return new TheUploadAdapter(loader);
};
}
}
export class DialogEditContentComponent implements OnInit {
public ckEditorClassic = DecoupledEditor;
public ckconfig = {};
constructor() {
this.ckconfig = {
// include any other configuration you want
// https://mcmap.net/q/1688623/-how-to-implement-custom-image-uploader-for-ckeditor5-in-angular-5
extraPlugins: [ TheUploadAdapterPlugin ]
};
}// constructor
public onReady( editor ) {
editor.ui.getEditableElement().parentElement.insertBefore(
editor.ui.view.toolbar.element,
editor.ui.getEditableElement()
);
}
}// DialogEditContentComponent
then in the .HTML-template file use (in this case I use the Reactive forms and the formbuilder, hence the 'formControlName' instead of the [(NgModel)]!
<ckeditor [editor]="ckEditorClassic"
formControlName="HTML"
[config]="ckconfig"
(ready)="onReady($event)"></ckeditor>
and then the uploader itself: (filename the-upload-adapter.ts)
export class TheUploadAdapter {
loader; // your adapter communicates to CKEditor through this
url;
constructor(loader) {
this.loader = loader;
this.url = 'xxxxxxxxxxxxxxxx';
console.log('Upload Adapter Constructor', this.loader, this.url);
}
upload() {
return new Promise((resolve, reject) => {
console.log('UploadAdapter upload called', this.loader, this.url);
console.log('the file we got was', this.loader.file);
resolve({ default: 'http://localhost:8080/image/1359' });
});
}
abort() {
console.log('UploadAdapter abort');
}
}
© 2022 - 2024 — McMap. All rights reserved.