parent
96770e5c6b
commit
578e4c7642
@ -217,8 +217,8 @@ export interface R3QueryMetadata {
|
|||||||
descendants: boolean;
|
descendants: boolean;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* An expression representing a type to read from each matched node, or null if the node itself
|
* An expression representing a type to read from each matched node, or null if the default value
|
||||||
* is to be returned.
|
* for a given node is to be returned.
|
||||||
*/
|
*/
|
||||||
read: o.Expression|null;
|
read: o.Expression|null;
|
||||||
}
|
}
|
||||||
|
@ -6,8 +6,9 @@
|
|||||||
* found in the LICENSE file at https://angular.io/license
|
* 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 {Component, Directive, HostBinding, HostListener, Input, Output} from '../../metadata/directives';
|
||||||
import {componentNeedsResolution, maybeQueueResolutionOfComponentResources} from '../../metadata/resource_loading';
|
import {componentNeedsResolution, maybeQueueResolutionOfComponentResources} from '../../metadata/resource_loading';
|
||||||
import {ViewEncapsulation} from '../../metadata/view';
|
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;
|
metadata.animations !== null ? new WrappedNodeExpr(metadata.animations) : null;
|
||||||
|
|
||||||
// Compile the component metadata, including template, into an expression.
|
// Compile the component metadata, including template, into an expression.
|
||||||
// TODO(alxhub): implement inputs, outputs, queries, etc.
|
|
||||||
const res = compileR3Component(
|
const res = compileR3Component(
|
||||||
{
|
{
|
||||||
...directiveMetadata(type, metadata),
|
...directiveMetadata(type, metadata),
|
||||||
template,
|
template,
|
||||||
directives: new Map(),
|
directives: new Map(),
|
||||||
pipes: new Map(),
|
pipes: new Map(),
|
||||||
viewQueries: [],
|
viewQueries: extractQueriesMetadata(getReflect().propMetadata(type), isViewQuery),
|
||||||
wrapDirectivesAndPipesInClosure: false,
|
wrapDirectivesAndPipesInClosure: false,
|
||||||
styles: metadata.styles || [],
|
styles: metadata.styles || [],
|
||||||
encapsulation: metadata.encapsulation || ViewEncapsulation.Emulated, animations,
|
encapsulation: metadata.encapsulation || ViewEncapsulation.Emulated, animations,
|
||||||
@ -176,7 +176,7 @@ function directiveMetadata(type: Type<any>, metadata: Directive): R3DirectiveMet
|
|||||||
deps: reflectDependencies(type), host,
|
deps: reflectDependencies(type), host,
|
||||||
inputs: {...inputsFromMetadata, ...inputsFromType},
|
inputs: {...inputsFromMetadata, ...inputsFromType},
|
||||||
outputs: {...outputsFromMetadata, ...outputsFromType},
|
outputs: {...outputsFromMetadata, ...outputsFromType},
|
||||||
queries: [],
|
queries: extractQueriesMetadata(propMetadata, isContentQuery),
|
||||||
lifecycle: {
|
lifecycle: {
|
||||||
usesOnChanges: type.prototype.ngOnChanges !== undefined,
|
usesOnChanges: type.prototype.ngOnChanges !== undefined,
|
||||||
},
|
},
|
||||||
@ -215,6 +215,38 @@ function extractHostBindings(metadata: Directive, propMetadata: {[key: string]:
|
|||||||
return {attributes, listeners, properties};
|
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 {
|
function isInput(value: any): value is Input {
|
||||||
return value.ngMetadataName === 'Input';
|
return value.ngMetadataName === 'Input';
|
||||||
}
|
}
|
||||||
@ -231,10 +263,24 @@ function isHostListener(value: any): value is HostListener {
|
|||||||
return value.ngMetadataName === '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 {
|
function parseInputOutputs(values: string[]): StringMap {
|
||||||
return values.reduce(
|
return values.reduce(
|
||||||
(map, value) => {
|
(map, value) => {
|
||||||
const [field, property] = value.split(',').map(piece => piece.trim());
|
const [field, property] = splitByComma(value);
|
||||||
map[field] = property || field;
|
map[field] = property || field;
|
||||||
return map;
|
return map;
|
||||||
},
|
},
|
||||||
|
@ -8,10 +8,12 @@
|
|||||||
|
|
||||||
import 'reflect-metadata';
|
import 'reflect-metadata';
|
||||||
|
|
||||||
|
import {ElementRef, QueryList} from '@angular/core';
|
||||||
import {InjectorDef, defineInjectable} from '@angular/core/src/di/defs';
|
import {InjectorDef, defineInjectable} from '@angular/core/src/di/defs';
|
||||||
import {Injectable} from '@angular/core/src/di/injectable';
|
import {Injectable} from '@angular/core/src/di/injectable';
|
||||||
import {inject, setCurrentInjector} from '@angular/core/src/di/injector_compatibility';
|
import {inject, setCurrentInjector} from '@angular/core/src/di/injector_compatibility';
|
||||||
import {ivyEnabled} from '@angular/core/src/ivy_switch';
|
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 {Component, Directive, HostBinding, HostListener, Input, Output, Pipe} from '@angular/core/src/metadata/directives';
|
||||||
import {NgModule, NgModuleDef} from '@angular/core/src/metadata/ng_module';
|
import {NgModule, NgModuleDef} from '@angular/core/src/metadata/ng_module';
|
||||||
import {ComponentDef, PipeDef} from '@angular/core/src/render3/interfaces/definition';
|
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).toBeDefined();
|
||||||
expect((C as any).ngBaseDef.outputs).toEqual({prop1: 'alias1', prop2: 'alias2'});
|
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', () => {});
|
it('ensure at least one spec exists', () => {});
|
||||||
|
@ -6,9 +6,23 @@
|
|||||||
* found in the LICENSE file at https://angular.io/license
|
* 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', () => {
|
it('should correctly behave with instanceof', () => {
|
||||||
expect(new Child() instanceof Object).toBeTruthy();
|
expect(new Child() instanceof Object).toBeTruthy();
|
||||||
expect(new Child() instanceof Parent).toBeTruthy();
|
expect(new Child() instanceof Parent).toBeTruthy();
|
||||||
@ -26,14 +40,57 @@ describe('extendsDirectlyFromObject', () => {
|
|||||||
expect(extendsDirectlyFromObject(Parent5)).toBeTruthy();
|
expect(extendsDirectlyFromObject(Parent5)).toBeTruthy();
|
||||||
expect(extendsDirectlyFromObject(Child5)).toBeFalsy();
|
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…
x
Reference in New Issue
Block a user