At work I have to display a lot of tables with data that can change dynamically and quickly. To make this process easier I've been developing a declarative table directive.
Often there will be tables of stuff with Ids associated with them, like clients. I'm trying to place a link around the Id so that when the user clicks it, they will either get more detail or be directed to a different page. Neither the ng-click
nor the ui-sref
directives work when inserted into the DOM.
I have tried implementing the solution to this question: angular ng-bind-html and directive within it
////////// TABLE DIRECTIVE
const TABLE_TEMPLATE = `<table class="table table-striped">
<thead>
<tr ng-transclude></tr>
</thead>
<tbody>
<tr ng-repeat="row in tbl.data">
<td ng-repeat="col in tbl.columns"
dc-compile="tbl.apply(col, row)"></td>
</tr>
</tbody>
</table>`;
class DcTableController {
addColumn(col) {
if (!this.columns) {
this.columns = [];
}
this.columns.push(col);
}
apply(col, row) {
let fields = col.field.split(",");
let result = "";
if (col.transform) {
let $value = fields.length > 1 ?
fields.map(field => row[field]) :
row[col.field];
return col.transform({ $value });
} else {
return fields.map(field => row[field]).join();
}
}
}
function dcTable() {
return {
restrict: "E",
transclude: true,
controller: DcTableController,
controllerAs: "tbl",
template: TABLE_TEMPLATE,
scope: {},
bindToController: {
data: "="
}
};
}
////////// COLUMN DIRECTIVE
function dcColumn() {
return {
restrict: "E",
replace: true,
require: "^dcTable",
transclude: true,
template: "<th ng-transclude></th>",
scope: {
field: "@",
transform: "&"
},
link(scope, elem, attrs, ctrl) {
scope.transform = angular.isDefined(attrs.transform) ?
scope.transform : false;
ctrl.addColumn(scope)
}
};
}
////////// COMPILE DIRECTIVE
dcCompile.$inject = ["$compile"];
function dcCompile($compile) {
return (scope, elem, attrs) => {
scope.$watch(
scope => scope.$eval(attrs.dcCompile),
value => {
elem.html(value);
$compile(elem.contents())(scope);
}
);
};
}
////////// MODULE CREATION
class DemoController {
constructor() {
this.employees = [
{
BadgeNo: "1701",
FirstName: "Jean-Luc",
LastName: "Picard",
Salary: 0
}
];
}
showAddress($event, badge) {
console.log(badge);
$event.preventDefault();
}
linkBadge(badge) {
let a = document.createElement("a");
a.href = "#";
a.setAttribute("ng-click", `dc.showAddress($event, ${ badge })`);
a.textContent = badge;
return a.outerHTML;
}
formatName(names) {
let [first, last] = names;
return `${ last }, ${ first }`;
}
}
I swear at one time it worked but for whatever reason I can't get it to work anymore. Here is the accompanying fiddle demonstrating the issue: https://jsfiddle.net/zachdunn/0n0m6dv7/1/
So when you do some logging to the console in the compile directive, you can see that the "contents" of the element being compiled includes the link with the ng-click
attached to it, yet when you click on the link it does not call the function in the DemoController
as it should.
In the title I mentioned that the compiling is in an ng-repeat loop. I don't really see why it would affect how the directive compiles but in the question I linked to, they weren't doing that and I haven't seen any other examples that implement the method inside of repeat clauses.