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/10] 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 d6075080b7427fd86c17f18a33484944bb3d3236 Mon Sep 17 00:00:00 2001 From: Juri <juri.strumpflohner@gmail.com> Date: Sat, 11 Nov 2017 17:25:27 +0100 Subject: [PATCH 02/10] 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 1c7aca7..96aa843 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 160f5e752a62b9fddd82251b88a64c734a5161db Mon Sep 17 00:00:00 2001 From: Juri <juri.strumpflohner@gmail.com> Date: Sat, 11 Nov 2017 17:44:43 +0100 Subject: [PATCH 03/10] 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 bcdd0b7052e1f12a81024125edb71cf79c562e56 Mon Sep 17 00:00:00 2001 From: Juri <juri.strumpflohner@gmail.com> Date: Sat, 11 Nov 2017 17:39:43 +0100 Subject: [PATCH 04/10] feat: add data to template --- src/app/app.component.ts | 6 +++--- src/app/tabs/tab.component.ts | 5 ++++- 2 files changed, 7 insertions(+), 4 deletions(-) diff --git a/src/app/app.component.ts b/src/app/app.component.ts index 96aa843..b7f63e6 100644 --- a/src/app/app.component.ts +++ b/src/app/app.component.ts @@ -9,11 +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'm a static template defined within the App component's template. + <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 10a8834..495222f 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></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 4c63a2ca17d60ba0cb8e26a64fa70c81565a5f37 Mon Sep 17 00:00:00 2001 From: Juri <juri.strumpflohner@gmail.com> Date: Sat, 11 Nov 2017 18:09:02 +0100 Subject: [PATCH 05/10] 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 b6a827aa3c6e1905d70fc3aa0f9dc330fbbfb9e5 Mon Sep 17 00:00:00 2001 From: Juri <juri.strumpflohner@gmail.com> Date: Sat, 11 Nov 2017 18:12:20 +0100 Subject: [PATCH 06/10] 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 b7f63e6..8dd279b 100644 --- a/src/app/app.component.ts +++ b/src/app/app.component.ts @@ -1,5 +1,6 @@ import { Component, OnInit, ViewChild } 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 { @ViewChild('personEdit') personEditTemplate; + @ViewChild(TabsComponent) tabsComponent; people; constructor(private peopleService: PeopleService) {} @@ -30,4 +32,8 @@ export class AppComponent implements OnInit { 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 ebe025c58c46f34345dab7fba61c6e4d666dd185 Mon Sep 17 00:00:00 2001 From: Juri <juri.strumpflohner@gmail.com> Date: Sat, 11 Nov 2017 18:13:47 +0100 Subject: [PATCH 07/10] 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 5832a6201225fd564b24f2de7f7364f48ce8cf4c Mon Sep 17 00:00:00 2001 From: Juri <juri.strumpflohner@gmail.com> Date: Sat, 11 Nov 2017 18:43:28 +0100 Subject: [PATCH 08/10] feat: implement dynamic tabs instantiation --- src/app/app.component.ts | 8 ++++++-- src/app/tabs/tabs.component.ts | 32 +++++++++++++++++++++++++++++--- src/app/tabs/tabs.module.ts | 3 ++- 3 files changed, 37 insertions(+), 6 deletions(-) diff --git a/src/app/app.component.ts b/src/app/app.component.ts index 8dd279b..42008c3 100644 --- a/src/app/app.component.ts +++ b/src/app/app.component.ts @@ -20,7 +20,7 @@ import { TabsComponent } from './tabs/tabs.component'; }) export class AppComponent implements OnInit { @ViewChild('personEdit') personEditTemplate; - @ViewChild(TabsComponent) tabsComponent; + @ViewChild(TabsComponent) tabsComponent: TabsComponent; people; constructor(private peopleService: PeopleService) {} @@ -34,6 +34,10 @@ export class AppComponent implements OnInit { } onAddPerson() { - this.tabsComponent.openTab(); + this.tabsComponent.openTab( + 'Dynamic title', + this.personEditTemplate, + this.people[0] + ); } } diff --git a/src/app/tabs/tabs.component.ts b/src/app/tabs/tabs.component.ts index 726a9dc..ec303c5 100644 --- a/src/app/tabs/tabs.component.ts +++ b/src/app/tabs/tabs.component.ts @@ -3,7 +3,8 @@ import { ContentChildren, QueryList, AfterContentInit, - ViewChild + ViewChild, + ComponentFactoryResolver } from '@angular/core'; import { TabComponent } from './tab.component'; import { DynamicTabAnchorDirective } from './dynamic-tab-anchor.directive'; @@ -15,6 +16,10 @@ import { DynamicTabAnchorDirective } from './dynamic-tab-anchor.directive'; <li *ngFor="let tab of tabs" (click)="selectTab(tab)" [class.active]="tab.active"> <a href="#">{{tab.tabTitle}}</a> </li> + <!-- dynamic tabs --> + <li *ngFor="let tab of dynamicTabs" (click)="selectTab(tab)" [class.active]="tab.active"> + <a href="#">{{tab.tabTitle}}</a> + </li> </ul> <ng-content></ng-content> <ng-template dynamicTabAnchor #container></ng-template> @@ -26,6 +31,9 @@ export class TabsComponent implements AfterContentInit { dynamicTabPlaceholder: DynamicTabAnchorDirective; // @ViewChild('container', { read: ViewContainerRef }) // dynamicTabPlaceholder; + dynamicTabs: TabComponent[] = []; + + constructor(private componentFactoryResolver: ComponentFactoryResolver) {} // contentChildren are set ngAfterContentInit() { @@ -38,13 +46,31 @@ export class TabsComponent implements AfterContentInit { } } - public openTab() { - console.log(this.dynamicTabPlaceholder.viewContainer); + openTab(title: string, template, data) { + const componentFactory = this.componentFactoryResolver.resolveComponentFactory( + TabComponent + ); + + const viewContainerRef = this.dynamicTabPlaceholder.viewContainer; + + // create a component instance + const componentRef = viewContainerRef.createComponent(componentFactory); + + // set the according properties on our component instance + const instance: TabComponent = componentRef.instance as TabComponent; + instance.tabTitle = title; + instance.template = template; + instance.dataContext = data; + + this.dynamicTabs.push(instance); + + this.selectTab(this.dynamicTabs[this.dynamicTabs.length - 1]); } selectTab(tab: TabComponent) { // deactivate all tabs this.tabs.toArray().forEach(tab => (tab.active = false)); + this.dynamicTabs.forEach(tab => (tab.active = false)); // activate the tab the user has clicked on. tab.active = true; diff --git a/src/app/tabs/tabs.module.ts b/src/app/tabs/tabs.module.ts index 736ed5d..240e740 100644 --- a/src/app/tabs/tabs.module.ts +++ b/src/app/tabs/tabs.module.ts @@ -7,6 +7,7 @@ import { DynamicTabAnchorDirective } from './dynamic-tab-anchor.directive'; @NgModule({ imports: [CommonModule], declarations: [TabsComponent, TabComponent, DynamicTabAnchorDirective], - exports: [TabsComponent, TabComponent] + exports: [TabsComponent, TabComponent], + entryComponents: [TabComponent] }) export class TabsModule {} From 013bd0376f520ce1b6ce3ccb7b699cb01a5a1ba0 Mon Sep 17 00:00:00 2001 From: Juri <juri.strumpflohner@gmail.com> Date: Thu, 16 Nov 2017 12:40:43 +0100 Subject: [PATCH 09/10] fix: adjustments for recording --- src/app/app.component.ts | 11 +++++------ src/app/tabs/tab.component.ts | 2 +- src/app/tabs/tabs.component.ts | 28 +++++++++++----------------- 3 files changed, 17 insertions(+), 24 deletions(-) diff --git a/src/app/app.component.ts b/src/app/app.component.ts index 42008c3..af7bc84 100644 --- a/src/app/app.component.ts +++ b/src/app/app.component.ts @@ -8,12 +8,13 @@ import { TabsComponent } from './tabs/tabs.component'; <h1>Angular tabs</h1> <ngx-tabs> <ngx-tab tabTitle="People List"> - <app-people-list [people]="people" (addPerson)="onAddPerson()"></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> - <ng-template let-person="person" #personEdit> + <ng-template let-person="data" #personEdit> Hi, I'm {{ person?.name }}. </ng-template> ` @@ -29,13 +30,11 @@ export class AppComponent implements OnInit { this.peopleService.getPeople().subscribe(data => { this.people = data; }); - - console.log(this.personEditTemplate); } onAddPerson() { this.tabsComponent.openTab( - 'Dynamic title', + 'Dynamic tab', this.personEditTemplate, this.people[0] ); diff --git a/src/app/tabs/tab.component.ts b/src/app/tabs/tab.component.ts index 495222f..5127671 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></ng-content> <ng-container *ngIf="template" [ngTemplateOutlet]="template" - [ngTemplateOutletContext]="{person: dataContext}"> + [ngTemplateOutletContext]="{data: dataContext}"> </ng-container> </div> ` diff --git a/src/app/tabs/tabs.component.ts b/src/app/tabs/tabs.component.ts index ec303c5..fb2da6d 100644 --- a/src/app/tabs/tabs.component.ts +++ b/src/app/tabs/tabs.component.ts @@ -16,7 +16,6 @@ import { DynamicTabAnchorDirective } from './dynamic-tab-anchor.directive'; <li *ngFor="let tab of tabs" (click)="selectTab(tab)" [class.active]="tab.active"> <a href="#">{{tab.tabTitle}}</a> </li> - <!-- dynamic tabs --> <li *ngFor="let tab of dynamicTabs" (click)="selectTab(tab)" [class.active]="tab.active"> <a href="#">{{tab.tabTitle}}</a> </li> @@ -29,23 +28,10 @@ export class TabsComponent implements AfterContentInit { @ContentChildren(TabComponent) tabs: QueryList<TabComponent>; @ViewChild(DynamicTabAnchorDirective) dynamicTabPlaceholder: DynamicTabAnchorDirective; - // @ViewChild('container', { read: ViewContainerRef }) - // dynamicTabPlaceholder; dynamicTabs: TabComponent[] = []; constructor(private componentFactoryResolver: ComponentFactoryResolver) {} - // contentChildren are set - ngAfterContentInit() { - // get all active tabs - const activeTabs = this.tabs.filter(tab => tab.active); - - // if there is no active tab set, activate the first - if (activeTabs.length === 0) { - this.selectTab(this.tabs.first); - } - } - openTab(title: string, template, data) { const componentFactory = this.componentFactoryResolver.resolveComponentFactory( TabComponent @@ -53,10 +39,7 @@ export class TabsComponent implements AfterContentInit { const viewContainerRef = this.dynamicTabPlaceholder.viewContainer; - // create a component instance const componentRef = viewContainerRef.createComponent(componentFactory); - - // set the according properties on our component instance const instance: TabComponent = componentRef.instance as TabComponent; instance.tabTitle = title; instance.template = template; @@ -67,6 +50,17 @@ export class TabsComponent implements AfterContentInit { this.selectTab(this.dynamicTabs[this.dynamicTabs.length - 1]); } + // contentChildren are set + ngAfterContentInit() { + // get all active tabs + const activeTabs = this.tabs.filter(tab => tab.active); + + // if there is no active tab set, activate the first + if (activeTabs.length === 0) { + this.selectTab(this.tabs.first); + } + } + selectTab(tab: TabComponent) { // deactivate all tabs this.tabs.toArray().forEach(tab => (tab.active = false)); From 03cf6458e80c25118a55e6f687917f97a260eddb Mon Sep 17 00:00:00 2001 From: Juri <juri.strumpflohner@gmail.com> Date: Thu, 16 Nov 2017 15:23:12 +0100 Subject: [PATCH 10/10] chore: update readme with links to lessons --- README.md | 38 ++++++++++++++++++++++---------------- 1 file changed, 22 insertions(+), 16 deletions(-) diff --git a/README.md b/README.md index 7c067d5..01c4d57 100644 --- a/README.md +++ b/README.md @@ -1,27 +1,33 @@ -# 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](https://github.com/juristr/egghead-create-dynamic-tabs-component-angular/tree/04-define-anchor-point) +1. Dynamically instantiate an Angular 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`. +## Setup & Run -## Build +Clone the repository and install all packages -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. +``` +$ npm install +``` -## Running unit tests +Run the project by executing -Run `ng test` to execute the unit tests via [Karma](https://karma-runner.github.io). +``` +$ npm start +``` -## Running end-to-end tests +## Questions? -Run `ng e2e` to execute the end-to-end tests via [Protractor](http://www.protractortest.org/). - -## 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).