Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Passing context to an Angular 2 component's projected content (using ng-content)

I'm writing a reusable Angular 2 component to retrieve and display data in a table, with server-side sorting, filtering and searching. I would like each row in the table to have an extra column with generic html using an ng-context tag, and I can do this.

The most common case for this is to have buttons which can do actions using the data each row was constructed from (say, navigate to a specific page for the entry, or delete the entry), but there doesn't seem to be a way to pass an object to the ContentChildren of a component. I could perhaps accomplish this via direct DOM manipulation and inspection, but that is both very hacky and bad for re-usability.

I'm not really concerned with any specific way to accomplish this, so long as it:

  • Allows me to add generic html as a child of the component
  • Can pass some generic data from the parent component to its ContentChildren (the aforementioned generic html) in such a way that it can be used by event handlers on the children.

I'm using typescript so a type-safe solution would be preferred, but I'd be happy having to ignore the type system if need be.

My table component (my-table) in use would look something like

<my-table /* some properties */>
    <button class="row-button" type="button" (click)="navigate(/* context */)">Navigate</button>
</my-table>

with its template using ng-content like so:

// table html
<tr *ngFor="let row of rows">
    <td *ngFor="let column of row.columns">{{column}}</td>
    <td>
        <ng-content select=".row-button"></ng-content>
    </td>
</tr>
// more table html

If I could pass a parameter to the ng-content which would be usable by the button element that would suffice.

like image 753
CoreyGrant Avatar asked Jun 22 '16 12:06

CoreyGrant


People also ask

What is Ng-content in Angular with example?

The ng-content is used when we want to insert the content dynamically inside the component that helps to increase component reusability. Using ng-content we can pass content inside the component selector and when angular parses that content that appears at the place of ng-content.

What is projected content in Angular?

Content projection is a pattern in which you insert, or project, the content you want to use inside another component. For example, you could have a Card component that accepts content provided by another component. With this type of content projection, a component accepts content from a single source.

How does Angular component pass custom content?

The first step to passing data into an Angular component is to create a custom property to bind to. This is done via “input” binding to pass data from one component to another (typically parent to child). This custom input binding is created via the @Input() decorator!


2 Answers

One way would be a template variable that allows you to explicitly refer to the parent:

<my-table #myTable>
  <button [context]="myTable.context">
</my-table>

As far as I know there are plans to make this more flexible. I guess it is this issue https://github.com/angular/angular/issues/8563

like image 144
Günter Zöchbauer Avatar answered Oct 08 '22 05:10

Günter Zöchbauer


If you're not strict on usin ng-content, ng-template could do the work:

<my-table [rowButtonTemplate]="buttonTemplate" /* some properties */>
    <ng-template #buttonTemplate let-column>
        <button class="row-button" type="button" (click)="navigate(column.context)">Navigate</button>
    </ng-template>
</my-table>

and then inside table:

// table ts
@Input()
rowButtonTemplate: TemplateRef;


// table html
<tr *ngFor="let row of rows">
    <td *ngFor="let column of row.columns">{{column}}</td>
    <td>
        <ng-container *ngTemplateOutlet="rowButtonTemplate; context: { $implicit: column, index: index }"></ng-container>
    </td>
</tr>
// more table html
like image 40
eagor Avatar answered Oct 08 '22 05:10

eagor