amcdnl amcdnl - 18 days ago 21
AngularJS Question

Angular2 TemplateRef Selector

In my datatable project, developers have the ability to declare templates in the table declaration to be used for the header cell and the body cell.

<datatable>
<datatable-column name="Name">
<template let-column="column">
Header: {{column.name}}
</template>
<template let-value="value">
Hi: <strong>{{value}}</strong>
</template>
</datatable-column>
<datatable-column name="Age">
<datatable-body-template let-value="value">
I'm: <strong>{{value}}</strong> old
</datatable-body-template>
</datatable-column>
</datatable>


in the above example, the first template is the header cell template and the second one is the body cell template and the order matters. If you only declare one template, it will only do body cell template. You can see how this is being done currently here.

I do currently allow you to pass
TemplateRef
s via a
Input
to the column directive but that approach is ok but as an API consumer, the declarative templates inside the component definition is a cleaner approach.

Its also worth noting, that its not possible to do naming via something like
#body
and
#header
since you can have multiple columns making the names not unique.

This DSL is not very intuitive because its not very explicit which is which and it depends on order.
ng-content
allows you to put in a selector attribute, I was wondering what the best way to accomplish something like that with templates would be? So something like:

<datatable>
<datatable-column name="Name">
<datatable-header-template let-column="column">
Header: {{column.name}}
</datatable-header-template>
<datatable-body-template let-value="value">
Hi: <strong>{{value}}</strong>
</datatable-body-template>
</datatable-column>
</datatable>


I was digging around trying to see if its possible to override the
TemplateRef
but I'm not sure thats even a good idea.

So with all that said... my question, how can i accomplish named selector template refs?

Answer

You can pass the templates to inputs

<template #header let-column="column">
    Header: {{column.name}}
</template>

<template #body let-value="value">
    Hi: <strong>{{value}}</strong>
</template>

<datatable>
    <datatable-column name="Name" [header]="header" [column]="body"></datatable-column>
</datatable>

update

It is also possible to add template variables and query by these

<datatable>
  <template let-column="column" #header>
    header1
  </template>
  <template let-value="value" #column>
    column1
  </template>

  <template let-value="value" #column>
    column2
  </template>

  @ContentChildren('header', {read: TemplateRef}) headerTemplates:QueryList<TemplateRef>;
  @ContentChildren('column', {read: TemplateRef}) columnTemplates:QueryList<TemplateRef>;

or use directives. This approach allows to pass additional information

@Directive({
  selector: '[header]';
})
class HeaderTemplate {
  constructor(private template:TemplateRef) {};

  @Input() header:String;
}

@Directive({
  selector: '[column]';
})
class ColumnTemplate {
  constructor(private template:TemplateRef) {};

  @Input() column:String;
}
<datatable>
  <template let-column="column" header="1">
    header1
  </template>
  <template let-value="value" column="1">
    column1
  </template>

  <template let-value="value" column="2">
    column2
  </template>

</datatable>
@ContentChildren(HeaderTemplate) headerTemplates:QueryList<HeaderTemplate>;
@ContentChildren(ColumnTemplate) columnTemplates:QueryList<ColumnTemplate>;

the properties template and header / column of headerTemplate / columnTemplate allow you to access the template and a passed value (can have additional @Inputs() but one that matches the selector is easiest to use).

Plunker example