May 26 2016

Angular - CKEditor Directive with ngModel Binding

If you've tried plugging CKEditor into an angularjs application you may have run into the same issue I did, that it doesn't play nicely with the ng-model directive. If you run ckeditor on a textarea element that is bound to an angular object using ng-model, the object isn't automatically updated with the content of the wysiwyg editor, it's left blank/empty.

In order to successfully bind the model variable to the ckeditor instance you can subscribe to the 'change' event of ckeditor and update the ngModel each time a change happens, like this:

// update ngModel on change
ckeditor.editor.on('change', function () {
	ngModel.$setViewValue(this.getData());
});


Below is a complete directive I created for a recent project that works with ngModel binding and includes a couple of different toolbar configuration options for either a minimal or regular sized editor.

NOTE: I'm using the jQuery adapter to initialize ckeditor so jQuery is a dependency for this directive to work. I'm a fan of jQuery but if you're not it wouldn't be difficult to tweak the code to use the CKEDITOR.replace() function instead, you can find details on the CKEditor quick start guide.

The directive also includes a couple of extra plugins that I purchased to enable automatic uploading of images pasted from word, to remove these just comment out the line containing: extraPlugins: 'simpleuploads,imagesfromword'.

AngularJS CKEditor Directive

(function () {
    'use strict';

    angular
        .module('app')
        .directive('ckeditor', Directive);

    function Directive($rootScope) {
        return {
            require: 'ngModel',
            link: function (scope, element, attr, ngModel) {
                var editorOptions;
                if (attr.ckeditor === 'minimal') {
                    // minimal editor
                    editorOptions = {
                        height: 100,
                        toolbar: [
                            { name: 'basic', items: ['Bold', 'Italic', 'Underline'] },
                            { name: 'links', items: ['Link', 'Unlink'] },
                            { name: 'tools', items: ['Maximize'] },
                            { name: 'document', items: ['Source'] },
                        ],
                        removePlugins: 'elementspath',
                        resize_enabled: false
                    };
                } else {
                    // regular editor
                    editorOptions = {
                        filebrowserImageUploadUrl: $rootScope.globals.apiUrl + '/upload',
                        removeButtons: 'About,Form,Checkbox,Radio,TextField,Textarea,Select,Button,ImageButton,HiddenField,Save,CreateDiv,Language,BidiLtr,BidiRtl,Flash,Iframe,addFile,Styles',
                        extraPlugins: 'simpleuploads,imagesfromword'
                    };
                }

                // enable ckeditor
                var ckeditor = element.ckeditor(editorOptions);

                // update ngModel on change
                ckeditor.editor.on('change', function () {
                    ngModel.$setViewValue(this.getData());
                });
            }
        };
    }
})();

 

Example Usage of CKEditor Directive in HTML

<!-- regular wysiwyg editor -->
<textarea ng-model="vm.article.Body" ckeditor></textarea>

<!-- minimal wysiwyg editor -->
<textarea ng-model="vm.article.Body" ckeditor="minimal"></textarea>

 

CKEditor Directive Script Dependencies

These are the CKEditor scripts required for the directive to work, I'm including the main scripts (ckeditor + jquery adapter) from a CDN, I prefer to use CDNs where possible to improve performance and declutter my own project. I'm also including a couple of extra plugins that I purchased to enable pasting images from word, you can simply remove them if you don't use these plugins, or if you're interested in them you can find more info here.

<script src="//cdn.ckeditor.com/4.5.7/full/ckeditor.js"></script>
<script src="//cdn.ckeditor.com/4.5.7/full/adapters/jquery.js"></script>
<script>
    // load extra ckeditor plugins
    CKEDITOR.plugins.addExternal('simpleuploads', '/js/ckeditor/plugins/simpleuploads/plugin.js');
    CKEDITOR.plugins.addExternal('imagesfromword', '/js/ckeditor/plugins/imagesfromword/plugin.js');
</script>

 

AngularJS Consulting and Web Development in Sydney

If you need a hand with your AngularJS project, feel free to drop me a line, I provide web development or AngularJS consulting services in Sydney Australia, I also provide remote consulting services for clients outside Sydney.


Sponsored by