【问题标题】:CKEditor Drag and drop with Angular 7CKEditor 使用 Angular 7 拖放
【发布时间】:2019-06-08 10:22:39
【问题描述】:

我想使用 CKEditor 并使用 Angular 7 拖放。他们在他们的网站上成功地做到了,但我似乎找不到任何 Angular 解决方案。正如你在这里看到的:

https://ckeditor.com/docs/ckeditor4/latest/examples/draganddrop.html

但我无法将其转换为 Angular 7 组件。我的代码是:

import { Component, OnInit } from '@angular/core';
import ClassicEditor from '@ckeditor/ckeditor5-build-classic';

@Component({
  selector: 'xxxx',
  templateUrl: 'xxxxx',
  styleUrls: ['xxxxx']
})
export class NyttInnholdComponent implements OnInit {

  public editor = ClassicEditor;

  public contacts = [];

  public model = {
        editorData: '<p>Tommy says, Hello!</p>'
    };

  constructor() { 
    this.contacts = [{
        name: 'Huckleberry Finn',
        tel: '+48 1345 234 235',
        email: 'h.finn@example.com',
        avatar: 'hfin'
      },
      {
        name: 'D\'Artagnan',
        tel: '+45 2345 234 235',
        email: 'dartagnan@example.com',
        avatar: 'dartagnan'
      },
      {
        name: 'Phileas Fogg',
        tel: '+44 3345 234 235',
        email: 'p.fogg@example.com',
        avatar: 'pfog'
      },
      {
        name: 'Alice',
        tel: '+20 4345 234 235',
        email: 'alice@example.com',
        avatar: 'alice'
      },
      {
        name: 'Little Red Riding Hood',
        tel: '+45 2345 234 235',
        email: 'lrrh@example.com',
        avatar: 'lrrh'
      }
    ];


  }

  ngOnInit() {
    //this.teswt ();
    this.editor.disableAutoInline = true;


    this.editor.plugins.add('hcard', {
      requires: 'widget',

      init: function(editor) {
        editor.widgets.add('hcard', {
          allowedContent: 'span(!h-card); a[href](!u-email,!p-name); span(!p-tel)',
          requiredContent: 'span(h-card)',
          pathName: 'hcard',

          upcast: function(el) {
            return el.name == 'span' && el.hasClass('h-card');
          }
        });

        // This feature does not have a button, so it needs to be registered manually.
        editor.addFeature(editor.widgets.registered.hcard);

        // Handle dropping a contact by transforming the contact object into HTML.
        // Note: All pasted and dropped content is handled in one event - editor#paste.
        editor.on('paste', function(evt) {
          var contact = evt.data.dataTransfer.getData('contact');
          if (!contact) {
            return;
          }

          evt.data.dataValue =
            '<span class="h-card">' +
            '<a href="mailto:' + contact.email + '" class="p-name u-email">' + contact.name + '</a>' +
            ' ' +
            '<span class="p-tel">' + contact.tel + '</span>' +
            '</span>';
        });
      }
    });

    this.editor.on('instanceReady', function() {
      // When an item in the contact list is dragged, copy its data into the drag and drop data transfer.
      // This data is later read by the editor#paste listener in the hcard plugin defined above.
      this.editor.document.getById('contactList').on('dragstart', function(evt) {
        // The target may be some element inside the draggable div (e.g. the image), so get the div.h-card.
        var target = evt.data.getTarget().getAscendant('div', true);

        // Initialization of the CKEditor data transfer facade is a necessary step to extend and unify native
        // browser capabilities. For instance, Internet Explorer does not support any other data type than 'text' and 'URL'.
        // Note: evt is an instance of CKEDITOR.dom.event, not a native event.
        this.editor.plugins.clipboard.initDragDataTransfer(evt);

        var dataTransfer = evt.data.dataTransfer;

        // Pass an object with contact details. Based on it, the editor#paste listener in the hcard plugin
        // will create the HTML code to be inserted into the editor. You could set 'text/html' here as well, but:
        // * It is a more elegant and logical solution that this logic is kept in the hcard plugin.
        // * You do not know now where the content will be dropped and the HTML to be inserted
        // might vary depending on the drop target.
        dataTransfer.setData('contact', this.contacts[target.data('contact')]);

        // You need to set some normal data types to backup values for two reasons:
        // * In some browsers this is necessary to enable drag and drop into text in the editor.
        // * The content may be dropped in another place than the editor.
        dataTransfer.setData('text/html', target.getText());

        // You can still access and use the native dataTransfer - e.g. to set the drag image.
        // Note: IEs do not support this method... :(.
        if (dataTransfer.$.setDragImage) {
          dataTransfer.$.setDragImage(target.findOne('img').$, 0, 0);
        }
      });
    });

    // Initialize the editor with the hcard plugin.
    this.editor.inline('editor1', {
      extraPlugins: 'hcard,sourcedialog,justify'
    });

  }

}

这样使用:

<ckeditor [(ngModel)]="model.editorData" [editor]="editor"></ckeditor>

