Gurnzbot
Gurnzbot

Reputation: 4132

Ckeditor usage in Ember

I want to use CKEditor with my Ember app. I am 100% a n00b with Ember, but I'm getting there.

I have tried my darndest to figure this out, but I've gotten nowhere :(

I have tried to use ember-ckeditor. This ended up with the editor throwing a bunch of net::ERR_NAME_NOT_RESOLVED errors for things such as config.js and other "assets" it expected to find in the assets folder.

I have tried ember-cli-ckeditor. Same exact issues as above.

These two addons have pretty lame documentation. For example, I have no idea how provide a custom config file, CSS, etc. Or what if I want to use CkFinder?

The two above addons also throw some depreciated warnings when loading up the server, but I disgress....

I finally tried to manually include ckeditor v4.5.6 in the vendor folder. I then included in ember-cli-build.js as such: app.import('vendor/ckeditor/ckeditor.js'); I'm not sure if I'm correct in doing this, and if so, how do I include use the editor plugin within my controller or component?

CKEDITOR.replace("content"); as per usual outside of Ember?

Please school me!

Upvotes: 1

Views: 892

Answers (2)

Gennady Dogaev
Gennady Dogaev

Reputation: 5991

To use ckeditor without addons (creating your own component):

  1. Install ckeditor using bower:

    bower install ckeditor --save
    
  2. Install broccoli-funnel, you will need it for ckeditor's assets:

    npm install broccoli-funnel --save-dev
    
  3. In your ember-cli-build.js:

    At the top of file requere funnel

    var Funnel = require('broccoli-funnel');
    

    In app's options exclude ckeditor's assets from fingerprinting:

    var app = new EmberApp(defaults, {
      fingerprint: {
        exclude: ['assets/ckeditor/']
      }
    });
    

    Import ckeditor's js and assets:

    app.import('bower_components/ckeditor/ckeditor.js');
    
    var ckeditorAssets = new Funnel('bower_components/ckeditor', {
      srcDir: '/',
      destDir: '/assets/ckeditor'
    });
    
    /**
     * If you need to use custom skin, put it into 
     * vendor/ckeditor/skins/<skin_name>
     * Also, custom plugins may be added in this way 
     * (look ckeditor's info for details)
     * If you don't need custom skins, you may remove
     * ckeditorCustoms
     */
    var ckeditorCustoms = new Funnel('vendor/ckeditor', {
      srcDir: '/',
      destDir: '/assets/ckeditor'
    });
    
    return app.toTree([ckeditorAssets, ckeditorCustoms]);
    
  4. If your app is not in website's root, you may need to put this script in body section of index.html, before other scripts:

    <script type="text/javascript">
      window.CKEDITOR_BASEPATH = '/path-to/assets/ckeditor/';
    </script>
    
  5. Create a component. Warning: this is a code from my abandoned pet project, and I'm 99% sure that it will not work for you "as is" because of missing dependencies and because it was created for different html layout. But I think it may help anyway. If you wish to try and copy-paste it, here are dependencies:

    npm install --save-dev ember-browserify
    npm install --save-dev sanitize-html
    

    Component's code:

    /* globals CKEDITOR */
    import Ember from 'ember';
    import layout from '../templates/components/md-ckeditor'; //component's name!
    import SanitizeHTML from 'npm:sanitize-html';
    
    export default Ember.Component.extend({
      layout: layout,
      classNames: ['input-field'],
    
      _editor: null,
    
      bindAttributes: ['disabled', 'readonly', 'autofocus'],
      validate: false,
    
      errorsPath: 'errors',
    
      init() {
        this._super(...arguments);
        const propertyPath = this.get('valueBinding._label');
        if (Ember.isPresent(propertyPath)) {
          Ember.Binding.from(`targetObject.${this.get('errorsPath')}.${propertyPath}`)
            .to('errors')
            .connect(this);
        }
      },
    
      didInsertElement() {
        var i18n = this.get('i18n');
    
        if (Ember.isPresent(this.get('icon'))) {
          this.$('> span').css('padding-left', '3rem');
        }
    
        this._setupLabel();
    
        this._editor = CKEDITOR.inline(this.element.querySelector('.ckeditor'), {
          skin: 'minimalist',
          toolbar: [
            ['Cut', 'Copy', 'Paste', 'PasteText', 'PasteFromWord'],
            ['Undo', 'Redo'],
            ['Bold', 'Italic', 'Strike'],
            ['Link', 'Unlink'],
            ['NumberedList', 'BulletedList', 'Blockquote'],
            ['Source']
          ],
          linkShowAdvancedTab: false,
          linkShowTargetTab: false,
          language: i18n.get('locale'),
          removePlugins: 'elementspath'
        });
        this._editor.on('instanceReady', (e) => {
          this._updateValidClass();
        });
        this._editor.on('change', (e) => {
          this.set('value', e.editor.getData());
        });
        this._editor.on('focus', (e) => {
          var label = this.$('> label, > i');
          label.addClass('active');
        });
        this._editor.on('blur', (e) => {
          var label = this.$('> label, > i');
          var text  = SanitizeHTML(e.editor.getData(), {
            allowedTags: []
          }).trim();
          if (text !== '') {
            label.addClass('active');
          } else {
            label.removeClass('active');
          }
        });
      },
    
      willDestroyElement()
      {
        this._editor.destroy();
        this._editor = null;
      },        
    
      id: Ember.computed('elementId', function () {
        return `${this.get('elementId')}-input`;
      }),
    
      validClass: Ember.computed('value', 'errors', function () {
        var errors = this.get('errors');
        if (errors && errors.get && errors.get('firstObject')) {
          return 'invalid';
        } else if (!!this.get('value')) {
          return 'valid';
        } else {
          return '';
        }
      }),
    
      validClassChanged: Ember.observer('validClass', function () {
        Ember.run.once(this, '_updateValidClass');
      }),
    
      _updateValidClass() {
        if (this._editor && this._editor.container &&         this._editor.container.$) {
          Ember.$(this._editor.container.$).removeClass('invalid         valid').addClass(this.get('validClass'));
        }
      },
    
      _setupLabel() {
        const label = this.$('> label, > i');
        if (Ember.isPresent(this.get('value'))) {
          label.addClass('active');
        }
      }
    });
    

    Template:

    {{textarea
    id=id
    value=value
    name=name
    required=required
    readonly=readonly
    disabled=disabled
    maxlength=maxlength
    class="materialize-textarea ckeditor"
    classNameBindings="validate:validate: validClass"
    }}
    <label for="{{id}}">{{label}}</label>
    <small class="red-text">
         {{#if errors}} {{errors.firstObject}} {{else}} &nbsp; {{/if}}
    </small>
    

Upvotes: 4

Related Questions