I currently have 2 directives:
- An editor directive that creates a textbox and a toolbar div.
- A bold button which is transcluded into the editor
Markup
<editor content="text">
<bold-button>B</bold-button>
</editor>
Editor directive
.directive('editor', function () {
return {
restrict: 'E',
replace: true,
transclude: true,
scope: {
'content': '='
},
template: '<div class="editor">' +
'<div class="toolbar" ng-transclude></div>' +
'<textarea ng-model="content"></textarea>' +
'</div>',
controller: function ($scope, $element, $attrs) { },
link: function (scope, elem, attrs, ctrl) {
var editor = new Editor();
editor.onPostUpdateDom = function () {
scope.content = elem.find('textarea').val();
};
// Expose editor to other directives
ctrl.editor = editor;
};
});
Bold button directive
.directive('boldButton', function () {
return {
require: '^editor',
restrict: 'E',
replace: true,
transclude: true,
scope: {},
template: '<button type="button" ng-click="format()" ng-transclude>'+
'</button>',
link: function (scope, elem, attrs, editorCtrl) {
scope.format = function () {
editorCtrl.editor.formatCommand("bold");
};
};
});
The editor directive uses a third party plugin that provides a formatCommand()
method which changes the textarea value.
The bold button directive fires this method via the editor's controller.
Now, every time the plugin changes the DOM it raises an onPostUpdateDOM
event which I use to grab the new value and assign it to scope within the editor directive:
scope.content = elem.find('textarea').val();
This works really well. The button is pressed and the values changes.
However the plugin also provides DOM manipulation via keyboard shortcuts. When the DOM is changed the scope.content = elem.find('textarea').val();
line doesn't work because it occurred outside of Angular. Wrapping that line in an $apply
works but then the formatCommand()
call from the button directive throws an "apply is already in progress" error.
Is this a case for a "safe apply" anti-pattern?!