如果我尝试这样做,this.editor 将不会包含我期望的任何属性。您可以在这里找到:https://ckeditor.com/docs/ckeditor5/latest/api/module_editor-classic_classiceditor-ClassicEditor.html

那么,有谁知道我如何让 Angular 与 Angular 7 和 CKEditor 一起工作?

【问题讨论】:

  • 不确定使用 cdk-drag-and-drop 是否真的可行/实用。默认情况下,您将项目放入 cdkDropLists,但 cdkDropLists 看起来与此编辑器完全不同。我会复制他们使用的相同方法。记住“javascript 是有效的打字稿”。
  • 请注意,您使用 CKEditor 5 以及 CKEditor 4 的插件和 API。这将不起作用。
  • 嗯,我知道了……现在。但当时没有;)。我只需要通过 CDN 调用 CKEditor 4 并在控制器中进行一些小的更改以使其工作。太糟糕了,他们没有合适的 Angular 2+ 拖放解决方案。现在它是一个“黑客”。

标签: javascript angular drag-and-drop ckeditor angular7


【解决方案1】:

是的,您可以使用 Angular 7 和 CKEditor5 以及自定义指令。 您可以创建一个 Drag Directive 并将数据“拖放”到编辑器文档中。

请参阅下面的拖动指令示例: https://threeventures.com/build-html-drag-drop-angular/ https://github.com/ThreeVenturesTechnology/angular-drag-and-drop

