test(ivy): add injection canonical specs (#22595)

PR Close #22595
This commit is contained in:
Misko Hevery 2018-03-04 20:21:23 -08:00 committed by Kara Erickson
parent f13f4db9dc
commit 2fee5cc095
14 changed files with 403 additions and 128 deletions

View File

@ -14,7 +14,7 @@ export {
detectChanges as ɵdetectChanges, detectChanges as ɵdetectChanges,
renderComponent as ɵrenderComponent, renderComponent as ɵrenderComponent,
ComponentType as ɵComponentType, ComponentType as ɵComponentType,
inject as ɵinject, directiveInject as ɵdirectiveInject,
injectTemplateRef as ɵinjectTemplateRef, injectTemplateRef as ɵinjectTemplateRef,
injectViewContainerRef as ɵinjectViewContainerRef, injectViewContainerRef as ɵinjectViewContainerRef,
injectChangeDetectorRef as ɵinjectChangeDetectorRef, injectChangeDetectorRef as ɵinjectChangeDetectorRef,

View File

@ -47,7 +47,6 @@ export function defineComponent<T>(componentDefinition: ComponentDefArgs<T>): Co
inputs: invertObject(componentDefinition.inputs), inputs: invertObject(componentDefinition.inputs),
inputsPropertyName: componentDefinition.inputsPropertyName || null, inputsPropertyName: componentDefinition.inputsPropertyName || null,
outputs: invertObject(componentDefinition.outputs), outputs: invertObject(componentDefinition.outputs),
methods: invertObject(componentDefinition.methods),
rendererType: resolveRendererType2(componentDefinition.rendererType) || null, rendererType: resolveRendererType2(componentDefinition.rendererType) || null,
exportAs: componentDefinition.exportAs, exportAs: componentDefinition.exportAs,
onInit: type.prototype.ngOnInit || null, onInit: type.prototype.ngOnInit || null,

View File

@ -174,7 +174,7 @@ export function diPublic(def: DirectiveDef<any>): void {
} }
/** /**
* Searches for an instance of the given directive type up the injector tree and returns * Searches for an instance of the given type up the injector tree and returns
* that instance if found. * that instance if found.
* *
* If not found, it will propagate up to the next parent injector until the token * If not found, it will propagate up to the next parent injector until the token
@ -187,15 +187,18 @@ export function diPublic(def: DirectiveDef<any>): void {
* *
* static ngDirectiveDef = defineDirective({ * static ngDirectiveDef = defineDirective({
* type: SomeDirective, * type: SomeDirective,
* factory: () => new SomeDirective(inject(DirectiveA)) * factory: () => new SomeDirective(directiveInject(DirectiveA))
* }); * });
* } * }
* *
* NOTE: use `directiveInject` with `@Directive`, `@Component`, and `@Pipe`. For
* all other injection use `inject` which does not walk the DOM render tree.
*
* @param token The directive type to search for * @param token The directive type to search for
* @param flags Injection flags (e.g. CheckParent) * @param flags Injection flags (e.g. CheckParent)
* @returns The instance found * @returns The instance found
*/ */
export function inject<T>(token: Type<T>, flags?: InjectFlags, defaultValue?: T): T { export function directiveInject<T>(token: Type<T>, flags?: InjectFlags, defaultValue?: T): T {
return getOrCreateInjectable<T>(getOrCreateNodeInjector(), token, flags, defaultValue); return getOrCreateInjectable<T>(getOrCreateNodeInjector(), token, flags, defaultValue);
} }

View File

@ -11,7 +11,7 @@ import {NgOnChangesFeature, PublicFeature, defineComponent, defineDirective, def
import {InjectFlags} from './di'; import {InjectFlags} from './di';
import {ComponentDef, ComponentTemplate, ComponentType, DirectiveDef, DirectiveDefFlags, DirectiveType} from './interfaces/definition'; import {ComponentDef, ComponentTemplate, ComponentType, DirectiveDef, DirectiveDefFlags, DirectiveType} from './interfaces/definition';
export {InjectFlags, QUERY_READ_CONTAINER_REF, QUERY_READ_ELEMENT_REF, QUERY_READ_FROM_NODE, QUERY_READ_TEMPLATE_REF, inject, injectAttribute, injectChangeDetectorRef, injectElementRef, injectTemplateRef, injectViewContainerRef} from './di'; export {InjectFlags, QUERY_READ_CONTAINER_REF, QUERY_READ_ELEMENT_REF, QUERY_READ_FROM_NODE, QUERY_READ_TEMPLATE_REF, directiveInject, injectAttribute, injectChangeDetectorRef, injectElementRef, injectTemplateRef, injectViewContainerRef} from './di';
export {CssSelector} from './interfaces/projection'; export {CssSelector} from './interfaces/projection';

View File

@ -8,10 +8,13 @@
import {ChangeDetectionStrategy} from '../../change_detection/constants'; import {ChangeDetectionStrategy} from '../../change_detection/constants';
import {PipeTransform} from '../../change_detection/pipe_transform'; import {PipeTransform} from '../../change_detection/pipe_transform';
import {Provider} from '../../core';
import {RendererType2} from '../../render/api'; import {RendererType2} from '../../render/api';
import {Type} from '../../type'; import {Type} from '../../type';
import {resolveRendererType2} from '../../view/util'; import {resolveRendererType2} from '../../view/util';
/** /**
* Definition of what a template rendering function should look like. * Definition of what a template rendering function should look like.
*/ */
@ -19,16 +22,37 @@ export type ComponentTemplate<T> = {
(ctx: T, creationMode: boolean): void; ngPrivateData?: never; (ctx: T, creationMode: boolean): void; ngPrivateData?: never;
}; };
/**
* A subclass of `Type` which has a static `ngComponentDef`:`ComponentDef` field making it
* consumable for rendering.
*/
export interface ComponentType<T> extends Type<T> { ngComponentDef: ComponentDef<T>; } export interface ComponentType<T> extends Type<T> { ngComponentDef: ComponentDef<T>; }
/**
* A subclass of `Type` which has a static `ngDirectiveDef`:`DirectiveDef` field making it
* consumable for rendering.
*/
export interface DirectiveType<T> extends Type<T> { ngDirectiveDef: DirectiveDef<T>; } export interface DirectiveType<T> extends Type<T> { ngDirectiveDef: DirectiveDef<T>; }
export const enum DirectiveDefFlags {ContentQuery = 0b10} export const enum DirectiveDefFlags {ContentQuery = 0b10}
/**
* A subclass of `Type` which has a static `ngPipeDef`:`PipeDef` field making it
* consumable for rendering.
*/
export interface PipeType<T> extends Type<T> { ngPipeDef: PipeDef<T>; } export interface PipeType<T> extends Type<T> { ngPipeDef: PipeDef<T>; }
/** /**
* `DirectiveDef` is a compiled version of the Directive used by the renderer instructions. * Runtime link information for Directives.
*
* This is internal data structure used by the render to link
* directives into templates.
*
* NOTE: Always use `defineDirective` function to create this object,
* never create the object directly since the shape of this object
* can change between versions.
*
* See: {@link defineDirective}
*/ */
export interface DirectiveDef<T> { export interface DirectiveDef<T> {
/** Token representing the directive. Used by DI. */ /** Token representing the directive. Used by DI. */
@ -59,11 +83,6 @@ export interface DirectiveDef<T> {
*/ */
readonly outputs: {[P in keyof T]: P}; readonly outputs: {[P in keyof T]: P};
/**
* A dictionary mapping the methods' minified names to their original unminified ones.
*/
readonly methods: {[P in keyof T]: P};
/** /**
* Name under which the directive is exported (for use with local references in template) * Name under which the directive is exported (for use with local references in template)
*/ */
@ -104,6 +123,18 @@ export interface DirectiveDef<T> {
onDestroy: (() => void)|null; onDestroy: (() => void)|null;
} }
/**
* Runtime link information for Components.
*
* This is internal data structure used by the render to link
* components into templates.
*
* NOTE: Always use `defineComponent` function to create this object,
* never create the object directly since the shape of this object
* can change between versions.
*
* See: {@link defineComponent}
*/
export interface ComponentDef<T> extends DirectiveDef<T> { export interface ComponentDef<T> extends DirectiveDef<T> {
/** /**
* The tag name which should be used by the component. * The tag name which should be used by the component.
@ -128,10 +159,31 @@ export interface ComponentDef<T> extends DirectiveDef<T> {
/** Whether or not this component's ChangeDetectionStrategy is OnPush */ /** Whether or not this component's ChangeDetectionStrategy is OnPush */
readonly onPush: boolean; readonly onPush: boolean;
/**
* Defines the set of injectable providers that are visible to a Directive and its content DOM
* children.
*/
readonly providers?: Provider[];
/**
* Defines the set of injectable providers that are visible to a Directive and its view DOM
* children only.
*/
readonly viewProviders?: Provider[];
} }
/** /**
* Runtime link information for Pipes.
* *
* This is internal data structure used by the renderer to link
* pipes into templates.
*
* NOTE: Always use `definePipe` function to create this object,
* never create the object directly since the shape of this object
* can change between versions.
*
* See: {@link definePipe}
*/ */
export interface PipeDef<T> { export interface PipeDef<T> {
/** /**
@ -154,30 +206,142 @@ export interface PipeDef<T> {
onDestroy: (() => void)|null; onDestroy: (() => void)|null;
} }
/**
* Arguments for `defineDirective`
*/
export interface DirectiveDefArgs<T> { export interface DirectiveDefArgs<T> {
/**
* Directive type, needed to configure the injector.
*/
type: Type<T>; type: Type<T>;
/**
* Factory method used to create an instance of directive.
*/
factory: () => T | [T]; factory: () => T | [T];
/**
* Static attributes to set on host element.
*
* Even indices: attribute name
* Odd indices: attribute value
*/
attributes?: string[]; attributes?: string[];
/**
* A map of input names.
*
* The format is in: `{[actualPropertyName: string]:string}`.
*
* Which the minifier may translate to: `{[minifiedPropertyName: string]:string}`.
*
* This allows the render to re-construct the minified and non-minified names
* of properties.
*/
inputs?: {[P in keyof T]?: string}; inputs?: {[P in keyof T]?: string};
/**
* TODO: Remove per https://github.com/angular/angular/issues/22591
*/
inputsPropertyName?: {[P in keyof T]?: string}; inputsPropertyName?: {[P in keyof T]?: string};
/**
* A map of output names.
*
* The format is in: `{[actualPropertyName: string]:string}`.
*
* Which the minifier may translate to: `{[minifiedPropertyName: string]:string}`.
*
* This allows the render to re-construct the minified and non-minified names
* of properties.
*/
outputs?: {[P in keyof T]?: string}; outputs?: {[P in keyof T]?: string};
methods?: {[P in keyof T]?: string};
/**
* A list of optional features to apply.
*
* See: {@link NgOnChangesFeature}, {@link PublicFeature}
*/
features?: DirectiveDefFeature[]; features?: DirectiveDefFeature[];
/**
* Function executed by the parent template to allow child directive to apply host bindings.
*/
hostBindings?: (directiveIndex: number, elementIndex: number) => void; hostBindings?: (directiveIndex: number, elementIndex: number) => void;
/**
* Defines the name that can be used in the template to assign this directive to a variable.
*
* See: {@link Directive.exportAs}
*/
exportAs?: string; exportAs?: string;
} }
/**
* Arguments for `defineComponent`.
*/
export interface ComponentDefArgs<T> extends DirectiveDefArgs<T> { export interface ComponentDefArgs<T> extends DirectiveDefArgs<T> {
/**
* HTML tag name to use in place where this component should be instantiated.
*/
tag: string; tag: string;
/**
* Template function use for rendering DOM.
*
* This function has following structure.
*
* ```
* function Template<T>(ctx:T, creationMode: boolean) {
* if (creationMode) {
* // Contains creation mode instructions.
* }
* // Contains binding update instructions
* }
* ```
*
* Common instructions are:
* Creation mode instructions:
* - `elementStart`, `elementEnd`
* - `text`
* - `container`
* - `listener`
*
* Binding update instructions:
* - `bind`
* - `elementAttribute`
* - `elementProperty`
* - `elementClass`
* - `elementStyle`
*
*/
template: ComponentTemplate<T>; template: ComponentTemplate<T>;
/**
* A list of optional features to apply.
*
* See: {@link NgOnChancesFeature}, {@link PublicFeature}
*/
features?: ComponentDefFeature[]; features?: ComponentDefFeature[];
rendererType?: RendererType2; rendererType?: RendererType2;
changeDetection?: ChangeDetectionStrategy; changeDetection?: ChangeDetectionStrategy;
/**
* Defines the set of injectable objects that are visible to a Directive and its light DOM
* children.
*/
providers?: Provider[];
/**
* Defines the set of injectable objects that are visible to its view DOM children.
*/
viewProviders?: Provider[];
} }
export type DirectiveDefFeature = <T>(directiveDef: DirectiveDef<T>) => void; export type DirectiveDefFeature = <T>(directiveDef: DirectiveDef<T>) => void;
export type ComponentDefFeature = <T>(directiveDef: DirectiveDef<T>) => void; export type ComponentDefFeature = <T>(componentDef: ComponentDef<T>) => void;
// Note: This hack is necessary so we don't erroneously get a circular dependency // Note: This hack is necessary so we don't erroneously get a circular dependency
// failure based on types. // failure based on types.

View File

@ -10,7 +10,7 @@ import {NgForOf as NgForOfDef} from '@angular/common';
import {IterableDiffers} from '@angular/core'; import {IterableDiffers} from '@angular/core';
import {defaultIterableDiffers} from '../../src/change_detection/change_detection'; import {defaultIterableDiffers} from '../../src/change_detection/change_detection';
import {DirectiveType, InjectFlags, NgOnChangesFeature, defineDirective, inject, injectTemplateRef, injectViewContainerRef} from '../../src/render3/index'; import {DirectiveType, InjectFlags, NgOnChangesFeature, defineDirective, directiveInject, injectTemplateRef, injectViewContainerRef} from '../../src/render3/index';
export const NgForOf: DirectiveType<NgForOfDef<any>> = NgForOfDef as any; export const NgForOf: DirectiveType<NgForOfDef<any>> = NgForOfDef as any;
@ -18,7 +18,7 @@ NgForOf.ngDirectiveDef = defineDirective({
type: NgForOfDef, type: NgForOfDef,
factory: () => new NgForOfDef( factory: () => new NgForOfDef(
injectViewContainerRef(), injectTemplateRef(), injectViewContainerRef(), injectTemplateRef(),
inject(IterableDiffers, InjectFlags.Default, defaultIterableDiffers)), directiveInject(IterableDiffers, InjectFlags.Default, defaultIterableDiffers)),
features: [NgOnChangesFeature], features: [NgOnChangesFeature],
inputs: { inputs: {
ngForOf: 'ngForOf', ngForOf: 'ngForOf',

View File

@ -9,7 +9,7 @@
import {Component, ContentChild, Directive, Injectable, Injector, Input, NgModule, NgModuleFactory, NgModuleRef, OnDestroy, Optional, Pipe, PipeTransform, QueryList, SimpleChanges, TemplateRef, Type, ViewChild, ViewContainerRef} from '../../../src/core'; import {Component, ContentChild, Directive, Injectable, Injector, Input, NgModule, NgModuleFactory, NgModuleRef, OnDestroy, Optional, Pipe, PipeTransform, QueryList, SimpleChanges, TemplateRef, Type, ViewChild, ViewContainerRef} from '../../../src/core';
import * as r3 from '../../../src/render3/index'; import * as r3 from '../../../src/render3/index';
import {pending_pull_22005} from './small_app_spec'; import {$pending_pr_22458$} from './pending_api_spec';
const details_elided = { const details_elided = {
type: Object, type: Object,
@ -60,7 +60,7 @@ export class LibBComponent {
@NgModule({declarations: [LibAComponent], imports: []}) @NgModule({declarations: [LibAComponent], imports: []})
export class LibBModule { export class LibBModule {
// COMPILER GENERATED // COMPILER GENERATED
static ngInjectorDef = pending_pull_22005.defineInjector(details_elided); static ngInjectorDef = $pending_pr_22458$.defineInjector(details_elided);
} }
// END FILE: node_modules/libB/module.ts // END FILE: node_modules/libB/module.ts
// BEGIN FILE: node_modules/libB/module.metadata.json // BEGIN FILE: node_modules/libB/module.metadata.json
@ -92,7 +92,7 @@ export class AppComponent {
@NgModule({declarations: [LibAComponent], imports: []}) @NgModule({declarations: [LibAComponent], imports: []})
export class AppModule { export class AppModule {
// COMPILER GENERATED // COMPILER GENERATED
static ngInjectorDef = pending_pull_22005.defineInjector(details_elided); static ngInjectorDef = $pending_pr_22458$.defineInjector(details_elided);
} }
// END FILE: src/app.ts // END FILE: src/app.ts
@ -113,7 +113,7 @@ function ngBackPatch_node_modules_libB_module_LibAComponent() {
} }
function ngBackPatch_node_modules_libB_module_LibAModule() { function ngBackPatch_node_modules_libB_module_LibAModule() {
(LibAModule as any).ngInjectorDef = pending_pull_22005.defineInjector(details_elided); (LibAModule as any).ngInjectorDef = $pending_pr_22458$.defineInjector(details_elided);
} }
export const AppModuleFactory: NgModuleFactory<AppModule>&{patchedDeps: boolean} = { export const AppModuleFactory: NgModuleFactory<AppModule>&{patchedDeps: boolean} = {

View File

@ -6,103 +6,194 @@
* found in the LICENSE file at https://angular.io/license * found in the LICENSE file at https://angular.io/license
*/ */
import {Attribute, ChangeDetectionStrategy, ChangeDetectorRef, Component, ContentChild, ContentChildren, Directive, HostBinding, HostListener, Injectable, Input, NgModule, OnDestroy, Optional, Pipe, PipeTransform, QueryList, SimpleChanges, TemplateRef, ViewChild, ViewChildren, ViewContainerRef} from '../../../src/core'; import {Attribute, ChangeDetectionStrategy, ChangeDetectorRef, Component, ContentChild, ContentChildren, Directive, HostBinding, HostListener, Inject, InjectFlags, Injectable, Injector, Input, NgModule, OnDestroy, Optional, Pipe, PipeTransform, QueryList, SimpleChanges, SkipSelf, TemplateRef, ViewChild, ViewChildren, ViewContainerRef} from '../../../src/core';
import * as $r3$ from '../../../src/core_render3_private_export'; import * as $r3$ from '../../../src/core_render3_private_export';
import {renderComponent, toHtml} from '../render_util'; import {renderComponent, toHtml} from '../render_util';
import {$pending_pr_22458$} from './pending_api_spec';
/// See: `normative.md` /// See: `normative.md`
describe('injection', () => { describe('injection', () => {
type $boolean$ = boolean; type $boolean$ = boolean;
it('should inject ChangeDetectorRef', () => { describe('directives', () => {
type $MyComp$ = MyComp; // Directives (and Components) should use `directiveInject`
type $MyApp$ = MyApp; it('should inject ChangeDetectorRef', () => {
type $MyComp$ = MyComp;
type $MyApp$ = MyApp;
@Component({selector: 'my-comp', template: `{{ value }}`}) @Component({selector: 'my-comp', template: `{{ value }}`})
class MyComp { class MyComp {
value: string; value: string;
constructor(public cdr: ChangeDetectorRef) { this.value = (cdr.constructor as any).name; } constructor(public cdr: ChangeDetectorRef) { this.value = (cdr.constructor as any).name; }
// NORMATIVE // NORMATIVE
static ngComponentDef = $r3$.ɵdefineComponent({ static ngComponentDef = $r3$.ɵdefineComponent({
type: MyComp, type: MyComp,
tag: 'my-comp', tag: 'my-comp',
factory: function MyComp_Factory() { return new MyComp($r3$.ɵinjectChangeDetectorRef()); }, factory: function MyComp_Factory() {
template: function MyComp_Template(ctx: $MyComp$, cm: $boolean$) { return new MyComp($r3$.ɵinjectChangeDetectorRef());
if (cm) { },
$r3$.ɵT(0); template: function MyComp_Template(ctx: $MyComp$, cm: $boolean$) {
if (cm) {
$r3$.ɵT(0);
}
$r3$.ɵt(0, $r3$.ɵb(ctx.value));
} }
$r3$.ɵt(0, $r3$.ɵb(ctx.value)); });
} // /NORMATIVE
}); }
// /NORMATIVE
}
class MyApp { class MyApp {
static ngComponentDef = $r3$.ɵdefineComponent({ static ngComponentDef = $r3$.ɵdefineComponent({
type: MyApp, type: MyApp,
tag: 'my-app', tag: 'my-app',
factory: function MyApp_Factory() { return new MyApp(); }, factory: function MyApp_Factory() { return new MyApp(); },
/** <my-comp></my-comp> */ /** <my-comp></my-comp> */
template: function MyApp_Template(ctx: $MyApp$, cm: $boolean$) { template: function MyApp_Template(ctx: $MyApp$, cm: $boolean$) {
if (cm) { if (cm) {
$r3$.ɵE(0, MyComp); $r3$.ɵE(0, MyComp);
$r3$.ɵe(); $r3$.ɵe();
}
MyComp.ngComponentDef.h(1, 0);
$r3$.ɵr(1, 0);
} }
MyComp.ngComponentDef.h(1, 0); });
$r3$.ɵr(1, 0); }
}
});
}
const app = renderComponent(MyApp); const app = renderComponent(MyApp);
// ChangeDetectorRef is the token, ViewRef is historically the constructor // ChangeDetectorRef is the token, ViewRef is historically the constructor
expect(toHtml(app)).toEqual('<my-comp>ViewRef</my-comp>'); expect(toHtml(app)).toEqual('<my-comp>ViewRef</my-comp>');
});
it('should inject attributes', () => {
type $MyComp$ = MyComp;
type $MyApp$ = MyApp;
@Component({selector: 'my-comp', template: `{{ title }}`})
class MyComp {
constructor(@Attribute('title') public title: string|undefined) {}
// NORMATIVE
static ngComponentDef = $r3$.ɵdefineComponent({
type: MyComp,
tag: 'my-comp',
factory: function MyComp_Factory() { return new MyComp($r3$.ɵinjectAttribute('title')); },
template: function MyComp_Template(ctx: $MyComp$, cm: $boolean$) {
if (cm) {
$r3$.ɵT(0);
}
$r3$.ɵt(0, $r3$.ɵb(ctx.title));
}
});
// /NORMATIVE
}
class MyApp {
static ngComponentDef = $r3$.ɵdefineComponent({
type: MyApp,
tag: 'my-app',
factory: function MyApp_Factory() { return new MyApp(); },
/** <my-comp></my-comp> */
template: function MyApp_Template(ctx: $MyApp$, cm: $boolean$) {
if (cm) {
$r3$.ɵE(0, MyComp, e0_attrs);
$r3$.ɵe();
}
MyComp.ngComponentDef.h(1, 0);
$r3$.ɵr(1, 0);
}
});
}
const e0_attrs = ['title', 'WORKS'];
const app = renderComponent(MyApp);
// ChangeDetectorRef is the token, ViewRef is historically the constructor
expect(toHtml(app)).toEqual('<my-comp title="WORKS">WORKS</my-comp>');
});
// TODO(misko): enable once `providers` and `viewProvdires` are implemented.
xit('should inject into an injectable', () => {
type $MyApp$ = MyApp;
@Injectable()
class ServiceA {
// NORMATIVE
static ngInjectableDef = $pending_pr_22458$.defineInjectable({
factory: function ServiceA_Factory() { return new ServiceA(); },
});
// /NORMATIVE
}
@Injectable()
class ServiceB {
// NORMATIVE
static ngInjectableDef = $pending_pr_22458$.defineInjectable({
factory: function ServiceA_Factory() { return new ServiceB(); },
});
// /NORMATIVE
}
@Component({
template: '',
providers: [ServiceA],
viewProviders: [ServiceB],
})
class MyApp {
constructor(serviceA: ServiceA, serviceB: ServiceB, injector: Injector) {}
static ngComponentDef = $r3$.ɵdefineComponent({
type: MyApp,
tag: 'my-app',
factory: function MyApp_Factory() {
return new MyApp(
$r3$.ɵdirectiveInject(ServiceA), $r3$.ɵdirectiveInject(ServiceB),
$pending_pr_22458$.injectInjector());
},
/** */
template: function MyApp_Template(ctx: $MyApp$, cm: $boolean$) {},
providers: [ServiceA],
viewProviders: [ServiceB],
});
}
const e0_attrs = ['title', 'WORKS'];
const app = renderComponent(MyApp);
// ChangeDetectorRef is the token, ViewRef is historically the constructor
expect(toHtml(app)).toEqual('<my-comp title="WORKS">WORKS</my-comp>');
});
}); });
it('should inject attributes', () => { describe('services', () => {
type $MyComp$ = MyComp; // Services should use `inject`
type $MyApp$ = MyApp; @Injectable()
class ServiceA {
@Component({selector: 'my-comp', template: `{{ title }}`}) constructor(@Inject(String) name: String, injector: Injector) {}
class MyComp {
constructor(@Attribute('title') public title: string|undefined) {}
// NORMATIVE // NORMATIVE
static ngComponentDef = $r3$.ɵdefineComponent({ static ngInjectableDef = $pending_pr_22458$.defineInjectable({
type: MyComp, factory: function ServiceA_Factory() {
tag: 'my-comp', return new ServiceA(
factory: function MyComp_Factory() { return new MyComp($r3$.ɵinjectAttribute('title')); }, $pending_pr_22458$.inject(String), $pending_pr_22458$.injectInjector());
template: function MyComp_Template(ctx: $MyComp$, cm: $boolean$) { },
if (cm) {
$r3$.ɵT(0);
}
$r3$.ɵt(0, $r3$.ɵb(ctx.title));
}
}); });
// /NORMATIVE // /NORMATIVE
} }
class MyApp { @Injectable()
static ngComponentDef = $r3$.ɵdefineComponent({ class ServiceB {
type: MyApp, constructor(serviceA: ServiceA, @SkipSelf() injector: Injector) {}
tag: 'my-app', // NORMATIVE
factory: function MyApp_Factory() { return new MyApp(); }, static ngInjectableDef = $pending_pr_22458$.defineInjectable({
/** <my-comp></my-comp> */ factory: function ServiceA_Factory() {
template: function MyApp_Template(ctx: $MyApp$, cm: $boolean$) { return new ServiceB(
if (cm) { $pending_pr_22458$.inject(ServiceA),
$r3$.ɵE(0, MyComp, e0_attrs); $pending_pr_22458$.injectInjector(InjectFlags.SkipSelf));
$r3$.ɵe(); },
}
MyComp.ngComponentDef.h(1, 0);
$r3$.ɵr(1, 0);
}
}); });
// /NORMATIVE
} }
const e0_attrs = ['title', 'WORKS'];
const app = renderComponent(MyApp);
// ChangeDetectorRef is the token, ViewRef is historically the constructor
expect(toHtml(app)).toEqual('<my-comp title="WORKS">WORKS</my-comp>');
}); });
}); });

View File

@ -32,7 +32,7 @@ xdescribe('NgModule', () => {
constructor(name: String) {} constructor(name: String) {}
// NORMATIVE // NORMATIVE
static ngInjectableDef = defineInjectable({ static ngInjectableDef = defineInjectable({
factory: () => new Toast($r3$.ɵinject(String)), factory: () => new Toast($r3$.ɵdirectiveInject(String)),
}); });
// /NORMATIVE // /NORMATIVE
} }
@ -51,7 +51,7 @@ xdescribe('NgModule', () => {
constructor(toast: Toast) {} constructor(toast: Toast) {}
// NORMATIVE // NORMATIVE
static ngInjectorDef = defineInjector({ static ngInjectorDef = defineInjector({
factory: () => new MyModule($r3$.ɵinject(Toast)), factory: () => new MyModule($r3$.ɵdirectiveInject(Toast)),
provider: [ provider: [
{provide: Toast, deps: [String]}, // If Toast has metadata generate this line {provide: Toast, deps: [String]}, // If Toast has metadata generate this line
Toast, // If Toast has no metadata generate this line. Toast, // If Toast has no metadata generate this line.
@ -69,7 +69,8 @@ xdescribe('NgModule', () => {
static ngInjectableDef = defineInjectable({ static ngInjectableDef = defineInjectable({
scope: MyModule, scope: MyModule,
factory: () => new BurntToast( factory: () => new BurntToast(
$r3$.ɵinject(Toast, $r3$.ɵInjectFlags.Optional), $r3$.ɵinject(String)), $r3$.ɵdirectiveInject(Toast, $r3$.ɵInjectFlags.Optional),
$r3$.ɵdirectiveInject(String)),
}); });
// /NORMATIVE // /NORMATIVE
} }

View File

@ -8,7 +8,7 @@
import {Component, ContentChild, Directive, Injectable, Input, NgModule, OnDestroy, Optional, Pipe, PipeTransform, QueryList, SimpleChanges, TemplateRef, Type, ViewChild, ViewContainerRef} from '../../../src/core'; import {Component, ContentChild, Directive, Injectable, Input, NgModule, OnDestroy, Optional, Pipe, PipeTransform, QueryList, SimpleChanges, TemplateRef, Type, ViewChild, ViewContainerRef} from '../../../src/core';
import * as r3 from '../../../src/render3/index'; import * as r3 from '../../../src/render3/index';
import {pending_pull_22005} from './small_app_spec'; import {$pending_pr_22458$} from './pending_api_spec';
/** /**
* GOALS: * GOALS:
@ -29,7 +29,7 @@ class ThirdPartyClass {
@Injectable() @Injectable()
class CompiledWithIvy { class CompiledWithIvy {
// NORMATIVE // NORMATIVE
static ngInjectableDef = pending_pull_22005.defineInjectable( static ngInjectableDef = $pending_pr_22458$.defineInjectable(
{factory: function CompileWithIvy_Factory() { return new CompiledWithIvy(); }}); {factory: function CompileWithIvy_Factory() { return new CompiledWithIvy(); }});
// /NORMATIVE // /NORMATIVE
} }
@ -38,7 +38,7 @@ class CompiledWithIvy {
@NgModule({providers: [ThirdPartyClass, CompiledWithIvy]}) @NgModule({providers: [ThirdPartyClass, CompiledWithIvy]})
class CompiledWithIvyModule { class CompiledWithIvyModule {
// NORMATIVE // NORMATIVE
static ngInjectorDef = pending_pull_22005.defineInjector({ static ngInjectorDef = $pending_pr_22458$.defineInjector({
providers: [ThirdPartyClass, CompiledWithIvy], providers: [ThirdPartyClass, CompiledWithIvy],
factory: function CompiledWithIvyModule_Factory() { return new CompiledWithIvyModule(); } factory: function CompiledWithIvyModule_Factory() { return new CompiledWithIvyModule(); }
}); });
@ -72,7 +72,7 @@ function ngPatch_depsOf_CompiledWithIvyModule() {
} }
function ngPatch_node_modules_some_library_path_public_CompileWithIvy() { function ngPatch_node_modules_some_library_path_public_CompileWithIvy() {
/** @__BUILD_OPTIMIZER_COLOCATE__ */ /** @__BUILD_OPTIMIZER_COLOCATE__ */
(ThirdPartyClass as any).ngInjectableDef = pending_pull_22005.defineInjectable( (ThirdPartyClass as any).ngInjectableDef = $pending_pr_22458$.defineInjectable(
{factory: function CompileWithIvy_Factory() { return new ThirdPartyClass(); }}); {factory: function CompileWithIvy_Factory() { return new ThirdPartyClass(); }});
} }
// /NORMATIVE // /NORMATIVE

View File

@ -0,0 +1,28 @@
/**
* @license
* Copyright Google Inc. All Rights Reserved.
*
* Use of this source code is governed by an MIT-style license that can be
* found in the LICENSE file at https://angular.io/license
*/
import {InjectFlags, InjectionToken, Injector, Type} from '@angular/core';
// TODO: remove once https://github.com/angular/angular/pull/22458 lands
export class $pending_pr_22458$ {
static defineInjectable<T>({providerFor, factory}: {providerFor?: Type<any>, factory: () => T}):
{providerFor: Type<any>| null, factory: () => T} {
return {providerFor: providerFor || null, factory: factory};
}
static defineInjector<T>({factory, providers}: {factory: () => T, providers: any[]}):
{factory: () => T, providers: any[]} {
return {factory: factory, providers: providers};
}
static injectInjector(flags?: InjectFlags): Injector { return null !; }
static inject<T>(token: Type<T>|InjectionToken<T>, flags?: InjectFlags): T { return null as any; }
}

View File

@ -11,24 +11,11 @@ import {Component, ContentChild, Directive, EventEmitter, Injectable, Input, NgM
import {withBody} from '@angular/core/testing'; import {withBody} from '@angular/core/testing';
import * as r3 from '../../../src/render3/index'; import * as r3 from '../../../src/render3/index';
import {$pending_pr_22458$} from './pending_api_spec';
/// See: `normative.md` /// See: `normative.md`
// TODO: remove once https://github.com/angular/angular/pull/22005 lands
export class pending_pull_22005 {
static defineInjectable<T>({scope, factory}: {scope?: Type<any>, factory: () => T}):
{scope: Type<any>| null, factory: () => T} {
return {scope: scope || null, factory: factory};
}
static defineInjector<T>({factory, providers}: {factory: () => T, providers: any[]}):
{factory: () => T, providers: any[]} {
return {factory: factory, providers: providers};
}
}
interface ToDo { interface ToDo {
text: string; text: string;
@ -46,7 +33,7 @@ class AppState {
]; ];
// NORMATIVE // NORMATIVE
static ngInjectableDef = pending_pull_22005.defineInjectable({factory: () => new AppState()}); static ngInjectableDef = $pending_pr_22458$.defineInjectable({factory: () => new AppState()});
// /NORMATIVE // /NORMATIVE
} }
@ -74,7 +61,7 @@ class ToDoAppComponent {
type: ToDoAppComponent, type: ToDoAppComponent,
tag: 'todo-app', tag: 'todo-app',
factory: function ToDoAppComponent_Factory() { factory: function ToDoAppComponent_Factory() {
return new ToDoAppComponent(r3.inject(AppState)); return new ToDoAppComponent(r3.directiveInject(AppState));
}, },
template: function ToDoAppComponent_Template(ctx: ToDoAppComponent, cm: boolean) { template: function ToDoAppComponent_Template(ctx: ToDoAppComponent, cm: boolean) {
if (cm) { if (cm) {
@ -171,7 +158,7 @@ const e1_attrs = ['type', 'checkbox'];
}) })
class ToDoAppModule { class ToDoAppModule {
// NORMATIVE // NORMATIVE
static ngInjectorDef = pending_pull_22005.defineInjector({ static ngInjectorDef = $pending_pr_22458$.defineInjector({
factory: () => new ToDoAppModule(), factory: () => new ToDoAppModule(),
providers: [AppState], providers: [AppState],
}); });

View File

@ -32,7 +32,6 @@ describe('component', () => {
}, },
factory: () => new CounterComponent, factory: () => new CounterComponent,
inputs: {count: 'count'}, inputs: {count: 'count'},
methods: {increment: 'increment'}
}); });
} }

View File

@ -10,7 +10,7 @@ import {ChangeDetectorRef, ElementRef, TemplateRef, ViewContainerRef} from '@ang
import {defineComponent} from '../../src/render3/definition'; import {defineComponent} from '../../src/render3/definition';
import {InjectFlags, bloomAdd, bloomFindPossibleInjector, getOrCreateNodeInjector, injectAttribute} from '../../src/render3/di'; import {InjectFlags, bloomAdd, bloomFindPossibleInjector, getOrCreateNodeInjector, injectAttribute} from '../../src/render3/di';
import {NgOnChangesFeature, PublicFeature, defineDirective, inject, injectChangeDetectorRef, injectElementRef, injectTemplateRef, injectViewContainerRef} from '../../src/render3/index'; import {NgOnChangesFeature, PublicFeature, defineDirective, directiveInject, injectChangeDetectorRef, injectElementRef, injectTemplateRef, injectViewContainerRef} from '../../src/render3/index';
import {bind, container, containerRefreshEnd, containerRefreshStart, createLNode, createLView, createTView, directiveRefresh, elementEnd, elementStart, embeddedViewEnd, embeddedViewStart, enterView, interpolation2, leaveView, load, projection, projectionDef, text, textBinding} from '../../src/render3/instructions'; import {bind, container, containerRefreshEnd, containerRefreshStart, createLNode, createLView, createTView, directiveRefresh, elementEnd, elementStart, embeddedViewEnd, embeddedViewStart, enterView, interpolation2, leaveView, load, projection, projectionDef, text, textBinding} from '../../src/render3/instructions';
import {LInjector} from '../../src/render3/interfaces/injector'; import {LInjector} from '../../src/render3/interfaces/injector';
import {LNodeFlags} from '../../src/render3/interfaces/node'; import {LNodeFlags} from '../../src/render3/interfaces/node';
@ -59,7 +59,7 @@ describe('di', () => {
constructor(a: DirectiveA, b: DirectiveB) { this.value = a.value + b.value; } constructor(a: DirectiveA, b: DirectiveB) { this.value = a.value + b.value; }
static ngDirectiveDef = defineDirective({ static ngDirectiveDef = defineDirective({
type: DirectiveC, type: DirectiveC,
factory: () => new DirectiveC(inject(DirectiveA), inject(DirectiveB)) factory: () => new DirectiveC(directiveInject(DirectiveA), directiveInject(DirectiveB))
}); });
} }
@ -101,7 +101,7 @@ describe('di', () => {
} }
static ngDirectiveDef = defineDirective({ static ngDirectiveDef = defineDirective({
type: DirectiveSameInstance, type: DirectiveSameInstance,
factory: () => new DirectiveSameInstance(injectElementRef(), inject(Directive)) factory: () => new DirectiveSameInstance(injectElementRef(), directiveInject(Directive))
}); });
} }
@ -141,7 +141,7 @@ describe('di', () => {
} }
static ngDirectiveDef = defineDirective({ static ngDirectiveDef = defineDirective({
type: DirectiveSameInstance, type: DirectiveSameInstance,
factory: () => new DirectiveSameInstance(injectTemplateRef(), inject(Directive)) factory: () => new DirectiveSameInstance(injectTemplateRef(), directiveInject(Directive))
}); });
} }
@ -181,7 +181,8 @@ describe('di', () => {
} }
static ngDirectiveDef = defineDirective({ static ngDirectiveDef = defineDirective({
type: DirectiveSameInstance, type: DirectiveSameInstance,
factory: () => new DirectiveSameInstance(injectViewContainerRef(), inject(Directive)) factory:
() => new DirectiveSameInstance(injectViewContainerRef(), directiveInject(Directive))
}); });
} }
@ -540,7 +541,8 @@ describe('di', () => {
static ngComponentDef = defineComponent({ static ngComponentDef = defineComponent({
type: MyApp, type: MyApp,
tag: 'my-app', tag: 'my-app',
factory: () => new MyApp(inject(String as any, InjectFlags.Default, 'DefaultValue')), factory: () => new MyApp(
directiveInject(String as any, InjectFlags.Default, 'DefaultValue')),
template: () => null template: () => null
}); });
} }
@ -565,7 +567,7 @@ describe('di', () => {
} }
static ngDirectiveDef = defineDirective({ static ngDirectiveDef = defineDirective({
type: ChildDirective, type: ChildDirective,
factory: () => new ChildDirective(inject(ParentDirective)), factory: () => new ChildDirective(directiveInject(ParentDirective)),
features: [PublicFeature] features: [PublicFeature]
}); });
} }
@ -577,7 +579,8 @@ describe('di', () => {
} }
static ngDirectiveDef = defineDirective({ static ngDirectiveDef = defineDirective({
type: Child2Directive, type: Child2Directive,
factory: () => new Child2Directive(inject(ParentDirective), inject(ChildDirective)) factory: () => new Child2Directive(
directiveInject(ParentDirective), directiveInject(ChildDirective))
}); });
} }