Editor 2 way-binding with angular

I would like to create an editor using some angular directive. From what I can gather from the comment plugin, an editor follows the following footprint. init, prepare, getValue, setValue, focus, open, and close.

The init phase seems to be where I should $compile my template to get a dom object which I need to attach to this instance
Almost like the compile phase of an angular directive

this.instance.rootElement.appendChild (dom);

The prepare phase seems to be the link phase.

The open phase seems to be the place where I need to compute the style coordinate of my editor

And finally the focus phase where I need to set the focus to an element of my editor

  1. Am i right?
  2. Do you have an example of an editor using an angular directive and/or an ng-model
  3. When and how is my editor being destroyed.

Thanks

Pascal

I made some progresses. Right now I need to know how to request the editor to close while saving. I found that I could call cancelChanges() to rollback my changes and close the editor but I am having difficulties save. saveValue seems to want an array of array. If I call close() directly then changes are not saved.

Also need to know when the editor is destroyed to clear my scope.

Thanks

Responding to my own question if that can help someone.

To accept a change and close the editor, I call finishEditing();
To destroy the created scope during the init phase, I listen to DOM remove events, and destroy my scope

                var scope = $rootScope.$new(true);
                var content = angular.element ($compile(template) (scope));
                content.on ('remove', function () {
                        scope.$destroy();
                });

I am now able to use angular 2-way binding and complex templating in my editor.

Finally make sure that in setValue to set your scope value inside a $apply, otherwise the editor will take up to 2s to update its content.

    self.prototype.setValue = function (value) {
          content.scope().$apply (function () { 
                   content.scope()['mymodel'] = value;  // or whatever you need to do to deserialize your value to your model
         }); 
   });

Hi,

I can see that you are very into the case and made some progress in last few hours. Maybe after this four hours you made even bigger progress from the last given reply. I really want to help you with this case but I would need to see a demo code or get the precise question how to help.

Thanks. I got it working nicely. I can now create editors using template that are angular aware (2 way binding, ng- event aware, with isolated scope). This should help me write custom editor a lot faster. Here is the final code:

MYAPP.factory ('PopoverEditor', function ($log, $compile, $rootScope) {

	var self = Handsontable.editors.BaseEditor.prototype.extend();

	self.prototype.init = function() {

		$log.debug ('init popover editor');

		var template = [
			'<div class="panel htPopoverEditor" style="min-width: 150px">',
			'	<div class="panel-heading">',
			'		<div class="text-center">{{title}}<a class="pull-right" ng-click="cancel()">x</a></div>',
			'	</div>',
			'	<div class="panel-content">',
			'		<p>Value: <input ng-model="mymodel" class=""></p>',
			'	</div>',
			'	<div class="panel-footing">',
			'		<button class="btn btn-xs btn-danger" ng-click="cancel()">Remove</button>',
			'		<button class="btn btn-xs btn-primary" ng-click="save()">Save</button>',
			'	</div>',
			'</div>',

		].join ('');

		// set scope for our editor

		var _this = this;
		var scope = $rootScope.$new (true);
		scope.title = 'Custom Popover';
		scope.mymodel = 'whatever you want';
		scope.cancel = function () {
			$log.debug ('cancel');
			_this.cancelChanges();
		};
		scope.save = function () {
			$log.debug ('save');
			_this.finishEditing();
		};

		scope.$on ('$destroy', function () {
			$log.debug ('destroying scope of editor');
		});
		self.content = angular.element ($compile(template) (scope));
		self.content.on ('remove', function () {
			$log.debug ('removing popover dom');
			scope.$destroy();
		});
		this.popover = self.content[0]; 
		this.popover.style.display = 'none';
		this.instance.rootElement.appendChild(this.popover);
	};

	self.prototype.prepare = function() {
		$log.debug ('preparing popover');
		// Remember to invoke parent's method
		Handsontable.editors.BaseEditor.prototype.prepare.apply(this, arguments);
		// Handsontable.Dom.empty(this.popover);
	};

	self.prototype.getValue = function() {
		$log.debug ('get value popover');
		return self.content.scope()['mymodel'];

	};

	self.prototype.setValue = function(value) {
		$log.debug ('set value popover:', value);
		self.content.scope().$apply(function () {
			self.content.scope()['mymodel'] = value;
		});
	};

	self.prototype.focus = function () {
		$log.debug ('set focus');
		this.getInputElement().focus();
	};

	self.prototype.getInputElement = function () {
		return self.content.find ('input');
	};

	self.prototype.open = function() {
		$log.debug ('open popover');
		var width = Handsontable.Dom.outerWidth(this.TD);
		// important - group layout reads together for better performance
		var height = Handsontable.Dom.outerHeight(this.TD);
		var rootOffset = Handsontable.Dom.offset(this.instance.rootElement);
		var tdOffset = Handsontable.Dom.offset(this.TD);
		var editorSection = this.checkEditorSection();
		var cssTransformOffset;

		height += Handsontable.Dom.outerHeight(self.content);

		switch (editorSection) {
			case 'top':
				cssTransformOffset = Handsontable.Dom.getCssTransform(this.instance.view.wt.wtScrollbars.vertical.clone.wtTable.holder.parentNode);
				break;
			case 'left':
				cssTransformOffset = Handsontable.Dom.getCssTransform(this.instance.view.wt.wtScrollbars.horizontal.clone.wtTable.holder.parentNode);
				break;
			case 'corner':
				cssTransformOffset = Handsontable.Dom.getCssTransform(this.instance.view.wt.wtScrollbars.corner.clone.wtTable.holder.parentNode);
				break;
		}
		var editorStyle = this.popover.style;

		if (cssTransformOffset && cssTransformOffset !== -1) {
			editorStyle[cssTransformOffset[0]] = cssTransformOffset[1];
		} else {
			Handsontable.Dom.resetCssTransform(this.popover);
		}

		// editorStyle.height = height + 'px';
		editorStyle.minWidth = width + 'px';
		editorStyle.top = tdOffset.top - rootOffset.top - 1 + 'px';
		editorStyle.left = tdOffset.left - rootOffset.left + 'px';
		editorStyle.margin = '0px';
		editorStyle.display = '';
		$log.debug ('set style popover', editorStyle);
	};

	self.prototype.checkEditorSection = function() {
		if (this.row < this.instance.getSettings().fixedRowsTop) {
			if (this.col < this.instance.getSettings().fixedColumnsLeft) {
				return 'corner';
			} else {
				return 'top';
			}
		} else {
			if (this.col < this.instance.getSettings().fixedColumnsLeft) {
				return 'left';
			}
		}
	};

	self.prototype.close = function() {
		$log.debug ('close popover');
		this.popover.style.display = 'none';
	};

	Handsontable.editors.registerEditor('popover', self);

	return self;
});

Glad you came with the right solution and even more glad than Handsontable kept you inspired to dig dipper.

Code looks promising! I keep my fingers crossed for creating amazing editors.