Reputation: 8638
I'm working on a open-source project for a AngularJS Data Table directive ( still WIP ). When you look at components like Angular Grid or UI Grid they all describe their columns and attributes in a object in the parent controller like:
$scope.gridOptions = {
enableSorting: true,
enableCellEditOnFocus: true,
columnDefs: [
{ name: 'field1', enableSorting: false, enableCellEdit: false },
{ name: 'field2' },
{ name: 'field3', visible: false }
]
};
which works fine, however, I don't think this is really the 'angular way'. It feels more like a jQuery widget. If you look at projects like Angular Material, they are much more expressive in the HTML template vs object driven.
For my implementation, I originally wanted to make it very expressive and expose each one of the inner directives I use, however, that ended up a mess to just create a basic table. So I did some research on other frameworks and found that react had a nice architecture where you just define the columns like:
React.render(
<Table
rowHeight={50}
rowGetter={rowGetter}
rowsCount={rows.length}
width={5000}
height={5000}
headerHeight={50}>
<Column
label="Col 1"
width={3000}
dataKey={0}
/>
<Column
label="Col 2"
width={2000}
dataKey={1}
/>
</Table>,
document.getElementById('example')
);
I feel in love with this approach, its simple and is expressive all at the same time. 90% of the time you only want to customize the column template anyways. So instead of this:
$scope.gridOptions = {
enableFiltering: true,
rowTemplate: rowTemplate(),
data: 'data',
columnDefs: [
{ name: 'name' },
{ name: 'gender' },
{ name: 'company' },
{ name: 'widgets' },
{
name: 'cumulativeWidgets',
field: 'widgets',
cellTemplate: '<div class="ui-grid-cell-contents" title="TOOLTIP">{{grid.appScope.cumulative(grid, row)}}</div>'
}
]
};
with the cell template, you could do something like this:
<dt options="options" rows="data" class="material">
<Column name="name" width="300"></Column>
<Column name="Gender">
<strong>{{value}}</strong>
</Column>
<Column name="Company"></Column>
</dt>
notice how I took the React concept and paired it with a more Angular concept where I would include the ability to have a template inside the column.
Ok now for the problem. I want the columns on init, but not after that. I want to replace it with the actual table. Problem is I can never get that HTML when I need it.
So on this line I tried to do something like:
compile: function(tElem, tAttrs){
var tags = z.getElementsByTagName('column');
console.log(tags) // equals = []
return {
pre: function($scope, $elm, $attrs, ctrl){
}
};
}
but the columns are never there til later when I try to transclude them ( not really what I wanna do ). I need a way to get ahold of them before the controller is initialized and the template replaces the inner contents. Here is a plunkr!
Additionally, since my directive is scoped ( which I def wanna do for perf reasons ) I have no way to get access to the parent scope to compile the inner template with the outer contents.
Also, any suggestions / thoughts on this design paradigm? Thanks!
Upvotes: 3
Views: 331
Reputation: 5825
Try to use the template
as a function, which returns the actual template string. The first parameter is the original html:
var tags;
return {
template: function(element) {
tags = element[0].getElementsByTagName('column');
return "<div>table</div>"
},
compile: ...
}
Upvotes: 3
Reputation: 4597
I only found a weird way to achieve what you want.
Before someone give a better solution, here is mine working in this plunker.
Adding the $transclude
service to the controller and transclude:'element'
to the directive
app.directive("simple", function(){
return {
restrict: "EA",
replace:true,
transclude:'element',
template:"<div>table</div>",
compile: function(element, attributes, transclude){
var tags = element[0].getElementsByTagName('column');
console.log("compile", tags);
console.log("transclude", transclude);
return {
pre: function(scope, element, attributes, controller, transcludeFn){
var tags = element[0].getElementsByTagName('column');
console.log("pre", tags);
},
post: function(scope, element, attributes, controller, transcludeFn){
var tags = element[0].getElementsByTagName('column');
console.log("post", tags);
}
}
},
controller: function($scope, $element, $transclude){
$transclude(function(clone,scope){
/* for demo am converting to html string*/
console.log("From controller",angular.element(clone).find('column'));
});
}
};
});
I tested some other things. But i'm only able to get the column with this and only into the controller.
Upvotes: 1