Angular Developing an Expressive Directive

I am working on an open source project for AngularJS datasheet (still WIP). When you look at components like Angular Grid or UI Grid they all describe their columns and attributes in an object in the parent controller, for example:

$scope.gridOptions = {
  enableSorting: true,
  enableCellEditOnFocus: true,
  columnDefs: [ 
    { name: 'field1', enableSorting: false, enableCellEdit: false },
    { name: 'field2' },
    { name: 'field3', visible: false }
  ]
};

      

which works great, however, I don't think this is really the "w501 way". It looks more like a jQuery widget. If you look at projects like Angular Material, they are much more expressive in HTML template and object.

For my implementation, I originally wanted to make it very expressive and expose each of the internal directives that I use, however, this resulted in a mess to just create a base table. So I did some research in other frameworks and found that react had a good architecture where you just define 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 fell in love with this approach, its simple and expressive at the same time. 90% of the time you want to customize the column template anyway. 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 a cell template, you can 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 combined it with the Angular concept, where I would include the ability to have a template inside a column.

Good for this problem. I want columns in init, but not after that. I want to replace it with an actual table. The problem is, I can never get this HTML when I need it.

So in 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 will never end up when I try to overlap them (not what I want to do). I need a way to get them before the controller is initialized and the template will replace the inner content. Here's a plunkr !

Also, since my directive is limited (which I want to do for basic reasons), I have no way to access the parent scope to compile the inner template with the outer content.

Also, any suggestions / thoughts on this design paradigm? Thank!

+3


source to share


2 answers


Try using it template

as a function that 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: ...
}

      

+3


source


I only found a weird way to achieve what you want.

Before someone gives a better solution, here is my work in this plunker .

Adding service $transclude

to controller and transclude:'element'

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 have checked some other things. But I can only get the column with this and only into the controller.

+1


source







All Articles