【讨论】:

    【解决方案2】:

    有两种选择:

    1. 使用 div 代替 Angular CKEditor 控件。
    2. 将您的 CKEditor 绑定到内置的拖放事件。

    选项 1 - 使用 div 而不是 Angular CKEditor 控件。

    您的模板看起来像这样。

    <div cols="10" id="editor1" name="editor1" rows="10" data-sample-short contenteditable="true" >
    </div>
    

    那么你的打字稿会看起来像

      ngOnInit(): void {
    
        CKEDITOR.plugins.add('hcard', {
          requires: 'widget',
    
          init(editor): void {
            editor.widgets.add('hcard', {
              allowedContent: 'span(!h-card); a[href](!u-email,!p-name); span(!p-tel)',
              requiredContent: 'span(h-card)',
              pathName: 'hcard',
    
              upcast: (el) => {
                return el.name === 'span' && el.hasClass('h-card');
              }
            });
    
            // This feature does not have a button, so it needs to be registered manually.
            editor.addFeature(editor.widgets. registered.hcard);
    
            // Handle dropping a contact by transforming the contact object into HTML.
            // Note: All pasted and dropped content is handled in one event - editor#paste.
            editor.on('paste', (evt) => {
              const contact = evt.data.dataTransfer.getData('contact');
              if (!contact) {
                return;
              }
    
              evt.data.dataValue =
                '<span class="h-card">' +
                '<a href="mailto:' + contact.email + '" class="p-name u-email">' + contact.name + '</a>' +
                ' ' +
                '<span class="p-tel">' + contact.tel + '</span>' +
                '</span>';
            });
          }
        });
    
        CKEDITOR.replace( 'editor1', {
          extraPlugins: 'hcard, sourcedialog',
            toolbar: [
              { name: 'basicstyles', items: [ 'Bold', 'Italic' ] },
              { name: 'clipboard', items: [ 'Cut', 'Copy', 'Paste', 'PasteText', '-', 'Undo', 'Redo' ] },
              { name: 'document', items: ['Source'] }
            ],
            allowedContent: true,
            fullPage: true,
            height: '500px'
        });
    
        CKEDITOR.on('instanceReady', (event) => {
          // When an item in the contact list is dragged, copy its data into the drag and drop data transfer.
          // This data is later read by the editor#paste listener in the hcard plugin defined above.
          CKEDITOR.document.getById('contactList').on('dragstart', (evt) => {
            // The target may be some element inside the draggable div (e.g. the image), so get the div.h-card.
            const target = evt.data.getTarget().getAscendant('div', true);
    
            // Initialization of the CKEditor 4 data transfer facade is a necessary step to extend and unify native
            // browser capabilities. For instance, Internet Explorer does not support any other data type than 'text' and 'URL'.
            // Note: evt is an instance of CKEDITOR.dom.event, not a native event.
            CKEDITOR.plugins.clipboard.initDragDataTransfer(evt);
    
            const dataTransfer = evt.data.dataTransfer;
    
            // Pass an object with contact details. Based on it, the editor#paste listener in the hcard plugin
            // will create the HTML code to be inserted into the editor. You could set 'text/html' here as well, but:
            // * It is a more elegant and logical solution that this logic is kept in the hcard plugin.
            // * You do not know now where the content will be dropped and the HTML to be inserted
            // might vary depending on the drop target.
            dataTransfer.setData('contact', this.CONTACTS[target.data('contact')]);
    
            // You need to set some normal data types to backup values for two reasons:
            // * In some browsers this is necessary to enable drag and drop into text in the editor.
            // * The content may be dropped in another place than the editor.
            dataTransfer.setData('text/html', target.getText());
    
            // You can still access and use the native dataTransfer - e.g. to set the drag image.
            // Note: IEs do not support this method... :(.
            if (dataTransfer.$.setDragImage) {
              dataTransfer.$.setDragImage(target.findOne('img').$, 0, 0);
            }
          });
    
        });
      }
    

    源代码(Angular 10):https://github.com/steve-giles/ckeditorApp

    选项 2 - 将 CKEditor 绑定到内置的拖放事件。

    这种方法的问题是您只能在源视图中执行拖放操作。

    您的模板看起来像这样。

    <ckeditor [(ngModel)]="model.editorData" [editor]="editor" (drop)="drop($event)"
              (dragover)="allowDrop($event)"></ckeditor>
    

    然后在您的打字稿中添加拖放代码,其中“文本”是对您拖动的元素的引用。

      allowDrop(ev): void {
        ev.preventDefault();
      }
    
      drag(ev): void {
        ev.dataTransfer.setData('text', ev.target.id);
      }
    

    最后添加代码以使用您的文本更新 CKEditor。在此示例中,我使用“getElementById”,但更好的方法是使用 Angular 绑定。请注意,要使其正常工作,用户需要在编辑器中将焦点设置到元素将被放置的位置。

      drop(ev): void {
        ev.preventDefault();
        var data = ev.dataTransfer.getData("text");
        var newValue = document.getElementById(data).textContent;
    
        var startPos = ev.target.selectionStart;
        var endPos = ev.target.selectionEnd;
    
        ev.target.value = ev.target.value.substring(0, startPos)
          + newValue 
          + ev.target.value.substring(endPos, ev.target.value.length);
      }
    

    源代码(Angular 10):https://github.com/steve-giles/draganddrop

    【讨论】:

      【解决方案3】:

      经过大量工作和文档搜索。使用 Ckeditor5 解耦文档编辑器和 Angular 10 实现的最简单的外部文本/html 拖放方法。下面的示例和参考链接Where are the editor.insertHtml() and editor.insertText() methods? How to insert some content?

      app.module

      import { BrowserModule } from '@angular/platform-browser';
      import { NgModule } from '@angular/core';
      
      import { AppComponent } from './app.component';
      import { CKEditorModule } from '@ckeditor/ckeditor5-angular';
      
      @NgModule({
        declarations: [AppComponent],
        imports: [BrowserModule, CKEditorModule],
        providers: [],
        bootstrap: [AppComponent],
      })
      export class AppModule {}
      

      app.component.ts

      import { Component } from '@angular/core';
      import * as DecoupledEditor from '@ckeditor/ckeditor5-build-decoupled-document';
      
      @Component({
        selector: 'app-root',
        templateUrl: './app.component.html',
        styleUrls: ['./app.component.css'],
      })
      export class AppComponent {
        public Editor = DecoupledEditor;
        editorInstance;
        public data = {
          editorData: '<p>Hello, world!</p>',
        };
      
        fieldMap: Map<string, string> = new Map<string, string>([
          ['studentFirstName', '{{ student.firstName }}'],
          ['studentLastName', '{{ student.lastName }}'],
          ['courseName', '{{ course.name }}'],
          ['courseHours', '{{ course.hours }}'],
        ]);
      
        constructor() {}
      
        ngOnInit() {}
      
        public onReady(editor) {
          editor.ui
            .getEditableElement()
            .parentElement.insertBefore(
              editor.ui.view.toolbar.element,
              editor.ui.getEditableElement()
            );
          this.editorInstance = editor;
        }
      
        allowDrop(ev): void {
          ev.preventDefault();
        }
      
        drag(ev): void {
          console.log('drage', ev.target);
          ev.dataTransfer.setData('dragElement', ev.target.id);
        }
      
        drop(ev): void {
          ev.preventDefault();
          const data = ev.dataTransfer.getData('dragElement');
          const dataValue = this.fieldMap.get(data);
          const viewFragment = this.editorInstance.data.processor.toView(dataValue);
          const modelFragment = this.editorInstance.data.toModel(viewFragment);
          this.editorInstance.model.insertContent(modelFragment);
        }
      }
      

      app.component.html

      <ckeditor [editor]="Editor"  [data]="data.editorData" (ready)="onReady($event)"
                (drop)="drop($event)"
                (dragover)="allowDrop($event)">
      </ckeditor>
      
      Student
      <ul>
        <li id="studentFirstName" draggable="true" (dragstart)="drag($event)">First name</li>
        <li id="studentLastName" draggable="true" (dragstart)="drag($event)">Last name</li>
      </ul>
      
      Course
      <ul>
        <li id="courseName" draggable="true" (dragstart)="drag($event)">Name</li>
        <li id="courseHours" draggable="true" (dragstart)="drag($event)">Hours</li>
      </ul>
      

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2019-05-05
        • 2019-04-02
        • 2019-04-13
        • 2019-11-02
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2019-05-09
        相关资源
        最近更新 更多