39

depending on the value of a (boolean) class variable I would like my ng-content to either be wrapped in a div or to not be wrapped in div (I.e. the div should not even be in the DOM) ... Whats the best way to go about this ? I have a Plunker that tries to do this, in what I assumed was the most obvious way, using ngIf .. but it's not working... It displays content only for one of the boolean values but not the other

kindly assist Thank you!

http://plnkr.co/edit/omqLK0mKUIzqkkR3lQh8

@Component({
  selector: 'my-component',
  template: `

   <div *ngIf="insideRedDiv" style="display: inline; border: 1px red solid">
      <ng-content *ngIf="insideRedDiv"  ></ng-content> 
   </div>

   <ng-content *ngIf="!insideRedDiv"></ng-content>     

  `,
})
export class MyComponent {
  insideRedDiv: boolean = true;
}


@Component({
  template: `
    <my-component> ... "Here is the Content"  ... </my-component>
  `
})
export class App {}
Somo S.
  • 2,604
  • 3
  • 19
  • 31
  • Good plunkr, good question.. Messed around with it and can't figure it out either. I would be interested to know the solution – jhhoff02 Jan 11 '17 at 15:05

3 Answers3

77

Angular ^4

As workaround i can offer you the following solution:

<div *ngIf="insideRedDiv; else elseTpl" style="display: inline; border: 1px red solid">
  <ng-container *ngTemplateOutlet="elseTpl"></ng-container>
</div>

<ng-template #elseTpl><ng-content></ng-content> </ng-template>

Plunker Example angular v4

Angular < 4

Here you can create dedicated directive that will do the same things:

<div *ngIf4="insideRedDiv; else elseTpl" style="display: inline; border: 1px red solid">
   <ng-container *ngTemplateOutlet="elseTpl"></ng-container>
</div>

<template #elseTpl><ng-content></ng-content></template>

Plunker Example

ngIf4.ts

class NgIfContext { public $implicit: any = null; }

@Directive({ selector: '[ngIf4]' })
export class NgIf4 {
  private context: NgIfContext = new NgIfContext();
  private elseTemplateRef: TemplateRef<NgIfContext>;
  private elseViewRef: EmbeddedViewRef<NgIfContext>;
  private viewRef: EmbeddedViewRef<NgIfContext>;

  constructor(private viewContainer: ViewContainerRef, private templateRef: TemplateRef<NgIfContext>) { }

  @Input()
  set ngIf4(condition: any) {
    this.context.$implicit = condition;
    this._updateView();
  }

  @Input()
  set ngIf4Else(templateRef: TemplateRef<NgIfContext>) {
    this.elseTemplateRef = templateRef;
    this.elseViewRef = null;
    this._updateView();
  }

  private _updateView() {
    if (this.context.$implicit) {
      this.viewContainer.clear();
      this.elseViewRef = null;

      if (this.templateRef) {
        this.viewRef = this.viewContainer.createEmbeddedView(this.templateRef, this.context);
      }
    } else {
      if (this.elseViewRef) return;

      this.viewContainer.clear();
      this.viewRef = null;

      if (this.elseTemplateRef) {
        this.elseViewRef = this.viewContainer.createEmbeddedView(this.elseTemplateRef, this.context);
      }
    }
  }
}
yurzui
  • 171,085
  • 24
  • 365
  • 354
  • 3
    Wish I could upvote this twice. Spent forever trying to figure out how to use ng-content inside of *ngIf – vangorra Feb 16 '17 at 15:42
  • Thank you Yurzi, that top sample was what I spent a long time trying to phrase for a google search. Angular 5 is fine with it. – user1059939 Feb 22 '18 at 17:10
  • Works great! Pretty insane btw. Just got into Angular, but I'm constantly amazed how powerful it is. – Leaky Jul 19 '19 at 08:43
  • This is awesome, I was afraid I had to write a lot of duplicate code – devqon Apr 28 '20 at 14:18
5

Remember that you can put all this logic in separate component! (based on yurzui answer):

import { Component, Input } from '@angular/core';

@Component({
    selector: 'div-wrapper',
    template: `
    <div *ngIf="wrap; else unwrapped">
      <ng-content *ngTemplateOutlet="unwrapped">
      </ng-content>
    </div>
    <ng-template #unwrapped>
      <ng-content>
      </ng-content>
    </ng-template>
    `,
})
export class ConditionalDivComponent {
  @Input()
  public wrap = false;
}

You can then use it like this:

<div-wrapper [wrap]="'true'">
 Hello world!        
</div-wrapper>
charlie_pl
  • 2,170
  • 1
  • 21
  • 32
2

I checked into this and found an open issue on the subject of multiple transclusions with the tag. This prevents you from defining multiple tags in a single template file.

This explains why the content is displayed correctly only when the other tag is removed in your plunker example.

You can see the open issue here: https://github.com/angular/angular/issues/7795

Jolmari
  • 31
  • 4
  • I figure that since the ngIf are mutually exclusive the two "ng-contents" are never in the view at the same time so they wont "conflict" ... the other perculiar thing is this is not failing loudly OR softly there is no error of any kind yet it clearly isn't working as one might think.. Thank you for the issue, I shall be following it closely! – Somo S. Jan 11 '17 at 15:47