Custom bindings are implemented inside the calculated observable data, so dependencies are tracked, and the update function can be run again.
It looks like for your functionality you might need to use calculated observable data that track your objects, depend on dependencies and make any necessary updates / api calls.
I still haven’t used fabric so far, but here you would take where you define the presentation model to represent the rectangle and create a computed one to constantly update the fabric object when any values change.
// create a wrapper around native canvas element (with id="c") var canvas = new fabric.Canvas('c'); var RectangleViewModel = function (canvas) { this.left = ko.observable(100); this.top = ko.observable(100); this.fill = ko.observable("red"); this.width = ko.observable(100); this.height = ko.observable(100); this.rect = new fabric.Rect(this.getParams()); canvas.add(this.rect); this.rectTracker = ko.computed(function () { this.rect.set(this.getParams()); canvas.renderAll(); }, this); }; RectangleViewModel.prototype.getParams = function () { return { left: +this.left(), top: +this.top(), fill: this.fill(), width: +this.width(), height: +this.height() }; }; var vm = new RectangleViewModel(canvas); ko.applyBindings(vm);
Another brief idea if you would rather save some of the tech / canvas from your view model (I probably will). You can create a fabric binding that takes an array of shapes to add to the canvas. You will also pass a handler that retrieves the parameters to go to it. After that, the binding will create the form, add it to the canvas, and then create the form calculated to update the form upon changes. Something like:
ko.bindingHandlers.fabric = { init: function (element, valueAccessor) { var shapes = valueAccessor(), canvas = new fabric.Canvas(element); ko.utils.arrayForEach(shapes, function (shape) { //create the new shape and initialize it var newShape = new fabric[shape.type](shape.params()); canvas.add(newShape); //track changes to the shape (dependencies accessed in the params() function ko.computed(function () { newShape.set(this.params()); canvas.renderAll(); }, shape, { disposeWhenNodeIsRemoved: element }); }); } };
You can place it on a canvas like:
<canvas data-bind="fabric: [ { type: 'Rect', params: rect.getParams }, { type: 'Rect', params: rect2.getParams } ]"></canvas>
At this point, the presentation model can be simplified quite a bit to just represent the data of the rectangle. Example here: http://jsfiddle.net/rniemeyer/G6MGm/