From 1c78cfc8b43ae0053ef8cf6db536bffcbb3b0cfa Mon Sep 17 00:00:00 2001 From: Juri <juri.strumpflohner@gmail.com> Date: Sat, 11 Nov 2017 17:03:32 +0100 Subject: [PATCH 01/12] feat: add ng-template example --- src/app/app.component.ts | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/src/app/app.component.ts b/src/app/app.component.ts index 8177f80..1c7aca7 100644 --- a/src/app/app.component.ts +++ b/src/app/app.component.ts @@ -1,4 +1,4 @@ -import { Component, OnInit } from '@angular/core'; +import { Component, OnInit, ViewChild } from '@angular/core'; import { PeopleService } from './people/people.service'; @Component({ @@ -12,9 +12,13 @@ import { PeopleService } from './people/people.service'; <ngx-tab tabTitle="Tab 2">Tab 2 Content</ngx-tab> </ngx-tabs> + <ng-template #personEdit> + Hi, I'm a static template defined within the App component's template. + </ng-template> ` }) export class AppComponent implements OnInit { + @ViewChild('personEdit') personEditTemplate; people; constructor(private peopleService: PeopleService) {} @@ -23,5 +27,7 @@ export class AppComponent implements OnInit { this.peopleService.getPeople().subscribe(data => { this.people = data; }); + + console.log(this.personEditTemplate); } } From b765fd8ca940cf404c688c6bbb7d167689a4321a Mon Sep 17 00:00:00 2001 From: Juri <juri.strumpflohner@gmail.com> Date: Thu, 16 Nov 2017 09:16:10 +0100 Subject: [PATCH 02/12] fix: use the AfterViewInit to access the @Viewchild --- src/app/app.component.ts | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/src/app/app.component.ts b/src/app/app.component.ts index 1c7aca7..771ef5b 100644 --- a/src/app/app.component.ts +++ b/src/app/app.component.ts @@ -1,4 +1,4 @@ -import { Component, OnInit, ViewChild } from '@angular/core'; +import { Component, OnInit, ViewChild, AfterViewInit } from '@angular/core'; import { PeopleService } from './people/people.service'; @Component({ @@ -13,11 +13,12 @@ import { PeopleService } from './people/people.service'; </ngx-tabs> <ng-template #personEdit> - Hi, I'm a static template defined within the App component's template. + Hi, I am a static template defined within the template + of another component. </ng-template> ` }) -export class AppComponent implements OnInit { +export class AppComponent implements OnInit, AfterViewInit { @ViewChild('personEdit') personEditTemplate; people; @@ -27,7 +28,9 @@ export class AppComponent implements OnInit { this.peopleService.getPeople().subscribe(data => { this.people = data; }); + } + ngAfterViewInit() { console.log(this.personEditTemplate); } } From 04d426ecbb286555e26de72ee7a87acd5d1287ea Mon Sep 17 00:00:00 2001 From: Juri <juri.strumpflohner@gmail.com> Date: Sat, 11 Nov 2017 17:25:27 +0100 Subject: [PATCH 03/12] feat: pass template onto tab component --- src/app/app.component.ts | 2 +- src/app/tabs/tab.component.ts | 3 +++ 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/src/app/app.component.ts b/src/app/app.component.ts index 771ef5b..b93b203 100644 --- a/src/app/app.component.ts +++ b/src/app/app.component.ts @@ -9,7 +9,7 @@ import { PeopleService } from './people/people.service'; <ngx-tab tabTitle="People List"> <app-people-list [people]="people"></app-people-list> </ngx-tab> - <ngx-tab tabTitle="Tab 2">Tab 2 Content</ngx-tab> + <ngx-tab tabTitle="Tab 2" [template]="personEdit"></ngx-tab> </ngx-tabs> <ng-template #personEdit> diff --git a/src/app/tabs/tab.component.ts b/src/app/tabs/tab.component.ts index 5515e8c..eb8eec8 100644 --- a/src/app/tabs/tab.component.ts +++ b/src/app/tabs/tab.component.ts @@ -12,10 +12,13 @@ import { Component, Input } from '@angular/core'; template: ` <div [hidden]="!active" class="pane"> <ng-content></ng-content> + <ng-container + [ngTemplateOutlet]="template"></ng-container> </div> ` }) export class TabComponent { @Input() tabTitle: string; @Input() active = false; + @Input() template; } From 8d1fe57ed5f1bd28aac9e13860498f669e74ce70 Mon Sep 17 00:00:00 2001 From: Juri <juri.strumpflohner@gmail.com> Date: Sat, 11 Nov 2017 17:44:43 +0100 Subject: [PATCH 04/12] fix: add if check for presence of template --- src/app/tabs/tab.component.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/app/tabs/tab.component.ts b/src/app/tabs/tab.component.ts index eb8eec8..10a8834 100644 --- a/src/app/tabs/tab.component.ts +++ b/src/app/tabs/tab.component.ts @@ -12,7 +12,7 @@ import { Component, Input } from '@angular/core'; template: ` <div [hidden]="!active" class="pane"> <ng-content></ng-content> - <ng-container + <ng-container *ngIf="template" [ngTemplateOutlet]="template"></ng-container> </div> ` From bdbed565f6a0c80842abdc68a745ae50d39d1a53 Mon Sep 17 00:00:00 2001 From: Juri <juri.strumpflohner@gmail.com> Date: Thu, 16 Nov 2017 09:32:55 +0100 Subject: [PATCH 05/12] fix: some improvements --- src/app/tabs/tab.component.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/app/tabs/tab.component.ts b/src/app/tabs/tab.component.ts index 10a8834..adaf853 100644 --- a/src/app/tabs/tab.component.ts +++ b/src/app/tabs/tab.component.ts @@ -11,7 +11,7 @@ import { Component, Input } from '@angular/core'; ], template: ` <div [hidden]="!active" class="pane"> - <ng-content></ng-content> + <ng-content *ngIf="!template"></ng-content> <ng-container *ngIf="template" [ngTemplateOutlet]="template"></ng-container> </div> From 10b880ba6f9c32aa1c83d8b630b5e264367fd046 Mon Sep 17 00:00:00 2001 From: Juri <juri.strumpflohner@gmail.com> Date: Sat, 11 Nov 2017 17:39:43 +0100 Subject: [PATCH 06/12] feat: add data to template --- src/app/app.component.ts | 7 +++---- src/app/tabs/tab.component.ts | 5 ++++- 2 files changed, 7 insertions(+), 5 deletions(-) diff --git a/src/app/app.component.ts b/src/app/app.component.ts index b93b203..fa8441b 100644 --- a/src/app/app.component.ts +++ b/src/app/app.component.ts @@ -9,12 +9,11 @@ import { PeopleService } from './people/people.service'; <ngx-tab tabTitle="People List"> <app-people-list [people]="people"></app-people-list> </ngx-tab> - <ngx-tab tabTitle="Tab 2" [template]="personEdit"></ngx-tab> + <ngx-tab tabTitle="Tab 2" [template]="personEdit" [dataContext]="people[0]"></ngx-tab> </ngx-tabs> - <ng-template #personEdit> - Hi, I am a static template defined within the template - of another component. + <ng-template let-person="person" #personEdit> + Hi, I'm {{ person?.name }}. </ng-template> ` }) diff --git a/src/app/tabs/tab.component.ts b/src/app/tabs/tab.component.ts index adaf853..25c3f90 100644 --- a/src/app/tabs/tab.component.ts +++ b/src/app/tabs/tab.component.ts @@ -13,7 +13,9 @@ import { Component, Input } from '@angular/core'; <div [hidden]="!active" class="pane"> <ng-content *ngIf="!template"></ng-content> <ng-container *ngIf="template" - [ngTemplateOutlet]="template"></ng-container> + [ngTemplateOutlet]="template" + [ngTemplateOutletContext]="{person: dataContext}"> + </ng-container> </div> ` }) @@ -21,4 +23,5 @@ export class TabComponent { @Input() tabTitle: string; @Input() active = false; @Input() template; + @Input() dataContext; } From 44edfa1dd8be1d6ce258131cb666e2e572da30c0 Mon Sep 17 00:00:00 2001 From: Juri <juri.strumpflohner@gmail.com> Date: Thu, 16 Nov 2017 09:59:40 +0100 Subject: [PATCH 07/12] fix: some minor adjustments on the variable names --- src/app/app.component.ts | 2 +- src/app/tabs/tab.component.ts | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/app/app.component.ts b/src/app/app.component.ts index fa8441b..1de4015 100644 --- a/src/app/app.component.ts +++ b/src/app/app.component.ts @@ -12,7 +12,7 @@ import { PeopleService } from './people/people.service'; <ngx-tab tabTitle="Tab 2" [template]="personEdit" [dataContext]="people[0]"></ngx-tab> </ngx-tabs> - <ng-template let-person="person" #personEdit> + <ng-template let-person="data" #personEdit> Hi, I'm {{ person?.name }}. </ng-template> ` diff --git a/src/app/tabs/tab.component.ts b/src/app/tabs/tab.component.ts index 25c3f90..426eef1 100644 --- a/src/app/tabs/tab.component.ts +++ b/src/app/tabs/tab.component.ts @@ -14,7 +14,7 @@ import { Component, Input } from '@angular/core'; <ng-content *ngIf="!template"></ng-content> <ng-container *ngIf="template" [ngTemplateOutlet]="template" - [ngTemplateOutletContext]="{person: dataContext}"> + [ngTemplateOutletContext]="{data: dataContext}"> </ng-container> </div> ` From 807c472905bed4789848385fe5e39d1f3ee4799e Mon Sep 17 00:00:00 2001 From: Juri <juri.strumpflohner@gmail.com> Date: Sat, 11 Nov 2017 18:09:02 +0100 Subject: [PATCH 08/12] feat: add anchor directive --- src/app/tabs/dynamic-tab-anchor.directive.ts | 8 ++++++++ src/app/tabs/tabs.component.ts | 11 ++++++++++- src/app/tabs/tabs.module.ts | 3 ++- 3 files changed, 20 insertions(+), 2 deletions(-) create mode 100644 src/app/tabs/dynamic-tab-anchor.directive.ts diff --git a/src/app/tabs/dynamic-tab-anchor.directive.ts b/src/app/tabs/dynamic-tab-anchor.directive.ts new file mode 100644 index 0000000..9f3bd88 --- /dev/null +++ b/src/app/tabs/dynamic-tab-anchor.directive.ts @@ -0,0 +1,8 @@ +import { Directive, ViewContainerRef } from '@angular/core'; + +@Directive({ + selector: '[dynamicTabAnchor]' +}) +export class DynamicTabAnchorDirective { + constructor(public viewContainer: ViewContainerRef) {} +} diff --git a/src/app/tabs/tabs.component.ts b/src/app/tabs/tabs.component.ts index 06f494e..34bcf90 100644 --- a/src/app/tabs/tabs.component.ts +++ b/src/app/tabs/tabs.component.ts @@ -2,9 +2,11 @@ import { Component, ContentChildren, QueryList, - AfterContentInit + AfterContentInit, + ViewChild } from '@angular/core'; import { TabComponent } from './tab.component'; +import { DynamicTabAnchorDirective } from './dynamic-tab-anchor.directive'; @Component({ selector: 'ngx-tabs', @@ -15,10 +17,13 @@ import { TabComponent } from './tab.component'; </li> </ul> <ng-content></ng-content> + <ng-template dynamicTabAnchor></ng-template> ` }) export class TabsComponent implements AfterContentInit { @ContentChildren(TabComponent) tabs: QueryList<TabComponent>; + @ViewChild(DynamicTabAnchorDirective) + dynamicTabPlaceholder: DynamicTabAnchorDirective; // contentChildren are set ngAfterContentInit() { @@ -31,6 +36,10 @@ export class TabsComponent implements AfterContentInit { } } + openTab() { + console.log(this.dynamicTabPlaceholder.viewContainer); + } + selectTab(tab: TabComponent) { // deactivate all tabs this.tabs.toArray().forEach(tab => (tab.active = false)); diff --git a/src/app/tabs/tabs.module.ts b/src/app/tabs/tabs.module.ts index d4528ee..736ed5d 100644 --- a/src/app/tabs/tabs.module.ts +++ b/src/app/tabs/tabs.module.ts @@ -2,10 +2,11 @@ import { NgModule } from '@angular/core'; import { CommonModule } from '@angular/common'; import { TabsComponent } from './tabs.component'; import { TabComponent } from './tab.component'; +import { DynamicTabAnchorDirective } from './dynamic-tab-anchor.directive'; @NgModule({ imports: [CommonModule], - declarations: [TabsComponent, TabComponent], + declarations: [TabsComponent, TabComponent, DynamicTabAnchorDirective], exports: [TabsComponent, TabComponent] }) export class TabsModule {} From faf4183aae7e0691adb2c831e5089e2b5adc9430 Mon Sep 17 00:00:00 2001 From: Juri <juri.strumpflohner@gmail.com> Date: Sat, 11 Nov 2017 18:12:20 +0100 Subject: [PATCH 09/12] feat: hook up openTab() function s.t. it can be called --- src/app/app.component.ts | 8 +++++++- src/app/tabs/tabs.component.ts | 2 +- 2 files changed, 8 insertions(+), 2 deletions(-) diff --git a/src/app/app.component.ts b/src/app/app.component.ts index 1de4015..92eced1 100644 --- a/src/app/app.component.ts +++ b/src/app/app.component.ts @@ -1,5 +1,6 @@ import { Component, OnInit, ViewChild, AfterViewInit } from '@angular/core'; import { PeopleService } from './people/people.service'; +import { TabsComponent } from './tabs/tabs.component'; @Component({ selector: 'app-root', @@ -7,7 +8,7 @@ import { PeopleService } from './people/people.service'; <h1>Angular tabs</h1> <ngx-tabs> <ngx-tab tabTitle="People List"> - <app-people-list [people]="people"></app-people-list> + <app-people-list [people]="people" (addPerson)="onAddPerson()"></app-people-list> </ngx-tab> <ngx-tab tabTitle="Tab 2" [template]="personEdit" [dataContext]="people[0]"></ngx-tab> </ngx-tabs> @@ -19,6 +20,7 @@ import { PeopleService } from './people/people.service'; }) export class AppComponent implements OnInit, AfterViewInit { @ViewChild('personEdit') personEditTemplate; + @ViewChild(TabsComponent) tabsComponent; people; constructor(private peopleService: PeopleService) {} @@ -32,4 +34,8 @@ export class AppComponent implements OnInit, AfterViewInit { ngAfterViewInit() { console.log(this.personEditTemplate); } + + onAddPerson() { + this.tabsComponent.openTab(); + } } diff --git a/src/app/tabs/tabs.component.ts b/src/app/tabs/tabs.component.ts index 34bcf90..3d241c5 100644 --- a/src/app/tabs/tabs.component.ts +++ b/src/app/tabs/tabs.component.ts @@ -36,7 +36,7 @@ export class TabsComponent implements AfterContentInit { } } - openTab() { + public openTab() { console.log(this.dynamicTabPlaceholder.viewContainer); } From bda887cce1eb874ddcd35313714c03487ffbc3a4 Mon Sep 17 00:00:00 2001 From: Juri <juri.strumpflohner@gmail.com> Date: Sat, 11 Nov 2017 18:13:47 +0100 Subject: [PATCH 10/12] feat: show additional approach of retrieving ViewContainerRef --- src/app/tabs/tabs.component.ts | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/app/tabs/tabs.component.ts b/src/app/tabs/tabs.component.ts index 3d241c5..726a9dc 100644 --- a/src/app/tabs/tabs.component.ts +++ b/src/app/tabs/tabs.component.ts @@ -17,13 +17,15 @@ import { DynamicTabAnchorDirective } from './dynamic-tab-anchor.directive'; </li> </ul> <ng-content></ng-content> - <ng-template dynamicTabAnchor></ng-template> + <ng-template dynamicTabAnchor #container></ng-template> ` }) export class TabsComponent implements AfterContentInit { @ContentChildren(TabComponent) tabs: QueryList<TabComponent>; @ViewChild(DynamicTabAnchorDirective) dynamicTabPlaceholder: DynamicTabAnchorDirective; + // @ViewChild('container', { read: ViewContainerRef }) + // dynamicTabPlaceholder; // contentChildren are set ngAfterContentInit() { From 78c0c0a39a1fabc43507684e283b21025a5afa6e Mon Sep 17 00:00:00 2001 From: Juri <juri.strumpflohner@gmail.com> Date: Thu, 16 Nov 2017 11:00:44 +0100 Subject: [PATCH 11/12] feat: some minor mods during lesson recording --- src/app/tabs/dynamic-tab-anchor.directive.ts | 2 +- src/app/tabs/tabs.component.ts | 17 +++++++++-------- 2 files changed, 10 insertions(+), 9 deletions(-) diff --git a/src/app/tabs/dynamic-tab-anchor.directive.ts b/src/app/tabs/dynamic-tab-anchor.directive.ts index 9f3bd88..b5eb23b 100644 --- a/src/app/tabs/dynamic-tab-anchor.directive.ts +++ b/src/app/tabs/dynamic-tab-anchor.directive.ts @@ -1,7 +1,7 @@ import { Directive, ViewContainerRef } from '@angular/core'; @Directive({ - selector: '[dynamicTabAnchor]' + selector: '[appDynamicTabAnchor]' }) export class DynamicTabAnchorDirective { constructor(public viewContainer: ViewContainerRef) {} diff --git a/src/app/tabs/tabs.component.ts b/src/app/tabs/tabs.component.ts index 726a9dc..fdf8ef2 100644 --- a/src/app/tabs/tabs.component.ts +++ b/src/app/tabs/tabs.component.ts @@ -3,9 +3,11 @@ import { ContentChildren, QueryList, AfterContentInit, - ViewChild + ViewChild, + ViewContainerRef } from '@angular/core'; import { TabComponent } from './tab.component'; + import { DynamicTabAnchorDirective } from './dynamic-tab-anchor.directive'; @Component({ @@ -17,15 +19,18 @@ import { DynamicTabAnchorDirective } from './dynamic-tab-anchor.directive'; </li> </ul> <ng-content></ng-content> - <ng-template dynamicTabAnchor #container></ng-template> + <ng-template appDynamicTabAnchor #container></ng-template> ` }) export class TabsComponent implements AfterContentInit { @ContentChildren(TabComponent) tabs: QueryList<TabComponent>; @ViewChild(DynamicTabAnchorDirective) dynamicTabPlaceholder: DynamicTabAnchorDirective; - // @ViewChild('container', { read: ViewContainerRef }) - // dynamicTabPlaceholder; + // @ViewChild('container', { read: ViewContainerRef }) placeholder; + + openTab() { + console.log(this.dynamicTabPlaceholder.viewContainer); + } // contentChildren are set ngAfterContentInit() { @@ -38,10 +43,6 @@ export class TabsComponent implements AfterContentInit { } } - public openTab() { - console.log(this.dynamicTabPlaceholder.viewContainer); - } - selectTab(tab: TabComponent) { // deactivate all tabs this.tabs.toArray().forEach(tab => (tab.active = false)); From d3ed000a99bc409fb9c8d598a95428f97d6ad8f7 Mon Sep 17 00:00:00 2001 From: Juri <juri.strumpflohner@gmail.com> Date: Thu, 16 Nov 2017 15:22:49 +0100 Subject: [PATCH 12/12] chore: update readme with links to lessons --- README.md | 37 ++++++++++++++++++++++--------------- 1 file changed, 22 insertions(+), 15 deletions(-) diff --git a/README.md b/README.md index 7c067d5..1dc704b 100644 --- a/README.md +++ b/README.md @@ -1,27 +1,34 @@ -# DynamicTabs +# Egghead Course: Create a Dynamic Tabs Component with Angular -This project was generated with [Angular CLI](https://github.com/angular/angular-cli) version 1.5.0. +by Juri Strumpflohner ([Twitter](https://twitter.com/juristr) - [Blog](https://juristr.com/blog)) -## Development server +This repository is organized in different branches, one branch for each video lesson. -Run `ng serve` for a dev server. Navigate to `http://localhost:4200/`. The app will automatically reload if you change any of the source files. +## Contents -## Code scaffolding +1. [Get to know our basic Angular TabsComponent](https://github.com/juristr/egghead-create-dynamic-tabs-component-angular/tree/master) +1. [Declare a template within a template using `ng-template` in Angular](https://github.com/juristr/egghead-create-dynamic-tabs-component-angular/tree/01-ng-template) +1. [Pass a reference of an ng-template to a component and render it in Angular](https://github.com/juristr/egghead-create-dynamic-tabs-component-angular/tree/02-ng-container-and-template-outlet) +1. [Pass data to be rendered in a dynamic ng-template using ngTemplateOutletContext in Angular](https://github.com/juristr/egghead-create-dynamic-tabs-component-angular/tree/03-ng-outlet-context) +1. Define an anchor point where to render dynamic components in Angular +1. [Dynamically instantiate an Angular component](https://github.com/juristr/egghead-create-dynamic-tabs-component-angular/tree/05-dynamically-instantiate-component) +1. [Destroy a dynamically instantiated Angular component](https://github.com/juristr/egghead-create-dynamic-tabs-component-angular/tree/06-destroy-dynamic-components) -Run `ng generate component component-name` to generate a new component. You can also use `ng generate directive|pipe|service|class|guard|interface|enum|module`. -## Build +## Setup & Run -Run `ng build` to build the project. The build artifacts will be stored in the `dist/` directory. Use the `-prod` flag for a production build. +Clone the repository and install all packages -## Running unit tests +``` +$ npm install +``` -Run `ng test` to execute the unit tests via [Karma](https://karma-runner.github.io). +Run the project by executing -## Running end-to-end tests +``` +$ npm start +``` -Run `ng e2e` to execute the end-to-end tests via [Protractor](http://www.protractortest.org/). +## Questions? -## Further help - -To get more help on the Angular CLI use `ng help` or go check out the [Angular CLI README](https://github.com/angular/angular-cli/blob/master/README.md). +Feel free to reach out to me [on Twitter](https://twitter.com/juristr) or [open an issue](https://github.com/juristr/egghead-create-dynamic-tabs-component-angular/issues).