Slides Reference: bit.ly/ng-hoc
@pankajparkar
@pankajparkar
Senior Software Engineer, Deskera
@pankajparkar
We're going to use private API's here
In angular private API name generally starts with θ (Theta)
@pankajparkar
Load component on-demand whenever needed and render it onto the DOM
<button (click)="loadComponent()">Load Lazy Component</button>
loadComponent() {
import('./lazy-load/lazy-load.component')
.then(({LazyLoadComponent}) => {
ɵrenderComponent(LazyLoadComponent, {
injector,
host: 'my-lazy-load-component'
})
});
}
<div class="cotent">
Some other content
<my-lazy-load-component></my-lazy-load-component>
</div>
What 🤔?
Wait ✋
@pankajparkar
In javascript we have
A higher-order function is a function that can take another function as an argument, or that returns a function as a result
@pankajparkar
function add (x, y) {
return x + y
}
function addFive (x, addReference) {
return addReference(x, 5)
}
addFive(10, add) // 15
@pankajparkar
function add (x,y) {
return x + y
}
function higherOrderFunction (x, callback) {
return callback(x, 5)
}
higherOrderFunction(10, add)
@pankajparkar
function add (x, y) {
return x + y
}
function addFive (x, addReference) {
return addReference(x, 5)
}
function addTen (x, addReference) {
return addReference(x, 10)
}
function addTwenty (x, addReference) {
return addReference(x, 20)
}
addFive(10, add) // 15
addTen(10, add) // 20
addTwenty(10, add) // 30
function add (x, y) {
return x + y
}
function makeAdder (x, addReference) {
return function (y) {
return addReference(x, y)
}
}
const addFive = makeAdder(5, add)
const addTen = makeAdder(10, add)
const addTwenty = makeAdder(20, add)
addFive(10) // 15
addTen(10) // 20
addTwenty(10) // 30
@pankajparkar
function higherOrderFunction (callback) {
return function () {
return callback()
}
}
@pankajparkar
const NewComponent = withTooltip(PageComponent);
Function takes an input as a component and generate another component
PageComponent
function withTooltip(componentRef) {
// Attach events to show
// Tooltip on hover
return componentRef;
}
PageComponent
withTooltip
@pankajparkar
Ivy Working
export class PageComponent {
constructor() {}
ngOnInit() {}
}
PageComponent.ɵfac = function PageComponent_Factory(t) {
return new (t || PageComponent)();
};
PageComponent.ɵcmp = i0.ɵɵdefineComponent({
type: PageComponent,
selectors: [["app-pager"]],
ngContentSelectors: _c0,
template: function PageComponent_Template(rf, ctx) { if (rf & 1) {
i0.ɵɵprojectionDef();
i0.ɵɵelementStart(0, "header");
i0.ɵɵtext(1, "Header is here");
i0.ɵɵelementEnd();
} },
directives: [i1.PageComponent],
styles: ["[_nghost-%COMP%] {\n display: flex;\n justify-content: space-around;\n flex-flow: column nowrap;\n header {\n height: 20px;\n }\n .section {\n height: calc(100vh-40px);\n }\n footer {\n height: 20px;\n }\n}"] });
/*@__PURE__*/
(function () { i0.ɵsetClassMetadata(PageComponent, [{
type: Component,
args: [{
selector: 'app-pager',
templateUrl: './pager.component.html',
styleUrls: ['./pager.component.scss']
}]
}], function () { return []; }, null);
})();
@Component({
selector: 'app-page',
templateUrl: './page.component.html',
styleUrls: ['./page.component.scss']
})
export class PageComponent implements OnInit {
constructor() { }
ngOnInit() {
}
}
Can be achieved using two Types
@pankajparkar
@withHoc()
@Component({...})
export class PageComponent {
@Input() id: string;
...
}
@pankajparkar
@Component({...})
export class PageComponent {
@Input() pageName: string;
...
}
const PageWithRouter = withRouter(PageComponent)
const routes = [
{path: 'page', component: PageComponent},
{path: 'page/:id', component: PageWithRouter}
]
@pankajparkar
export function fadeIn() {
return (cmpType) => {
console.dir(cmpType)
const originalFactory = cmpType.ɵfac;
cmpType.ɵfac = (...args) => {
const cmp = originalFactory(...args);
}
}
}
@pankajparkar
export function fadeIn(cmpType) {
console.dir(cmpType)
const originalFactory = cmpType.ɵfac;
cmpType.ɵfac = (...args) => {
originalFactory(...args);
}
}
@pankajparkar
import { Component, OnInit,
Injector, ElementRef, Input } from '@angular/core';
import { fadeIn } from '../hoc/decorators/animation/fadeIn';
@fadeIn()
@Component({
selector: 'app-page',
templateUrl: './page.component.html',
styleUrls: ['./page.component.scss'],
})
export class PageComponent implements OnInit {
@Input() test: string;
constructor(
private injector: Injector
) {}
ngAfterViewInit() {
console.log(this, 'After View INit')
}
ngOnInit() {
console.log('routeParams', this.test);
}
}
import { ElementRef, ɵComponentType, ɵComponentDef, INJECTOR, ɵɵdirectiveInject } from '@angular/core';
import { AnimationBuilder, query, style, stagger, animate } from '@angular/animations';
import { cloneDeep } from 'lodash';
function _buildAnimation(builder) { ... }
export function fadeIn() {
return (cmpType) => {
const originalFactory = cmpType.ɵfac;
cmpType.ɵfac = (...args) => {
const cmp = originalFactory(...args);
const originalAfterViewInit = cmp.afterViewInit;
cmp.afterViewInit = function afterViewInit(...args) {
if (originalAfterViewInit) {
originalAfterViewInit.apply(this, ...args);
}
const injector = this.injector;
const el = injector.get(ElementRef);
const builder = injector.get(AnimationBuilder);
const animation = _buildAnimation(builder);
animation.create(el.nativeElement).play();
}
return cmp;
};
return cmpType;
};
}
function _buildAnimation(builder) {
return builder.build([
query('*', [
style({ opacity: 0, transform: 'translateY(-50px)' }),
stagger(100, [
animate('500ms',
style({ opacity: 1, transform: 'none'
}))
])
])
]);
}
@pankajparkar
@pankajparkar
@pankajparkar
@pankajparkar
const NewPageComponent = withSelector(
withStyle(
withFadeIn(withRoute(PageComponent))
),
'new-page-component'
);
@NgModule({
declarations: [
PageComponent,
NewPageComponent,
...
],
imports: [...],
bootstrap: [AppComponent],
})
@pankajparkar
@pankajparkar