Skip to content

Instantly share code, notes, and snippets.

@ghetolay
Last active October 24, 2016 10:51
Show Gist options
  • Save ghetolay/b1ed92138ef17562789896208aee1d56 to your computer and use it in GitHub Desktop.
Save ghetolay/b1ed92138ef17562789896208aee1d56 to your computer and use it in GitHub Desktop.
Custom decorator to extend Angular 2 component
//Example of a custom decorator
import { forwardRef } from '@angular/core';
import { NG_VALUE_ACCESSOR } from '@angular/forms';
import { ExtendsComponent, ExtendsComponentMetadata } from './extendscomponent';
export var DatePicker = function( metadata: ExtendsComponentMetadata ): (cls: any) => any {
return (target: Function) => {
metadata.providers = metadata.providers || [];
metadata.providers.push({
provide: NG_VALUE_ACCESSOR,
useExisting: forwardRef(() => target),
multi: true
});
return ExtendsComponent(metadata)(target);
};
};
//only work for JIT not AOT
//That's why it's just partial code, maybe someday this will be usefull and will finish it.
import { Component, Provider, isDevMode } from '@angular/core';
export interface ExtendsComponentMetadata {
selector?: string;
template?: string;
templateUrl?: string;
styles?: string[];
styleUrls?: string[];
providers?: Provider[];
append?: {
styles?: string[];
styleUrls?: string[];
providers?: Provider[];
};
//TODO
//remove?: Component;
//reduce?: {
// styles, styleUrls, providers
//}
}
export function ExtendsComponent( componentMetadata: ExtendsComponentMetadata ): (cls: any) => any {
return function (target: Function) {
let metadata: Component = {};
let metas: (ExtendsComponentMetadata | Component)[] = [ componentMetadata ];
//find all metadatas across parents
let parentTarget = target;
do {
parentTarget = Object.getPrototypeOf(parentTarget.prototype).constructor;
let parentAnnot = Reflect.getMetadata('annotations', parentTarget);
if (parentAnnot) {
if (! (parentAnnot instanceof Array) ) {
//dunno what getMedata() can return just throw until we found a case
if (isDevMode) throw 'annotations not an array : ' + parentAnnot;
else continue;
}
for ( let annotation of parentAnnot ) {
/*
TODO if it's ExtendsComponent
*/
if (annotation instanceof Component)
metas.push(annotation);
}
}
} while ( parentTarget !== Object);
// loop from last to first
for ( let i = metas.length - 1; i >= 0; i--)
extendsMetadata(metadata, metas[i]);
return Component(metadata)(target);
};
}
function extendsMetadata(source: Component, target: (ExtendsComponentMetadata | Component)) {
for (let prop in target) {
let targetValue = target[prop];
if ( isBlank(targetValue) )
continue;
if (prop === 'append') {
for ( let appendProp in targetValue)
source[appendProp] = [...source[appendProp], targetValue[appendProp]];
}
else
source[prop] = targetValue;
};
}
//same func used on angular 2, dunno why they don't export it
function isBlank(v) {
return v === undefined || v === null;
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment