好的,所以我需要做几乎相同的事情,并提出了一个可能被认为有点 hack 的解决方案(但没有其他方法 AFAIK,因为 SlickGrid 只处理 html 字符串,而不是 html/jquery 对象)。
简而言之,它涉及在格式化程序中编译模板(如您所做的那样),但除此之外,将生成的对象(不是 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),并且由于 $compile 以及我们使用真实对象而不是 asyncPostRender 上的 HTML 的事实,ng-click 将正常工作。
这是完整的指令,后面是 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();
};
});