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 MEAN Stack CMS 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'. To see how to handle image uploads on the server side from CKEditor using the MEAN Stack you can check out MEAN Stack Image Uploads from CKEditor.
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>
Need Some AngularJS Help?
Search fiverr for freelance AngularJS developers.
Follow me for updates
When I'm not coding...
Me and Tina are on a motorcycle adventure around Australia.
Come along for the ride!