I wrote a directive for redactor (a wysiwyg editor). It works after some hacking but I would like find out the right way. The main challenge for me is the bi-directional binding between the ng-model and the redactor jquery plugin. I listen to keyup and command event from the wysiwyg editor and update the model. I also watch for model change from outside the redactor editor so that I can update the redactor editor accordingly. The tricky part is: How do I ignore the ng-model change imposed by the reactor editor (from the first half of the binding)?
In the following code, it remembers the last value the redactor editor updates to the model and ignore model change if the new value of the model equals to that last value. I am really not sure if this is the right way to achieve this. It seems to me that this is a common problem with bi-directional binding in Angular and there must be a right way. Thanks!
<textarea ui-redactor='{minHeight: 500}' ng-model='content'></textarea>
directive.coffee (sorry for the coffeescript)
angular.module("ui.directives").directive "uiRedactor", ->
require: "ngModel"
link: (scope, elm, attrs, ngModel) ->
redactor = null
updatedVal = null
updateModel = ->
ngModel.$setViewValue updatedVal = elm.val()
scope.$apply()
options =
execCommandCallback: updateModel
keydownCallback: updateModel
keyupCallback: updateModel
optionsInAttr = if attrs.uiRedactor then scope.$eval(attrs.uiRedactor) else {}
angular.extend options, optionsInAttr
setTimeout ->
redactor = elm.redactor options
#watch external model change
scope.$watch attrs.ngModel, (newVal) ->
if redactor? and updatedVal isnt newVal
redactor.setCode(ngModel.$viewValue or '')
updatedVal = newVal
Mark Rajcok gave the solution (Thank you!) The trick is to use ngModel.$render() rather than $watch().
use
ngModel.$render = ->
redactor?.setCode(ngModel.$viewValue or '')
instead of
scope.$watch attrs.ngModel, (newVal) ->
if redactor? and updatedVal isnt newVal
redactor.setCode(ngModel.$viewValue or '')
updatedVal = newVal