Skip to content

Instantly share code, notes, and snippets.

@duhast
Last active January 22, 2018 02:19
Show Gist options
  • Save duhast/bb8fe9f752c725aef42a3438a2303ab6 to your computer and use it in GitHub Desktop.
Save duhast/bb8fe9f752c725aef42a3438a2303ab6 to your computer and use it in GitHub Desktop.
[Angular 2] Attaching a component inside other component dynamically - total row in a table
import * as _ from 'lodash';
import { Component, DoCheck, Input, IterableDiffer, IterableDiffers, OnInit } from '@angular/core';
import { Trend } from '../../../models/trend-trade/trend-trade';
@Component({
template: `
<tr>
<td></td>
<td colspan="2">Total:</td>
<td>{{ totalValue }}</td>
</tr>
`,
})
export class GridTotalsRowComponent implements OnInit, DoCheck {
@Input() tableData: any[];
totalValue: number = 0;
private iterableDiff: IterableDiffer<number>;
constructor(private _iterableDiffers: IterableDiffers) {
this.iterableDiff = this._iterableDiffers.find([]).create(null);
}
ngOnInit() {
this.updateTotals();
}
ngDoCheck() {
const changes = this.iterableDiff.diff(this.tableData);
if (changes) {
this.updateTotals();
}
}
private updateTotals() {
this.totalValue = _.sumBy(this.tableData, 'val3');
}
}
import { ApplicationRef, ComponentFactoryResolver, ComponentRef, EmbeddedViewRef, Injector, Type } from '@angular/core';
export class HtmlContainer {
private attached: boolean = false;
// https://stackoverflow.com/questions/41949465
private disposeFn: () => void;
constructor(
private hostElement: Element,
private appRef: ApplicationRef,
private componentFactoryResolver: ComponentFactoryResolver,
private injector: Injector) {
}
attach(component: Type<any>): ComponentRef<any> {
if (this.attached) {
throw new Error('component has already been attached');
}
this.attached = true;
const childComponentFactory = this.componentFactoryResolver.resolveComponentFactory(component);
const componentRef = childComponentFactory.create(this.injector);
this.appRef.attachView(componentRef.hostView);
this.disposeFn = () => {
this.appRef.detachView(componentRef.hostView);
componentRef.destroy();
};
this.hostElement.appendChild((componentRef.hostView as EmbeddedViewRef<any>).rootNodes[0]);
return componentRef;
}
dispose() {
if (this.attached) {
this.disposeFn();
}
}
}
import { GridTotalsRowComponent } from './grid-totals-row.component';
import { HtmlContainer } from './html-container';
@Component({
selector: 'app-my-table',
template: `
<table #grid>
<tr *ngFor="let row of data">
<td>{{ val1 }}</td>
<td>{{ val2 }}</td>
<td>{{ val3 }}</td>
</tr>
</table>
`,
styleUrls: ['./instruments.component.scss'],
})
export class InstrumentsComponent implements OnInit, OnDestroy {
@ViewChild('grid', { read: ViewContainerRef }) grid: ViewContainerRef;
publc data: any[] = [
{val1: 11, val2: 22, val3: 33},
{val1: 44, val2: 55, val3: 66},
{val1: 77, val2: 88, val3: 99},
];
private totalsContainer: HtmlContainer;
private totalsRef: ComponentRef<GridTotalsRowComponent>;
constructor(pprivate componentFactoryResolver: ComponentFactoryResolver, private appRef: ApplicationRef, private injector: Injector) {
}
ngOnInit() {
const tableEl: HTMLElement = this.grid.element.nativeElement.children[0];
this.totalsContainer = new HtmlContainer(tableEl, this.appRef, this.componentFactoryResolver, this.injector);
this.totalsRef = this.totalsContainer.attach(GridTotalsRowComponent);
this.totalsRef.instance.tableData = this.data; // Pass component input here
}
ngOnDestroy() {
this.totalsContainer.dispose();
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment