One Component Multiple Templates based on Condition
Asked Answered
G

3

25

So here's the deal. I have a component thats very well written and being used in a lot of places. Now I need to use the same component, but want a different template to be rendered, based upon a condition.

I tried a lot.

1) Tried using multiple component decorators - no luck

2) Tried multiple level of abstractions, where I just ended up creating more components - bad idea

3) Can literally copy the whole component, and just change the selector and template - bad idea

4) Currently I was trying this:

<div *ngIf="!isWizard">
    <ul class="nav" role="tablist">
        <ng-content select="tab-link"></ng-content>
    </ul>
    <ng-content select="tab-content"></ng-content>
</div>


<div *ngIf="isWizard">
    <nav class="nav-panel sidenav">
        <ng-content select=".wizard-title"></ng-content>
            <ul class="nav" role="tablist">
                <ng-content select="tab-link"></ng-content>
            </ul>

    </nav>

    <main class="settings-panel content-area">
        <ng-content select="tab-content"></ng-content>
    </main>

</div>

I set the isWizard property as true/false. Now the problem is, ng-content runs only once. So when isWizard is true, even though the div block is displayed, ng-content doesn't run ( cause it ran in the above block ).

5) Instead of using ngIf I also tried ngSwitch - didn't work

I'm desperate now. Please help :)

Geiger answered 4/6, 2016 at 20:30 Comment(7)
What Angular2 version are you using. As far as I remember this is an issue that was fixed recently. Not sure this fix is already included in RC.1Blowzed
@GünterZöchbauer I'm using 2.0.0-rc.1, but doesn't work.Geiger
I suggest you try again after the next update. The last update is quite a while back and that fix wasn't too long ago, don't remember exactly but I'm quite sure it covers this problem.Blowzed
@GünterZöchbauer I can't find the precise link, but I found somewhere that ng-content is supposed to be run only once, thats how it's developed. So thats why I'm relying on ngIf and ngSwitch ( so ngContent runs once ) , but even those don't work.Geiger
There is better support planned for this requirement, looks like it's required for Material components as well.Blowzed
Any solution yet? I'm trying to figure out a way to accomplish the same thing.Diva
Why not make the component a simple class. Then you should be able to inherit from or extend it. This way it is decoupled from the template.Creditable
S
4

As far as I know it cannot be done using ng-content but you could achieve this using templates (or ng-templates in Angular 4+). So instead of passing content directly to your component, just wrap it in <template> like that:

<my-component [isWizard]="true">
    <template>Hello World!</template>
</my-component>

Then you need to inject the template to your component with @ContentChild(TemplateRef) and render it as many times as you wish.

@Component({
  selector: "my-component",
  template: `
    <div *ngIf="!isWizard">
      first: <template [ngTemplateRenderer]="template"></template>
    </div>
    <div *ngIf="isWizard">
      second: <template [ngTemplateRenderer]="template"></template>
    </div>`
})
export class MyComponent {

  @ContentChild(TemplateRef)
  private template: TemplateRef<any>;

  @Input("isWizard")
  private isWizard: boolean;
}

There is one last thing, our component uses ngTemplateRenderer which is a simple utility directive that renders templates passed by reference. Here's the code for that directive:

@Directive({ selector: '[ngTemplateRenderer]'})
export class TemplateRenderer implements OnInit, OnDestroy {

    @Input("ngTemplateRenderer")
    private template: TemplateRef<any>;

    private view: EmbeddedViewRef<any>;

    constructor(private container: ViewContainerRef) {}

    ngOnInit(): void {
      this.view = this.container.createEmbeddedView(this.template);
    }

    ngOnDestroy(): void {
      this.view.destroy(); 
    }
}
Scruple answered 12/3, 2017 at 20:20 Comment(2)
can you add plnkr or jsBin example?Staley
What if we have multiple templates <my-component [isWizard]="true"> <template [key]="'email'"><input formControlName="email" type="email"> <template [key]="'name'"><input formControlName="name" type="text"></template></template> </my-component> And in my-component we want to conditionally display any of the template. either template email or name.Certain
K
0

In recent version above can be done using *ngIf="somevar", while you can pass "somevar" value using @input.

Example:

import { Component, OnInit, Input, Output, EventEmitter} from '@angular/core';
import {SearchPipe} from './../filters/search.pipe';

@Component({
  selector: 'itra-filter',
  templateUrl: 'filter.component.html',
  styleUrls: ['filter.component.scss'],
  inputs:['IsCheckboxEnabled','IsRadioboxEnabled'],
  outputs: ['itemClicked']
})
export class ItraFilterComponent {
	// Default Value
	public IsCheckboxEnabled:boolean = false;
	public IsRadioboxEnabled:boolean = false;

	constructor() {		
	}

	ngOnInit() {
		
	}
}
<span class="checkbox-control" *ngIf="IsCheckboxEnabled">
        <i class="icon icon_Check-box_filled"   *ngIf="y.checked"></i>
				<i class="icon icon_Check-box" *ngIf="!y.checked">        </i>
</span>
      
<span class="radiobox-control" *ngIf="IsRadioboxEnabled">
				<i class="icon icon_Radio-button-filled" *ngIf="y.checked"></i>
				<i class="icon icon_Radio-button" *ngIf="!y.checked"></i>
</span>
Knur answered 8/3, 2017 at 23:5 Comment(0)
A
0

Perhaps, we can try the solution made here, angular 2 include html templates.

I am pretty much satisfied with this solution as I was in the same situation like switching Templates based on certain values and ultimately not to mess up the code with huge amount of lines.

I'm just describing the structure of my project a bit for clarification,

Component Structure
====================
Comp A
 -> Comp a1
 -> Comp a2 
 -> Comp a3 
 -> Comp a-consolidated*(declaring all needed component's selectors) 
Comp B 
Comp C 
Comp D 

This works with my problem and I recommend this :)

Arterial answered 13/6, 2018 at 16:5 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.