Skip to content

Commit

Permalink
fix(ViewChild): finish ViewChild implementation and bugfixes
Browse files Browse the repository at this point in the history
  • Loading branch information
sternbach committed Feb 25, 2018
1 parent 2d45dbc commit ea40c33
Show file tree
Hide file tree
Showing 7 changed files with 45 additions and 32 deletions.
32 changes: 22 additions & 10 deletions src/component.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
import * as angular from 'angular';
import {
Declarations, defineMetadata, getAttributeName, getMetadata, isAttributeSelector, kebabToCamel,
camelToKebab,
Declaration, defineMetadata, getAttributeName, getMetadata, getTypeDeclaration, getTypeName, isAttributeSelector,
kebabToCamel,
metadataKeys
} from './utils';
import { IHostListeners } from './hostListener';
Expand Down Expand Up @@ -37,7 +39,7 @@ export function Component({selector, ...options}: ComponentOptionsDecorated) {

const selectorName = isAttrSelector ? getAttributeName(selector) : selector;
defineMetadata(metadataKeys.name, kebabToCamel(selectorName), ctrl);
defineMetadata(metadataKeys.declaration, isAttrSelector ? Declarations.directive : Declarations.component, ctrl);
defineMetadata(metadataKeys.declaration, isAttrSelector ? Declaration.Directive : Declaration.Component, ctrl);
defineMetadata(metadataKeys.options, options, ctrl);
};
}
Expand Down Expand Up @@ -76,16 +78,26 @@ export function extendWithHostListenersAndChildren(ctrl: {new(...args: any[])},
});
properties.forEach(property => {
const child = viewChildren[property];
const viewChildEls = this.$element[0].querySelectorAll(child.selector)
.map(viewChild => angular.element(viewChild).isolateScope<any>()['$ctrl']);
let selector: string;
if (typeof child.selector !== 'string') {
const type = getTypeDeclaration(child.selector);
if (type !== Declaration.Component && type !== Declaration.Directive) {
console.error(`No valid selector was provided for ViewChild${child.first ? '' : 'ren'} decorator, it should be type or selector of component/directive`);
return;
}
selector = camelToKebab(getTypeName(child.selector));
} else selector = child.selector;

if (child.first && viewChildEls.length) {
this[property] = viewChildEls[0];
}
else {
this[property] = viewChildEls;
const viewChildEls = Array.from(this.$element[0].querySelectorAll(selector)).map((viewChild: Element) => {
const el = angular.element(viewChild);
const scope = el && el.isolateScope && el.isolateScope();
return scope ? scope['$ctrl'] : el;
}).filter(el => !!el);

if (viewChildEls.length) {
this[property] = child.first ? viewChildEls[0] : viewChildEls;
}
})
});
}
$onDestroy() {
if (super.$onDestroy) {
Expand Down
4 changes: 2 additions & 2 deletions src/directive.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import {
Declarations, defineMetadata, getAttributeName, getMetadata, isAttributeSelector, kebabToCamel,
Declaration, defineMetadata, getAttributeName, getMetadata, isAttributeSelector, kebabToCamel,
metadataKeys
} from './utils';
import { IHostListeners } from './hostListener';
Expand All @@ -25,7 +25,7 @@ export function Directive({selector, ...options}: DirectiveOptionsDecorated) {

const selectorName = isAttributeSelector(selector) ? getAttributeName(selector) : selector;
defineMetadata(metadataKeys.name, kebabToCamel(selectorName), ctrl);
defineMetadata(metadataKeys.declaration, Declarations.directive, ctrl);
defineMetadata(metadataKeys.declaration, Declaration.Directive, ctrl);
defineMetadata(metadataKeys.options, options, ctrl);
};
}
Expand Down
8 changes: 4 additions & 4 deletions src/module.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import * as angular from 'angular';
import { PipeTransform, registerPipe } from './pipe';
import { registerProviders } from './injectable';
import { camelToKebab, Declarations, getMetadata, getTypeName, metadataKeys } from './utils';
import { camelToKebab, Declaration, getMetadata, getTypeName, metadataKeys } from './utils';
import { registerComponent } from './component';
import { registerDirective } from './directive';
import { Provider } from './provider';
Expand Down Expand Up @@ -37,13 +37,13 @@ export function NgModule({ id, bootstrap = [], declarations = [], imports = [],
declarations.forEach((declaration: any) => {
const declarationType = getMetadata(metadataKeys.declaration, declaration);
switch (declarationType) {
case Declarations.component:
case Declaration.Component:
registerComponent(module, declaration);
break;
case Declarations.directive:
case Declaration.Directive:
registerDirective(module, declaration);
break;
case Declarations.pipe:
case Declaration.Pipe:
registerPipe(module, declaration);
break;
default:
Expand Down
4 changes: 2 additions & 2 deletions src/pipe.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { Declarations, defineMetadata, getMetadata, metadataKeys } from './utils';
import { Declaration, defineMetadata, getMetadata, metadataKeys } from './utils';
import { IModule } from 'angular';

export interface PipeTransformConstructor {
Expand All @@ -12,7 +12,7 @@ export interface PipeTransform {
export function Pipe(options: {name: string}) {
return (Class: PipeTransformConstructor) => {
defineMetadata(metadataKeys.name, options.name, Class);
defineMetadata(metadataKeys.declaration, Declarations.pipe, Class);
defineMetadata(metadataKeys.declaration, Declaration.Pipe, Class);
};
}

Expand Down
8 changes: 6 additions & 2 deletions src/utils.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import 'reflect-metadata';

/** @internal */
export enum Declarations { component, directive, pipe }
export enum Declaration { Component = 'Component', Directive = 'Directive', Pipe = 'Pipe' }

/** @internal */
export const metadataKeys = {
Expand Down Expand Up @@ -43,6 +43,10 @@ export function defineMetadata(metadataKey: any, metadataValue: any, target: any
Reflect.defineMetadata(metadataKey, metadataValue, target);
}

export function getTypeName(target: any) {
export function getTypeName(target: any): string {
return getMetadata(metadataKeys.name, target);
}

export function getTypeDeclaration(target: any): Declaration {
return getMetadata(metadataKeys.declaration, target);
}
18 changes: 7 additions & 11 deletions src/viewChild.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,25 +8,21 @@ export interface IViewChildren {
[property: string]: {
first: boolean;
selector: any;
}
};
}

export function ViewChild(selector: any): any {
export function ViewChild(selector: Type<any>|Function|string, opts?: {read?: any}): any {
return (target: any, key: string) => addBindingToMetadata(target, key, selector, true);
}

export function ViewChildren(selector: any): any {
export function ViewChildren(selector: Type<any>|Function|string, opts?: {read?: any}): any {
return (target: any, key: string) => addBindingToMetadata(target, key, selector, false);
}

/** @internal */
function addBindingToMetadata(target: any, key: string, type: any, first: boolean) {
function addBindingToMetadata(target: any, key: string, selector: Type<any>|Function|string, first: boolean) {
const targetConstructor = target.constructor;
const selector = camelToKebab(getTypeName(type));
const children: IViewChildren = getMetadata(metadataKeys.viewChildren, targetConstructor) || {};
children[key] = {
first,
selector: selector
};
defineMetadata(metadataKeys.viewChildren, children, targetConstructor);
const viewChildren: IViewChildren = getMetadata(metadataKeys.viewChildren, targetConstructor) || {};
viewChildren[key] = { first, selector };
defineMetadata(metadataKeys.viewChildren, viewChildren, targetConstructor);
}
3 changes: 2 additions & 1 deletion tsconfig.json
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,8 @@
"declarationDir": "dist/types",
"stripInternal": true,
"outDir": "dist/temp",
"importHelpers": true
"importHelpers": true,
"lib": ["dom", "es2015"]
},
"files": [
"src/index.ts"
Expand Down

0 comments on commit ea40c33

Please sign in to comment.