parent
96770e5c6b
commit
578e4c7642
|
@ -217,8 +217,8 @@ export interface R3QueryMetadata {
|
|||
descendants: boolean;
|
||||
|
||||
/**
|
||||
* An expression representing a type to read from each matched node, or null if the node itself
|
||||
* is to be returned.
|
||||
* An expression representing a type to read from each matched node, or null if the default value
|
||||
* for a given node is to be returned.
|
||||
*/
|
||||
read: o.Expression|null;
|
||||
}
|
||||
|
|
|
@ -6,8 +6,9 @@
|
|||
* found in the LICENSE file at https://angular.io/license
|
||||
*/
|
||||
|
||||
import {ConstantPool, R3DirectiveMetadata, WrappedNodeExpr, compileComponentFromMetadata as compileR3Component, compileDirectiveFromMetadata as compileR3Directive, jitExpression, makeBindingParser, parseHostBindings, parseTemplate} from '@angular/compiler';
|
||||
import {ConstantPool, Expression, R3DirectiveMetadata, R3QueryMetadata, WrappedNodeExpr, compileComponentFromMetadata as compileR3Component, compileDirectiveFromMetadata as compileR3Directive, jitExpression, makeBindingParser, parseHostBindings, parseTemplate} from '@angular/compiler';
|
||||
|
||||
import {Query} from '../../metadata/di';
|
||||
import {Component, Directive, HostBinding, HostListener, Input, Output} from '../../metadata/directives';
|
||||
import {componentNeedsResolution, maybeQueueResolutionOfComponentResources} from '../../metadata/resource_loading';
|
||||
import {ViewEncapsulation} from '../../metadata/view';
|
||||
|
@ -69,14 +70,13 @@ export function compileComponent(type: Type<any>, metadata: Component): void {
|
|||
metadata.animations !== null ? new WrappedNodeExpr(metadata.animations) : null;
|
||||
|
||||
// Compile the component metadata, including template, into an expression.
|
||||
// TODO(alxhub): implement inputs, outputs, queries, etc.
|
||||
const res = compileR3Component(
|
||||
{
|
||||
...directiveMetadata(type, metadata),
|
||||
template,
|
||||
directives: new Map(),
|
||||
pipes: new Map(),
|
||||
viewQueries: [],
|
||||
viewQueries: extractQueriesMetadata(getReflect().propMetadata(type), isViewQuery),
|
||||
wrapDirectivesAndPipesInClosure: false,
|
||||
styles: metadata.styles || [],
|
||||
encapsulation: metadata.encapsulation || ViewEncapsulation.Emulated, animations,
|
||||
|
@ -176,7 +176,7 @@ function directiveMetadata(type: Type<any>, metadata: Directive): R3DirectiveMet
|
|||
deps: reflectDependencies(type), host,
|
||||
inputs: {...inputsFromMetadata, ...inputsFromType},
|
||||
outputs: {...outputsFromMetadata, ...outputsFromType},
|
||||
queries: [],
|
||||
queries: extractQueriesMetadata(propMetadata, isContentQuery),
|
||||
lifecycle: {
|
||||
usesOnChanges: type.prototype.ngOnChanges !== undefined,
|
||||
},
|
||||
|
@ -215,6 +215,38 @@ function extractHostBindings(metadata: Directive, propMetadata: {[key: string]:
|
|||
return {attributes, listeners, properties};
|
||||
}
|
||||
|
||||
function convertToR3QueryPredicate(selector: any): Expression|string[] {
|
||||
return typeof selector === 'string' ? splitByComma(selector) : new WrappedNodeExpr(selector);
|
||||
}
|
||||
|
||||
export function convertToR3QueryMetadata(propertyName: string, ann: Query): R3QueryMetadata {
|
||||
return {
|
||||
propertyName: propertyName,
|
||||
predicate: convertToR3QueryPredicate(ann.selector),
|
||||
descendants: ann.descendants,
|
||||
first: ann.first,
|
||||
read: ann.read ? new WrappedNodeExpr(ann.read) : null
|
||||
};
|
||||
}
|
||||
|
||||
function extractQueriesMetadata(
|
||||
propMetadata: {[key: string]: any[]},
|
||||
isQueryAnn: (ann: any) => ann is Query): R3QueryMetadata[] {
|
||||
const queriesMeta: R3QueryMetadata[] = [];
|
||||
|
||||
for (const field in propMetadata) {
|
||||
if (propMetadata.hasOwnProperty(field)) {
|
||||
propMetadata[field].forEach(ann => {
|
||||
if (isQueryAnn(ann)) {
|
||||
queriesMeta.push(convertToR3QueryMetadata(field, ann));
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
return queriesMeta;
|
||||
}
|
||||
|
||||
function isInput(value: any): value is Input {
|
||||
return value.ngMetadataName === 'Input';
|
||||
}
|
||||
|
@ -231,10 +263,24 @@ function isHostListener(value: any): value is HostListener {
|
|||
return value.ngMetadataName === 'HostListener';
|
||||
}
|
||||
|
||||
function isContentQuery(value: any): value is Query {
|
||||
const name = value.ngMetadataName;
|
||||
return name === 'ContentChild' || name === 'ContentChildren';
|
||||
}
|
||||
|
||||
function isViewQuery(value: any): value is Query {
|
||||
const name = value.ngMetadataName;
|
||||
return name === 'ViewChild' || name === 'ViewChildren';
|
||||
}
|
||||
|
||||
function splitByComma(value: string): string[] {
|
||||
return value.split(',').map(piece => piece.trim());
|
||||
}
|
||||
|
||||
function parseInputOutputs(values: string[]): StringMap {
|
||||
return values.reduce(
|
||||
(map, value) => {
|
||||
const [field, property] = value.split(',').map(piece => piece.trim());
|
||||
const [field, property] = splitByComma(value);
|
||||
map[field] = property || field;
|
||||
return map;
|
||||
},
|
||||
|
|
|
@ -8,10 +8,12 @@
|
|||
|
||||
import 'reflect-metadata';
|
||||
|
||||
import {ElementRef, QueryList} from '@angular/core';
|
||||
import {InjectorDef, defineInjectable} from '@angular/core/src/di/defs';
|
||||
import {Injectable} from '@angular/core/src/di/injectable';
|
||||
import {inject, setCurrentInjector} from '@angular/core/src/di/injector_compatibility';
|
||||
import {ivyEnabled} from '@angular/core/src/ivy_switch';
|
||||
import {ContentChild, ContentChildren, ViewChild, ViewChildren} from '@angular/core/src/metadata/di';
|
||||
import {Component, Directive, HostBinding, HostListener, Input, Output, Pipe} from '@angular/core/src/metadata/directives';
|
||||
import {NgModule, NgModuleDef} from '@angular/core/src/metadata/ng_module';
|
||||
import {ComponentDef, PipeDef} from '@angular/core/src/render3/interfaces/definition';
|
||||
|
@ -304,6 +306,54 @@ ivyEnabled && describe('render3 jit', () => {
|
|||
expect((C as any).ngBaseDef).toBeDefined();
|
||||
expect((C as any).ngBaseDef.outputs).toEqual({prop1: 'alias1', prop2: 'alias2'});
|
||||
});
|
||||
|
||||
it('should compile ContentChildren query on a directive', () => {
|
||||
@Directive({selector: '[test]'})
|
||||
class TestDirective {
|
||||
@ContentChildren('foo') foos: QueryList<ElementRef>|undefined;
|
||||
}
|
||||
|
||||
expect((TestDirective as any).ngDirectiveDef.contentQueries).not.toBeNull();
|
||||
expect((TestDirective as any).ngDirectiveDef.contentQueriesRefresh).not.toBeNull();
|
||||
});
|
||||
|
||||
it('should compile ContentChild query on a directive', () => {
|
||||
@Directive({selector: '[test]'})
|
||||
class TestDirective {
|
||||
@ContentChild('foo') foo: ElementRef|undefined;
|
||||
}
|
||||
|
||||
expect((TestDirective as any).ngDirectiveDef.contentQueries).not.toBeNull();
|
||||
expect((TestDirective as any).ngDirectiveDef.contentQueriesRefresh).not.toBeNull();
|
||||
});
|
||||
|
||||
it('should not pick up view queries from directives', () => {
|
||||
@Directive({selector: '[test]'})
|
||||
class TestDirective {
|
||||
@ViewChildren('foo') foos: QueryList<ElementRef>|undefined;
|
||||
}
|
||||
|
||||
expect((TestDirective as any).ngDirectiveDef.contentQueries).toBeNull();
|
||||
expect((TestDirective as any).ngDirectiveDef.viewQuery).toBeNull();
|
||||
});
|
||||
|
||||
it('should compile ViewChild query on a component', () => {
|
||||
@Component({selector: 'test', template: ''})
|
||||
class TestComponent {
|
||||
@ViewChild('foo') foo: ElementRef|undefined;
|
||||
}
|
||||
|
||||
expect((TestComponent as any).ngComponentDef.foo).not.toBeNull();
|
||||
});
|
||||
|
||||
it('should compile ViewChildren query on a component', () => {
|
||||
@Component({selector: 'test', template: ''})
|
||||
class TestComponent {
|
||||
@ViewChildren('foo') foos: QueryList<ElementRef>|undefined;
|
||||
}
|
||||
|
||||
expect((TestComponent as any).ngComponentDef.viewQuery).not.toBeNull();
|
||||
});
|
||||
});
|
||||
|
||||
it('ensure at least one spec exists', () => {});
|
||||
|
|
|
@ -6,9 +6,23 @@
|
|||
* found in the LICENSE file at https://angular.io/license
|
||||
*/
|
||||
|
||||
import {extendsDirectlyFromObject} from '../../../src/render3/jit/directive';
|
||||
import {WrappedNodeExpr} from '@angular/compiler';
|
||||
import {convertToR3QueryMetadata, extendsDirectlyFromObject} from '../../../src/render3/jit/directive';
|
||||
|
||||
describe('jit directive helper functions', () => {
|
||||
|
||||
describe('extendsDirectlyFromObject', () => {
|
||||
|
||||
// Inheritance Example using Classes
|
||||
class Parent {}
|
||||
class Child extends Parent {}
|
||||
|
||||
// Inheritance Example using Function
|
||||
const Parent5 = function Parent5() {} as any as{new (): {}};
|
||||
const Child5 = function Child5() {} as any as{new (): {}};
|
||||
Child5.prototype = new Parent5;
|
||||
Child5.prototype.constructor = Child5;
|
||||
|
||||
describe('extendsDirectlyFromObject', () => {
|
||||
it('should correctly behave with instanceof', () => {
|
||||
expect(new Child() instanceof Object).toBeTruthy();
|
||||
expect(new Child() instanceof Parent).toBeTruthy();
|
||||
|
@ -26,14 +40,57 @@ describe('extendsDirectlyFromObject', () => {
|
|||
expect(extendsDirectlyFromObject(Parent5)).toBeTruthy();
|
||||
expect(extendsDirectlyFromObject(Child5)).toBeFalsy();
|
||||
});
|
||||
});
|
||||
|
||||
describe('convertToR3QueryMetadata', () => {
|
||||
|
||||
it('should convert decorator with a single string selector', () => {
|
||||
expect(convertToR3QueryMetadata('propName', {
|
||||
selector: 'localRef',
|
||||
descendants: false,
|
||||
first: false,
|
||||
isViewQuery: false,
|
||||
read: undefined
|
||||
})).toEqual({
|
||||
propertyName: 'propName',
|
||||
predicate: ['localRef'],
|
||||
descendants: false,
|
||||
first: false,
|
||||
read: null
|
||||
});
|
||||
});
|
||||
|
||||
it('should convert decorator with multiple string selectors', () => {
|
||||
expect(convertToR3QueryMetadata('propName', {
|
||||
selector: 'foo, bar,baz',
|
||||
descendants: true,
|
||||
first: true,
|
||||
isViewQuery: true,
|
||||
read: undefined
|
||||
})).toEqual({
|
||||
propertyName: 'propName',
|
||||
predicate: ['foo', 'bar', 'baz'],
|
||||
descendants: true,
|
||||
first: true,
|
||||
read: null
|
||||
});
|
||||
});
|
||||
|
||||
it('should convert decorator with type selector and read option', () => {
|
||||
|
||||
class Directive {}
|
||||
|
||||
const converted = convertToR3QueryMetadata('propName', {
|
||||
selector: Directive,
|
||||
descendants: true,
|
||||
first: true,
|
||||
isViewQuery: true,
|
||||
read: Directive
|
||||
});
|
||||
|
||||
expect(converted.predicate).toEqual(jasmine.any(WrappedNodeExpr));
|
||||
expect(converted.read).toEqual(jasmine.any(WrappedNodeExpr));
|
||||
});
|
||||
|
||||
});
|
||||
});
|
||||
|
||||
// Inheritance Example using Classes
|
||||
class Parent {}
|
||||
class Child extends Parent {}
|
||||
|
||||
// Inheritance Example using Function
|
||||
const Parent5 = function Parent5() {} as any as{new (): {}};
|
||||
const Child5 = function Child5() {} as any as{new (): {}};
|
||||
Child5.prototype = new Parent5;
|
||||
Child5.prototype.constructor = Child5;
|
||||
|
|
Loading…
Reference in New Issue