ng-content select bound variable

gatapia picture gatapia · May 14, 2016 · Viewed 21.3k times · Source

I'm trying to create a form builder using angular 2. An very basic example is as follows:

this.fields = [{name: 'Name', type: 'text'}, {name: 'Age', type: 'number'}];

But I also want to support custom elements like:

this.fields = [
  {name: 'Name', type: text}, 
  {name: 'Age', type: 'custom', customid: 'Ctl1'},
  {name: 'Whatever', type: 'custom', customid: 'Ctl2'}
];
// template:
<super-form [fields]="fields">
  <Ctl1><input type="number" ...><Ctl1>
  <Ctl2><whaterver-control ...><Ctl2>
</super-form>

In my form builder component I then have something like:

<div *ngFor="let f of fields">
  <div [ngSwitch]="f.type">
    <span *ngSwitchWhen="'custom'">          
      <ng-content select="f.customid"></ng-content>
    </span>
  </div>
</div>

But given that I'm here this obviously does not work. Is this an ng2 limitation? If so, I guess I could hard code say 5 optional content elements and check if they are specified and not have dynamic selects but this is a hack.

Cheers

Answer

Justin picture Justin · Mar 1, 2017

I know this is an old question, but this is one of the first places I landed when searching for this functionality so I'll add how I was able to solve it.

ngContent is only for static projection, so you can't use it to do any bindings. If you need bindings in your projected content you can use ngTemplateOutlet and ngOutletContext.

Usage Example:

<my-component>
    <template let-item="item">
        <h1>{{item?.label}}</h1> - <span>{{item?.id}}</span>
    </template>
</my-component>

Inside MyComponent you can access that template using ContentChild:

@ContentChild(TemplateRef) templateVariable: TemplateRef<any>;

Then inside your component's template you pass that to ngTemplateOutlet like this:

<div *ngFor="let item of list">
    <template [ngTemplateOutlet]="templateVariable" [ngOutletContext]="{item: item}"></template>
</div>

The ngOutletContext is optional but it allows you to create the object that you will be binding to in the template. Notice that I created a property item in the context object. That matches the name I put on the template here: let-item="item"

Now the consumer of my-component can pass in the template to be used for each item in the list.

Credit: This answer led me in the right direction.