A few days ago, I was playing with CKEditor 4, inline mode. I created a knockoutJS bindling handler to test see if it was possible to use CKEditor in SPA type of applications, utilizing knockoutJS.
This time, I am trying it out with angularJS. I am rather new to angularJS, so my codes might not be optimal, worse case, it could be buggy uggh!!!
In angularJS jargon, knockoutJS’s bindling handlers loosely correlates to directives (IMHO). The documentation on angularJS is rather, a work in progress, to put it lightly, compared to knockoutJS’s docs. But nevertheless, you can dig around and try it!
I’m not going to illustrate here how you start with angularJS, checkout their awesome tutorial.
The View
<!DOCTYPE html> <html ng-app="ckApp"> <head> <title></title> <style type="text/css"> pre { white-space: pre-wrap; /* css-3 */ white-space: -moz-pre-wrap; /* Mozilla, since 1999 */ white-space: -pre-wrap; /* Opera 4-6 */ white-space: -o-pre-wrap; /* Opera 7 */ word-wrap: break-word; /* Internet Explorer 5.5+ */ } [ng\:cloak], [ng-cloak], .ng-cloak { display: none !important; } </style> </head> <body ng-controller="CkController"> <h1>CKEditor And knockoutJS</h1> <h2>Raw Data</h2> <pre ng-cloak>{{body}}</pre> <h2>Editor</h2> <div ckedit="body" ></div> <button ng-click="change()" > Test Change from outside </button> <script src="ckeditor/ckeditor.js"></script> <script src="Scripts/angular.js"></script> <script src="Scripts/angular-sanitize.js"></script> <script src="App/app.js"></script> <script src="App/CkController.js"></script> <script src="App/ckdirective.js"></script> </body> </html>
Pay attention to the line:
<div ckedit="body" ></div>
We declared a directive here (attribute style) named ckedit. You can name it however you wish it to be that would make sense to you and your project. It has been “bound” to our scope property named “body”. For “debugging” purposes, I also bounded “body” to a pre tag above the ckeditor element.
The controller
Now let’s look at the controller. It is rather simple for our purpose of illustration. Pretty straightforward eh?
'use strict'; ckApp.controller('CkController', function CkController($scope) { $scope.body = '<h1>Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.</h1>'; $scope.change = function () { $scope.body = '<h2>Changed</h2>'; }; } );
The CKEditor 4 inline mode directive
"use strict"; ckApp.directive('ckedit', function ($parse) { CKEDITOR.disableAutoInline = true; var counter = 0, prefix = '__ckd_'; return { restrict: 'A', link: function (scope, element, attrs, controller) { var getter = $parse(attrs.ckedit), setter = getter.assign; attrs.$set('contenteditable', true); // inline ckeditor needs this if (!attrs.id) { attrs.$set('id', prefix + (++counter)); } // CKEditor stuff // Override the normal CKEditor save plugin CKEDITOR.plugins.registered['save'] = { init: function (editor) { editor.addCommand('save', { modes: { wysiwyg: 1, source: 1 }, exec: function (editor) { if (editor.checkDirty()) { var ckValue = editor.getData(); scope.$apply(function () { setter(scope, ckValue); }); ckValue = null; editor.resetDirty(); } } } ); editor.ui.addButton('Save', { label: 'Save', command: 'save', toolbar: 'document' }); } }; var options = {}; options.on = { blur: function (e) { if (e.editor.checkDirty()) { var ckValue = e.editor.getData(); scope.$apply(function () { setter(scope, ckValue); }); ckValue = null; e.editor.resetDirty(); } } }; options.extraPlugins = 'sourcedialog'; options.removePlugins = 'sourcearea'; var editorangular = CKEDITOR.inline(element[0], options); //invoke scope.$watch(attrs.ckedit, function (value) { editorangular.setData(value); }); } } });
angularJS directive ephipanies
I was playing between scope.$watch vs attr.$observe. When I used $observe, I had to declare my markup as
<div ckedit="{{body}}" ></div>
instead of the one shown above. The syntax for “observing” is
attrs.$observe('ckedit', function (value) { editorangular.setData(value); });
The getter and setter stuff would have to go, and you won’t need the $parser service. Also, setting the data to the editor needs to change to:
scope.$apply(function () { scope.body = ckValue; });
I didn’t like the “scope.body” line because that would make this directive non-generic. Or maybe there’s a better way to do that – which I don’t know yet.
Long story short, fork it over at Github ….