わかりましたので、ほとんど同じことをする必要があり、少しハックと見なすことができる解決策を思いつきました(しかし、SlickGrid は html/jquery オブジェクトではなく html 文字列のみを処理するため、他に方法はありません)。
簡単に言えば、フォーマッターでテンプレートをコンパイルする必要がありますが (あなたが行ったように)、それに加えて、生成されたオブジェクト(HTML 文字列ではありません) を辞書に格納し、それを使用してasyncPostRenderを使用してセルの内容を置き換えます( http://mleibman.github.io/SlickGrid/examples/example10-async-post-render.html )。
ここで重要なリンク関数の部分を次に示します。
var cols = angular.copy(scope.columns);
var templates = new Array();
// Special Sauce: Allow columns to have an angular template
// in place of a regular slick grid formatter function
angular.forEach(cols, function (col) {
if (angular.isDefined(col.template)) {
col.formatter = function (row, cell, value, columnDef, dataContext) {
// Create a new scope, for each cell
var cellScope = scope.$parent.$new(false);
cellScope.value = value;
cellScope.context = dataContext;
// Interpolate (i.e. turns {{context.myProp}} into its value)
var interpolated = $interpolate(col.template)(cellScope);
// Compile the interpolated string into an angular object
var linker = $compile(interpolated);
var o = linker(cellScope);
// Create a guid to identify this object
var guid = guidGenerator.create();
// Set this guid to that object as an attribute
o.attr("guid", guid);
// Store that Angular object into a dictionary
templates[guid] = o;
// Returns the generated HTML: this is just so the grid displays the generated template right away, but if any event is bound to it, they won't work just yet
return o[0].outerHTML;
};
col.asyncPostRender = function(cellNode, row, dataContext, colDef) {
// From the cell, get the guid generated on the formatter above
var guid = $(cellNode.firstChild).attr("guid");
// Get the actual Angular object that matches that guid
var template = templates[guid];
// Remove it from the dictionary to free some memory, we only need it once
delete templates[guid];
if (template) {
// Empty the cell node...
$(cellNode).empty();
// ...and replace its content by the object (visually this won't make any difference, no flicker, but this one has event bound to it!)
$(cellNode).append(template);
} else {
console.log("Error: template not found");
}
};
}
});
列は次のように定義できます。
{ name: '', template: '<button ng-click="delete(context)" class="btn btn-danger btn-mini">Delete {{context.user}}</button>', width:80}
context.user は適切に補間され ($interpolate のおかげで)、ng-click は $compile と、asyncPostRender で HTML ではなく実際のオブジェクトを使用するという事実のおかげで機能します。
これは完全なディレクティブで、その後に HTML とコントローラーが続きます。
指令:
(function() {
'use strict';
var app = angular.module('xweb.common');
// Slick Grid Directive
app.directive('slickGrid', function ($compile, $interpolate, guidGenerator) {
return {
restrict: 'E',
replace: true,
template: '<div></div>',
scope: {
data:'=',
options: '=',
columns: '='
},
link: function (scope, element, attrs) {
var cols = angular.copy(scope.columns);
var templates = new Array();
// Special Sauce: Allow columns to have an angular template
// in place of a regular slick grid formatter function
angular.forEach(cols, function (col) {
if (angular.isDefined(col.template)) {
col.formatter = function (row, cell, value, columnDef, dataContext) {
// Create a new scope, for each cell
var cellScope = scope.$parent.$new(false);
cellScope.value = value;
cellScope.context = dataContext;
// Interpolate (i.e. turns {{context.myProp}} into its value)
var interpolated = $interpolate(col.template)(cellScope);
// Compile the interpolated string into an angular object
var linker = $compile(interpolated);
var o = linker(cellScope);
// Create a guid to identify this object
var guid = guidGenerator.create();
// Set this guid to that object as an attribute
o.attr("guid", guid);
// Store that Angular object into a dictionary
templates[guid] = o;
// Returns the generated HTML: this is just so the grid displays the generated template right away, but if any event is bound to it, they won't work just yet
return o[0].outerHTML;
};
col.asyncPostRender = function(cellNode, row, dataContext, colDef) {
// From the cell, get the guid generated on the formatter above
var guid = $(cellNode.firstChild).attr("guid");
// Get the actual Angular object that matches that guid
var template = templates[guid];
// Remove it from the dictionary to free some memory, we only need it once
delete templates[guid];
if (template) {
// Empty the cell node...
$(cellNode).empty();
// ...and replace its content by the object (visually this won't make any difference, no flicker, but this one has event bound to it!)
$(cellNode).append(template);
} else {
console.log("Error: template not found");
}
};
}
});
var container = element;
var slickGrid = null;
var dataView = new Slick.Data.DataView();
var bindDataView = function() {
templates = new Array();
var index = 0;
for (var j = 0; j < scope.data.length; j++) {
scope.data[j].data_view_id = index;
index++;
}
dataView.setItems(scope.data, 'data_view_id');
};
var rebind = function() {
bindDataView();
scope.options.enableAsyncPostRender = true;
slickGrid = new Slick.Grid(container, dataView, cols, scope.options);
slickGrid.onSort.subscribe(function(e, args) {
console.log('Sort clicked...');
var comparer = function(a, b) {
return a[args.sortCol.field] > b[args.sortCol.field];
};
dataView.sort(comparer, args.sortAsc);
scope.$apply();
});
slickGrid.onCellChange.subscribe(function(e, args) {
console.log('Cell changed');
console.log(e);
console.log(args);
args.item.isDirty = true;
scope.$apply();
});
};
rebind();
scope.$watch('data', function (val, prev) {
console.log('SlickGrid ngModel updated');
bindDataView();
slickGrid.invalidate();
}, true);
scope.$watch('columns', function (val, prev) {
console.log('SlickGrid columns updated');
rebind();
}, true);
scope.$watch('options', function (val, prev) {
console.log('SlickGrid options updated');
rebind();
}, true);
}
};
});
})();
HTML:
<slick-grid id="slick" class="gridStyle" data="data" columns="columns" options="options" ></slick-grid>
コントローラー:
$scope.data = [
{ spreadMultiplier: 1, supAmount: 2, from: "01/01/2013", to: "31/12/2013", user: "jaussan", id: 1000 },
{ spreadMultiplier: 2, supAmount: 3, from: "01/01/2014", to: "31/12/2014", user: "camerond", id: 1001 },
{ spreadMultiplier: 3, supAmount: 4, from: "01/01/2015", to: "31/12/2015", user: "sarkozyn", id: 1002 }
];
// SlickGrid Columns definitions
$scope.columns = [
{ name: "Spread Multiplier", field: "spreadMultiplier", id: "spreadMultiplier", sortable: true, width: 100, editor: Slick.Editors.Decimal },
{ name: "Sup Amount", field: "supAmount", id: "supAmount", sortable: true, width: 100, editor: Slick.Editors.Decimal },
{ name: "From", field: "from", id: "from", sortable: true, width: 130, editor: Slick.Editors.Date },
{ name: "To", field: "to", id: "to", sortable: true, width: 130, editor: Slick.Editors.Date },
{ name: "Added By", field: "user", id: "user", sortable: true, width: 200 },
{ name: '', template: '<button ng-click="delete(context)" class="btn btn-danger btn-mini">Delete</button>', width:80}
];
// SlickGrid Options
$scope.options = {
fullWidthRows: true,
editable: true,
selectable: true,
enableCellNavigation: true,
rowHeight:30
};
重要:
rebind() メソッドで、
scope.options.enableAsyncPostRender = true;
これは非常に重要です。そうしないと、asyncPostRender が呼び出されません。
また、完全を期すために、ここに GuidGenerator サービスを示します。
app.service('guidGenerator', function() {
this.create = function () {
function s4() {
return (((1 + Math.random()) * 0x10000) | 0).toString(16).substring(1);
}
function guid() {
return (s4() + s4() + "-" + s4() + "-" + s4() + "-" + s4() + "-" + s4() + s4() + s4());
}
return guid();
};
});