Created
April 12, 2020 22:43
-
-
Save nirkaufman/a737b9551706e580042242452b9a1444 to your computer and use it in GitHub Desktop.
Reference code for: https://www.youtube.com/watch?v=KKyiEd6yeUM&feature=youtu.be
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import {AfterViewInit, Component, OnInit, TemplateRef, ViewChild, ViewContainerRef,} from '@angular/core'; | |
import {Card, CardTypes} from "./cards/card.types"; | |
@Component({ | |
selector: 'app-root', | |
template: ` | |
<div class="container"> | |
<h1 class="display-1">Angular<span class="text-muted">MasterClass</span></h1> | |
<!-- will render the built-in templates --> | |
<ng-container *nkDeck="let card for cards;"></ng-container> | |
<!-- optional override for the 'built-in' primary template --> | |
<ng-container *nkDeck="let card for cards; primary altPrimaryTemplate"></ng-container> | |
<ng-template #altPrimaryTemplate let-card> | |
<!-- custom content for a primary card--> | |
</ng-template> | |
</div> | |
`, | |
}) | |
export class AppComponent{ | |
cards: Card[] = [ | |
{ | |
type: CardTypes.Plain, | |
title: "The title", | |
text: "The Text" | |
}, | |
{ | |
type: CardTypes.Plain, | |
title: "Another title", | |
text: "another text to render" | |
}, | |
{ | |
type: CardTypes.Primary, | |
title: "What else", | |
text: "The Text The Text The Text", | |
header: 'Im The header', | |
smallText: 'and some small text' | |
}, | |
] | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import {Component, TemplateRef, ViewChild} from '@angular/core'; | |
import {CardTemplateContext} from "./card.types"; | |
// this component used as a 'TemplateStorage' | |
// no selector needed | |
@Component({ | |
template: ` | |
<ng-template #plainCard let-card> | |
<div class="card"> | |
<div class="card-body"> | |
<h5 class="card-title">{{card.title}}</h5> | |
<p class="card-text">{{card.text}}</p> | |
</div> | |
</div> | |
</ng-template> | |
<ng-template #primaryCard let-card> | |
<div class="card border-primary"> | |
<div class="card-header">{{ card.header }}</div> | |
<div class="card-body text-primary"> | |
<h5 class="card-title">{{card.title}}</h5> | |
<p class="card-text">{{card.text}}</p> | |
<p class="card-text text-small">{{ card.smallText }}</p> | |
</div> | |
</div> | |
</ng-template> | |
`, | |
}) | |
export class CardTemplatesComponent { | |
@ViewChild('plainCard', {static: true}) plainCardTemplate: TemplateRef<CardTemplateContext>; | |
@ViewChild('primaryCard', {static: true}) primaryCardTemplate: TemplateRef<CardTemplateContext>; | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
export enum CardTypes { | |
Plain = 'Plain', | |
Primary = 'Primary', | |
} | |
export interface Card{ | |
type: CardTypes, | |
header?: string; | |
title?: string; | |
text?: string; | |
smallText?: string | |
} | |
export interface CardTemplateContext { | |
$implicit: Card | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import { | |
ComponentFactoryResolver, ComponentRef, | |
Directive, | |
ElementRef, | |
Injector, | |
Input, OnInit, | |
Renderer2, TemplateRef, | |
ViewContainerRef | |
} from '@angular/core'; | |
import {Card, CardTemplateContext, CardTypes} from "./card.types"; | |
import {CardTemplatesComponent} from "./card-templates.component"; | |
@Directive({ | |
selector: '[nkDeck]' | |
}) | |
export class DeckDirective implements OnInit { | |
// Angular will make the cards and primary: *nkDeck="let card for cards; primary altPrimaryTemplate | |
// available via input by suffixing the name of the variable to the directive name | |
@Input('nkDeckFor') cards: Card[]; | |
@Input('nkDeckPrimary') primaryTemplate: TemplateRef<CardTemplateContext>; | |
constructor( | |
private viewContainer: ViewContainerRef, | |
private renderer: Renderer2, | |
private cfr: ComponentFactoryResolver, | |
private injector: Injector, | |
private hostElement: ElementRef | |
) {} | |
ngOnInit(): void { | |
// get a reference to the the parent element of this directive | |
// hostElement (available via ElementRef injection) | |
const parentNode = this.renderer.parentNode(this.hostElement.nativeElement) | |
// create a brand new div element and set it's css class | |
const wrapper = this.renderer.createElement('div'); | |
this.renderer.addClass(wrapper, 'card-deck') | |
// insert the the new div element under the parent above this directive host element | |
this.renderer.insertBefore(parentNode, wrapper, this.hostElement.nativeElement) | |
// remove this directive host element and append it as a child of the the new div element | |
this.renderer.removeChild(parentNode, this.hostElement.nativeElement) | |
this.renderer.appendChild(wrapper, this.hostElement.nativeElement) | |
// create a CardTemplatesComponent by resoling a factory and use it | |
const cardTemplateFactory = this.cfr.resolveComponentFactory<CardTemplatesComponent>(CardTemplatesComponent); | |
const cardTemplateComponent: ComponentRef<CardTemplatesComponent> = cardTemplateFactory.create(this.injector); | |
// loop over the cards and create embedded view for each card | |
this.cards.forEach( card => { | |
this.renderTemplate(card, cardTemplateComponent); | |
} ) | |
} | |
// helper method for creating an embedded view out of templates | |
private renderTemplate(card: Card, templateComponent: ComponentRef<CardTemplatesComponent> ) { | |
switch (card.type) { | |
case CardTypes.Plain: | |
this.viewContainer.createEmbeddedView( | |
// try to use a provided template. if not available, use one from the CardComponentTemplate | |
this.primaryTemplate || templateComponent.instance.plainCardTemplate, {$implicit: card}) | |
break | |
case CardTypes.Primary: | |
this.viewContainer.createEmbeddedView(templateComponent.instance.primaryCardTemplate, {$implicit: card}) | |
break | |
} | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment