Angular 13 Ckeditor5自定义插件pluginCollection-plugin-non-not-found
我在Angular应用程序中使用CKEditor5。我已经生成了自定义构建,并保存了ckeditor.js
在我的项目dir中。一切都很好。我可以通过将此行添加到顶部来渲染编辑器并在角度组件中使用它:import *作为'src/ckcustombuild/ckeditor'的custombuild';
现在我正在浏览他们的样本“占位符”插件的文档,我似乎无法使其在Angular中使用。我在与ckeditor.js
的同一文件夹中添加了一个名为lugin.js
的新文件中的相同代码。
然后,我还将其导入我的组件,并按照以下方式构造配置:
import * as customBuild from 'src/ckCustomBuild/ckeditor';
import * as Placeholder from 'src/ckCustomBuild/placeholder.plugin';
...component boilerplate code....
htmlEditorConfig = {
plugins: [Placeholder],
toolbar: {
items: ['bold', 'italic', 'underline', 'link', 'alignment', 'bulletedList', 'numberedList', '|', 'fontFamily', 'fontSize', 'fontColor', 'fontBackgroundColor', '|', 'Paceholder'],
shouldNotGroupWhenFull: false,
},
// placeholderConfig: {
// types: ['date', 'color', 'first name', 'surname'],
// },
fontSize: {
options: [9, 11, 13, 'default', 17, 19, 21],
},
mediaEmbed: {
previewsInData: true,
},
};
当我运行应用程序时,我会收到以下运行时错误:
core.mjs:6494 ERROR Error: Uncaught (in promise): CKEditorError: plugincollection-plugin-not-found {"plugin":{}}
Read more: https://ckeditor.com/docs/ckeditor5/latest/framework/guides/support/error-codes.html#error-plugincollection-plugin-not-found
CKEditorError: plugincollection-plugin-not-found {"plugin":{}}
Read more: https://ckeditor.com/docs/ckeditor5/latest/framework/guides/support/error-codes.html#error-plugincollection-plugin-not-found
at p (ckeditor.js:5:432588)
at ckeditor.js:5:432433
at Array.forEach (<anonymous>)
at f (ckeditor.js:5:432420)
at wa.init (ckeditor.js:5:431516)
at bq.initPlugins (ckeditor.js:5:703694)
at ckeditor.js:5:831531
at new ZoneAwarePromise (zone.js:1351:1)
at bq.create (ckeditor.js:5:831488)
at CKEditorComponent.<anonymous> (ckeditor-ckeditor5-angular.js:235:1)
at resolvePromise (zone.js:1262:1)
at resolvePromise (zone.js:1216:1)
at zone.js:1329:1
at push.46026._ZoneDelegate.invokeTask (zone.js:443:1)
at Object.onInvokeTask (core.mjs:25595:1)
at push.46026._ZoneDelegate.invokeTask (zone.js:442:1)
at push.46026.Zone.runTask (zone.js:214:1)
at drainMicroTaskQueue (zone.js:632:1)
at push.46026.ZoneTask.invokeTask [as invoke] (zone.js:529:1)
at invokeTask (zone.js:1727:1)
这是我的两个文件的内容: 占位符。plugin.js
import * as Plugin from 'src/ckCustomBuild/ckeditor';
import * as Widget from 'src/ckCustomBuild/ckeditor';
import * as Command from 'src/ckCustomBuild/ckeditor';
import * as Collection from 'src/ckCustomBuild/ckeditor';
import * as Model from 'src/ckCustomBuild/ckeditor';
import * as toWidget from 'src/ckCustomBuild/ckeditor';
import * as viewToModelPositionOutsideModelElement from 'src/ckCustomBuild/ckeditor';
import * as addListToDropdown from 'src/ckCustomBuild/ckeditor';
import * as createDropdown from 'src/ckCustomBuild/ckeditor';
class Placeholder extends Plugin {
static get requires() {
return [PlaceholderEditing, PlaceholderUI];
}
}
class PlaceholderCommand extends Command {
execute({ value }) {
const editor = this.editor;
const selection = editor.model.document.selection;
editor.model.change((writer) => {
// Create a <placeholder> elment with the "name" attribute (and all the selection attributes)...
const placeholder = writer.createElement('placeholder', {
...Object.fromEntries(selection.getAttributes()),
name: value,
});
// ... and insert it into the document.
editor.model.insertContent(placeholder);
// Put the selection on the inserted element.
writer.setSelection(placeholder, 'on');
});
}
refresh() {
const model = this.editor.model;
const selection = model.document.selection;
const isAllowed = model.schema.checkChild(selection.focus.parent, 'placeholder');
this.isEnabled = isAllowed;
}
}
class PlaceholderUI extends Plugin {
init() {
const editor = this.editor;
const t = editor.t;
const placeholderNames = editor.config.get('placeholderConfig.types');
// The "placeholder" dropdown must be registered among the UI components of the editor
// to be displayed in the toolbar.
editor.ui.componentFactory.add('placeholder', (locale) => {
const dropdownView = createDropdown(locale);
// Populate the list in the dropdown with items.
addListToDropdown(dropdownView, getDropdownItemsDefinitions(placeholderNames));
dropdownView.buttonView.set({
// The t() function helps localize the editor. All strings enclosed in t() can be
// translated and change when the language of the editor changes.
label: t('Placeholder'),
tooltip: true,
withText: true,
});
// Disable the placeholder button when the command is disabled.
const command = editor.commands.get('placeholder');
dropdownView.bind('isEnabled').to(command);
// Execute the command when the dropdown item is clicked (executed).
this.listenTo(dropdownView, 'execute', (evt) => {
editor.execute('placeholder', { value: evt.source.commandParam });
editor.editing.view.focus();
});
return dropdownView;
});
}
}
function getDropdownItemsDefinitions(placeholderNames) {
const itemDefinitions = new Collection();
for (const name of placeholderNames) {
const definition = {
type: 'button',
model: new Model({
commandParam: name,
label: name,
withText: true,
}),
};
// Add the item definition to the collection.
itemDefinitions.add(definition);
}
return itemDefinitions;
}
class PlaceholderEditing extends Plugin {
static get requires() {
return [Widget];
}
init() {
console.log('PlaceholderEditing#init() got called');
this._defineSchema();
this._defineConverters();
this.editor.commands.add('placeholder', new PlaceholderCommand(this.editor));
this.editor.editing.mapper.on(
'viewToModelPosition',
viewToModelPositionOutsideModelElement(this.editor.model, (viewElement) => viewElement.hasClass('placeholder'))
);
this.editor.config.define('placeholderConfig', {
types: ['date', 'first name', 'surname'],
});
}
_defineSchema() {
const schema = this.editor.model.schema;
schema.register('placeholder', {
// Allow wherever text is allowed:
allowWhere: '$text',
// The placeholder will act as an inline node:
isInline: true,
// The inline widget is self-contained so it cannot be split by the caret and it can be selected:
isObject: true,
// The inline widget can have the same attributes as text (for example linkHref, bold).
allowAttributesOf: '$text',
// The placeholder can have many types, like date, name, surname, etc:
allowAttributes: ['name'],
});
}
_defineConverters() {
const conversion = this.editor.conversion;
conversion.for('upcast').elementToElement({
view: {
name: 'span',
classes: ['placeholder'],
},
model: (viewElement, { writer: modelWriter }) => {
// Extract the "name" from "{name}".
const name = viewElement.getChild(0).data.slice(1, -1);
return modelWriter.createElement('placeholder', { name });
},
});
conversion.for('editingDowncast').elementToElement({
model: 'placeholder',
view: (modelItem, { writer: viewWriter }) => {
const widgetElement = createPlaceholderView(modelItem, viewWriter);
// Enable widget handling on a placeholder element inside the editing view.
return toWidget(widgetElement, viewWriter);
},
});
conversion.for('dataDowncast').elementToElement({
model: 'placeholder',
view: (modelItem, { writer: viewWriter }) => createPlaceholderView(modelItem, viewWriter),
});
// Helper method for both downcast converters.
function createPlaceholderView(modelItem, viewWriter) {
const name = modelItem.getAttribute('name');
const placeholderView = viewWriter.createContainerElement('span', {
class: 'placeholder',
});
// Insert the placeholder name (as a text).
const innerText = viewWriter.createText('{' + name + '}');
viewWriter.insert(viewWriter.createPositionAt(placeholderView, 0), innerText);
return placeholderView;
}
}
}
component.ts
import { CdkDragDrop, moveItemInArray } from '@angular/cdk/drag-drop';
import { Component, OnInit } from '@angular/core';
import { ActivatedRoute, Router } from '@angular/router';
import { CKEditorUploadAdapter } from 'src/app/_helpers/ckeditor-upload-adapter';
import { ConferenceViewModelWithDetails, PaperAcknowledgementViewModel, PaperConfigurationViewModel } from 'src/app/_models/generatedModels';
import { AuthenticationService } from 'src/app/_services/authentication.service';
import { AdminCallForPapersService, OrganizationService } from 'src/app/_services/generatedServices';
import { ToasterService } from 'src/app/_services/toaster.service';
import * as customBuild from 'src/ckCustomBuild/ckeditor';
//import * as Placeholder from 'src/ckCustomBuild/ckeditor';
import * as Placeholder from 'src/ckCustomBuild/placeholder.plugin';
@Component({
selector: 'bxl-abstract-management-configuration',
templateUrl: 'abstract-management-configuration.component.html',
})
export class AbstractManagementConfigurationComponent implements OnInit {
initialized = false;
public Editor = customBuild;
htmlEditorConfig = {
plugins: [Placeholder],
toolbar: {
items: ['bold', 'italic', 'underline', 'link', 'alignment', 'bulletedList', 'numberedList', '|', 'fontFamily', 'fontSize', 'fontColor', 'fontBackgroundColor', '|', 'Paceholder'],
shouldNotGroupWhenFull: false,
},
// placeholderConfig: {
// types: ['date', 'color', 'first name', 'surname'],
// },
fontSize: {
options: [9, 11, 13, 'default', 17, 19, 21],
},
mediaEmbed: {
previewsInData: true,
},
};
organizationId: string;
conferenceId: any;
configuration: PaperConfigurationViewModel;
constructor(private auth: AuthenticationService, private orgService: OrganizationService, private router: Router, private route: ActivatedRoute, private cfpService: AdminCallForPapersService, private toaster: ToasterService) {}
ngOnInit(): void {
this.route.paramMap.subscribe((params) => {
this.organizationId = params.get('organizationId');
this.conferenceId = params.get('conferenceId');
this.cfpService.getPaperConfiguration(this.organizationId, this.conferenceId).subscribe((result) => {
this.configuration = result;
this.initialized = true;
});
});
}
I am using CKEditor5 within my angular application. I have generated a custom build and saved the ckeditor.js
file in my project dir. Everything works fine. I am able to render the editor and use it without issue in an angular component by adding this line to the top: import * as customBuild from 'src/ckCustomBuild/ckeditor';
Now I am going through the documentation for their sample "placeholder" plugin and I can't seem to get it to work in angular. I've added the same code from their site to a new file called placeholder.plugin.js
in the same folder as ckeditor.js
above.
I then import it into my component also, and structure the config as follows:
import * as customBuild from 'src/ckCustomBuild/ckeditor';
import * as Placeholder from 'src/ckCustomBuild/placeholder.plugin';
...component boilerplate code....
htmlEditorConfig = {
plugins: [Placeholder],
toolbar: {
items: ['bold', 'italic', 'underline', 'link', 'alignment', 'bulletedList', 'numberedList', '|', 'fontFamily', 'fontSize', 'fontColor', 'fontBackgroundColor', '|', 'Paceholder'],
shouldNotGroupWhenFull: false,
},
// placeholderConfig: {
// types: ['date', 'color', 'first name', 'surname'],
// },
fontSize: {
options: [9, 11, 13, 'default', 17, 19, 21],
},
mediaEmbed: {
previewsInData: true,
},
};
When I go to run the application, I get the following runtime error:
core.mjs:6494 ERROR Error: Uncaught (in promise): CKEditorError: plugincollection-plugin-not-found {"plugin":{}}
Read more: https://ckeditor.com/docs/ckeditor5/latest/framework/guides/support/error-codes.html#error-plugincollection-plugin-not-found
CKEditorError: plugincollection-plugin-not-found {"plugin":{}}
Read more: https://ckeditor.com/docs/ckeditor5/latest/framework/guides/support/error-codes.html#error-plugincollection-plugin-not-found
at p (ckeditor.js:5:432588)
at ckeditor.js:5:432433
at Array.forEach (<anonymous>)
at f (ckeditor.js:5:432420)
at wa.init (ckeditor.js:5:431516)
at bq.initPlugins (ckeditor.js:5:703694)
at ckeditor.js:5:831531
at new ZoneAwarePromise (zone.js:1351:1)
at bq.create (ckeditor.js:5:831488)
at CKEditorComponent.<anonymous> (ckeditor-ckeditor5-angular.js:235:1)
at resolvePromise (zone.js:1262:1)
at resolvePromise (zone.js:1216:1)
at zone.js:1329:1
at push.46026._ZoneDelegate.invokeTask (zone.js:443:1)
at Object.onInvokeTask (core.mjs:25595:1)
at push.46026._ZoneDelegate.invokeTask (zone.js:442:1)
at push.46026.Zone.runTask (zone.js:214:1)
at drainMicroTaskQueue (zone.js:632:1)
at push.46026.ZoneTask.invokeTask [as invoke] (zone.js:529:1)
at invokeTask (zone.js:1727:1)
Here is the contents of my 2 files:
placeholder.plugin.js
import * as Plugin from 'src/ckCustomBuild/ckeditor';
import * as Widget from 'src/ckCustomBuild/ckeditor';
import * as Command from 'src/ckCustomBuild/ckeditor';
import * as Collection from 'src/ckCustomBuild/ckeditor';
import * as Model from 'src/ckCustomBuild/ckeditor';
import * as toWidget from 'src/ckCustomBuild/ckeditor';
import * as viewToModelPositionOutsideModelElement from 'src/ckCustomBuild/ckeditor';
import * as addListToDropdown from 'src/ckCustomBuild/ckeditor';
import * as createDropdown from 'src/ckCustomBuild/ckeditor';
class Placeholder extends Plugin {
static get requires() {
return [PlaceholderEditing, PlaceholderUI];
}
}
class PlaceholderCommand extends Command {
execute({ value }) {
const editor = this.editor;
const selection = editor.model.document.selection;
editor.model.change((writer) => {
// Create a <placeholder> elment with the "name" attribute (and all the selection attributes)...
const placeholder = writer.createElement('placeholder', {
...Object.fromEntries(selection.getAttributes()),
name: value,
});
// ... and insert it into the document.
editor.model.insertContent(placeholder);
// Put the selection on the inserted element.
writer.setSelection(placeholder, 'on');
});
}
refresh() {
const model = this.editor.model;
const selection = model.document.selection;
const isAllowed = model.schema.checkChild(selection.focus.parent, 'placeholder');
this.isEnabled = isAllowed;
}
}
class PlaceholderUI extends Plugin {
init() {
const editor = this.editor;
const t = editor.t;
const placeholderNames = editor.config.get('placeholderConfig.types');
// The "placeholder" dropdown must be registered among the UI components of the editor
// to be displayed in the toolbar.
editor.ui.componentFactory.add('placeholder', (locale) => {
const dropdownView = createDropdown(locale);
// Populate the list in the dropdown with items.
addListToDropdown(dropdownView, getDropdownItemsDefinitions(placeholderNames));
dropdownView.buttonView.set({
// The t() function helps localize the editor. All strings enclosed in t() can be
// translated and change when the language of the editor changes.
label: t('Placeholder'),
tooltip: true,
withText: true,
});
// Disable the placeholder button when the command is disabled.
const command = editor.commands.get('placeholder');
dropdownView.bind('isEnabled').to(command);
// Execute the command when the dropdown item is clicked (executed).
this.listenTo(dropdownView, 'execute', (evt) => {
editor.execute('placeholder', { value: evt.source.commandParam });
editor.editing.view.focus();
});
return dropdownView;
});
}
}
function getDropdownItemsDefinitions(placeholderNames) {
const itemDefinitions = new Collection();
for (const name of placeholderNames) {
const definition = {
type: 'button',
model: new Model({
commandParam: name,
label: name,
withText: true,
}),
};
// Add the item definition to the collection.
itemDefinitions.add(definition);
}
return itemDefinitions;
}
class PlaceholderEditing extends Plugin {
static get requires() {
return [Widget];
}
init() {
console.log('PlaceholderEditing#init() got called');
this._defineSchema();
this._defineConverters();
this.editor.commands.add('placeholder', new PlaceholderCommand(this.editor));
this.editor.editing.mapper.on(
'viewToModelPosition',
viewToModelPositionOutsideModelElement(this.editor.model, (viewElement) => viewElement.hasClass('placeholder'))
);
this.editor.config.define('placeholderConfig', {
types: ['date', 'first name', 'surname'],
});
}
_defineSchema() {
const schema = this.editor.model.schema;
schema.register('placeholder', {
// Allow wherever text is allowed:
allowWhere: '$text',
// The placeholder will act as an inline node:
isInline: true,
// The inline widget is self-contained so it cannot be split by the caret and it can be selected:
isObject: true,
// The inline widget can have the same attributes as text (for example linkHref, bold).
allowAttributesOf: '$text',
// The placeholder can have many types, like date, name, surname, etc:
allowAttributes: ['name'],
});
}
_defineConverters() {
const conversion = this.editor.conversion;
conversion.for('upcast').elementToElement({
view: {
name: 'span',
classes: ['placeholder'],
},
model: (viewElement, { writer: modelWriter }) => {
// Extract the "name" from "{name}".
const name = viewElement.getChild(0).data.slice(1, -1);
return modelWriter.createElement('placeholder', { name });
},
});
conversion.for('editingDowncast').elementToElement({
model: 'placeholder',
view: (modelItem, { writer: viewWriter }) => {
const widgetElement = createPlaceholderView(modelItem, viewWriter);
// Enable widget handling on a placeholder element inside the editing view.
return toWidget(widgetElement, viewWriter);
},
});
conversion.for('dataDowncast').elementToElement({
model: 'placeholder',
view: (modelItem, { writer: viewWriter }) => createPlaceholderView(modelItem, viewWriter),
});
// Helper method for both downcast converters.
function createPlaceholderView(modelItem, viewWriter) {
const name = modelItem.getAttribute('name');
const placeholderView = viewWriter.createContainerElement('span', {
class: 'placeholder',
});
// Insert the placeholder name (as a text).
const innerText = viewWriter.createText('{' + name + '}');
viewWriter.insert(viewWriter.createPositionAt(placeholderView, 0), innerText);
return placeholderView;
}
}
}
Component.ts
import { CdkDragDrop, moveItemInArray } from '@angular/cdk/drag-drop';
import { Component, OnInit } from '@angular/core';
import { ActivatedRoute, Router } from '@angular/router';
import { CKEditorUploadAdapter } from 'src/app/_helpers/ckeditor-upload-adapter';
import { ConferenceViewModelWithDetails, PaperAcknowledgementViewModel, PaperConfigurationViewModel } from 'src/app/_models/generatedModels';
import { AuthenticationService } from 'src/app/_services/authentication.service';
import { AdminCallForPapersService, OrganizationService } from 'src/app/_services/generatedServices';
import { ToasterService } from 'src/app/_services/toaster.service';
import * as customBuild from 'src/ckCustomBuild/ckeditor';
//import * as Placeholder from 'src/ckCustomBuild/ckeditor';
import * as Placeholder from 'src/ckCustomBuild/placeholder.plugin';
@Component({
selector: 'bxl-abstract-management-configuration',
templateUrl: 'abstract-management-configuration.component.html',
})
export class AbstractManagementConfigurationComponent implements OnInit {
initialized = false;
public Editor = customBuild;
htmlEditorConfig = {
plugins: [Placeholder],
toolbar: {
items: ['bold', 'italic', 'underline', 'link', 'alignment', 'bulletedList', 'numberedList', '|', 'fontFamily', 'fontSize', 'fontColor', 'fontBackgroundColor', '|', 'Paceholder'],
shouldNotGroupWhenFull: false,
},
// placeholderConfig: {
// types: ['date', 'color', 'first name', 'surname'],
// },
fontSize: {
options: [9, 11, 13, 'default', 17, 19, 21],
},
mediaEmbed: {
previewsInData: true,
},
};
organizationId: string;
conferenceId: any;
configuration: PaperConfigurationViewModel;
constructor(private auth: AuthenticationService, private orgService: OrganizationService, private router: Router, private route: ActivatedRoute, private cfpService: AdminCallForPapersService, private toaster: ToasterService) {}
ngOnInit(): void {
this.route.paramMap.subscribe((params) => {
this.organizationId = params.get('organizationId');
this.conferenceId = params.get('conferenceId');
this.cfpService.getPaperConfiguration(this.organizationId, this.conferenceId).subscribe((result) => {
this.configuration = result;
this.initialized = true;
});
});
}
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(1)
在您的
loteholder.plugin.js
中,将以下内容放置在这些行中,而不是
指错误的路径,它必须是通往您
ckeditor.js
的路径。这样,您可以消除该文件中的模块加载错误。In your
placeholder.plugin.js
put the below content,Instead of these lines,
It was referring to a wrong path and it must be the path to your
ckeditor.js
. In this way you can eliminate the module load error in that file.