Angular: WYSIWYG Widget

An example of how to properly integrate a WYSIWYG like TinyMCE into AngularJS

HTML

<script src="http://resources.holycrap.ws/jscripts/tiny_mce/jquery.tinymce.js"></script>
<script src="http://code.angularjs.org/0.10.5/angular-0.10.5.js" ng:autobind></script>
<div ng:controller="Tester">
    
    <a ng:click="newBox()">Add New</a>
    
    <h2>Scroll Down!</h2>
    <div class="clear">
        <div class="boxes">
            <div ng:repeat="box in boxes">
                <h1>{{box.name}}</h1>
                <textarea ui:tinymce ng:model="box.content"></textarea>
            </div>
        </div>
        <h1>Dynamic Data:</h1>
        <p>Uses <a href="http://www.tinymce.com/wiki.php/Configuration:onchange_callback">onchange_callback</a> which fires less often.</p>
        <pre>{{boxes}}</pre>
    </div>
    <div class="boxes">
        <h1>Fixed</h1>
        <textarea ui:tinymce="fast" ng:model="content"></textarea>
    </div>
    <h1>Fixed Data:</h1>
    <p>Uses <a href="http://www.tinymce.com/wiki.php/Configuration:handle_event_callback">handle_event_callback</a> which fires more often.</p>
    <pre>{{content}}</pre>
</div>

CSS

h1 { margin: 10px 0 4px; font-weight: bold; }
h2 { color: red; }
p { font-size: 80%; }
body { font-family: helvetica }
.boxes { float: left; margin-right: 10px; }
.clear { overflow: auto; }

JavaScript

function Tester() {
    var scope = this;
    scope.boxes = [{name: 'First'},{name:'Second'}];
    
    scope.newBox = function(){
        scope.boxes.push({name:'new'});
    }
}

// --- Custom TinyMCE Service --- //
angular.directive('ui:tinymce', function(expression, config) {
        
        
    return function(element) {
        if (expression === 'fast') {
            element.tinymce({
                // Location of TinyMCE script
                script_url: 'http://tinymce.cachefly.net/4.0/tinymce.min.js',
    
                // General options
                theme: "simple",
                
                // Update Textarea and Trigger change event
                handle_event_callback: function(e) {
                    if (this.isDirty()) {
                        this.save();
                        element.trigger('change');
                    }
                    return true;
                }
            });
        } else {
            element.tinymce({
                // Location of TinyMCE script
                script_url: 'http://resources.holycrap.ws/jscripts/tiny_mce/tiny_mce.js',
    
                // General options
                theme: "simple",
    
                // Update Textarea and Trigger change event
                onchange_callback: function(inst) {
    
                    if (inst.isDirty()) {
                        inst.save();
                        element.trigger('change');
                    }
                    return true; // Continue handling
                }
            });
        }
    };
});