From 6a62ed224580499988aa46106f7888e092ea9859 Mon Sep 17 00:00:00 2001 From: Kara Erickson Date: Fri, 21 Sep 2018 18:38:13 -0700 Subject: [PATCH] fix(ivy): objects like ElementRef should not use a special injection fn (#26064) PR Close #26064 --- .../src/ngtsc/annotations/src/util.ts | 12 - .../compliance/r3_compiler_compliance_spec.ts | 49 +- .../compiler-cli/test/ngtsc/ngtsc_spec.ts | 2 +- packages/compiler/src/render3/r3_factory.ts | 40 +- .../compiler/src/render3/r3_identifiers.ts | 10 - .../compiler/src/render3/view/compiler.ts | 2 +- packages/core/BUILD.bazel | 34 +- .../change_detection/change_detector_ref.ts | 5 + packages/core/src/core_private_export.ts | 2 +- .../core/src/core_render3_private_export.ts | 17 +- packages/core/src/di/injectable.ts | 2 +- .../compiler/index.ts} | 2 +- .../compiler}/ivy_switch_on.ts | 8 +- .../compiler/jit.ts} | 0 .../compiler/legacy.ts} | 21 +- .../compiler/local.ts} | 0 packages/core/src/ivy_switch/runtime/index.ts | 15 + .../src/ivy_switch/runtime/ivy_switch_on.ts | 14 + packages/core/src/ivy_switch/runtime/jit.ts | 10 + .../core/src/ivy_switch/runtime/legacy.ts | 31 + packages/core/src/ivy_switch/runtime/local.ts | 10 + packages/core/src/linker/element_ref.ts | 5 + packages/core/src/linker/template_ref.ts | 10 +- .../core/src/linker/view_container_ref.ts | 6 + packages/core/src/metadata/directives.ts | 2 +- packages/core/src/metadata/ng_module.ts | 2 +- packages/core/src/render3/VIEW_DATA.md | 17 +- packages/core/src/render3/component_ref.ts | 24 +- packages/core/src/render3/di.ts | 381 +-------- packages/core/src/render3/fields.ts | 7 + packages/core/src/render3/index.ts | 7 +- packages/core/src/render3/instructions.ts | 5 +- packages/core/src/render3/jit/environment.ts | 4 - packages/core/src/render3/jit/util.ts | 8 +- packages/core/src/render3/query.ts | 68 +- .../src/render3/view_engine_compatibility.ts | 351 ++++++++ .../bundle.golden_symbols.json | 32 +- .../hello_world_r2/bundle.golden_symbols.json | 15 + .../bundling/todo/bundle.golden_symbols.json | 35 +- .../todo_r2/bundle.golden_symbols.json | 26 +- .../test/render3/animations/players_spec.ts | 5 +- .../test/render3/change_detection_spec.ts | 30 +- .../test/render3/common_integration_spec.ts | 29 +- packages/core/test/render3/common_with_def.ts | 12 +- .../component_directives_spec.ts | 2 +- .../compiler_canonical/injection_spec.ts | 4 +- .../local_reference_spec.ts | 5 +- .../render3/compiler_canonical/pipes_spec.ts | 7 +- .../template_variables_spec.ts | 7 +- packages/core/test/render3/content_spec.ts | 3 - packages/core/test/render3/di_spec.ts | 808 +++++++++--------- .../core/test/render3/integration_spec.ts | 16 +- packages/core/test/render3/ivy/jit_spec.ts | 2 +- packages/core/test/render3/query_spec.ts | 126 +-- packages/core/test/render3/render_util.ts | 24 + .../test/render3/view_container_ref_spec.ts | 69 +- 56 files changed, 1367 insertions(+), 1073 deletions(-) rename packages/core/src/{ivy_switch.ts => ivy_switch/compiler/index.ts} (93%) rename packages/core/src/{ => ivy_switch/compiler}/ivy_switch_on.ts (66%) rename packages/core/src/{ivy_switch_jit.ts => ivy_switch/compiler/jit.ts} (100%) rename packages/core/src/{ivy_switch_legacy.ts => ivy_switch/compiler/legacy.ts} (91%) rename packages/core/src/{ivy_switch_local.ts => ivy_switch/compiler/local.ts} (100%) create mode 100644 packages/core/src/ivy_switch/runtime/index.ts create mode 100644 packages/core/src/ivy_switch/runtime/ivy_switch_on.ts create mode 100644 packages/core/src/ivy_switch/runtime/jit.ts create mode 100644 packages/core/src/ivy_switch/runtime/legacy.ts create mode 100644 packages/core/src/ivy_switch/runtime/local.ts create mode 100644 packages/core/src/render3/view_engine_compatibility.ts diff --git a/packages/compiler-cli/src/ngtsc/annotations/src/util.ts b/packages/compiler-cli/src/ngtsc/annotations/src/util.ts index fe11c90ea3..68cde9b25f 100644 --- a/packages/compiler-cli/src/ngtsc/annotations/src/util.ts +++ b/packages/compiler-cli/src/ngtsc/annotations/src/util.ts @@ -68,21 +68,9 @@ export function getConstructorDependencies( const importedSymbol = reflector.getImportOfIdentifier(tokenExpr); if (importedSymbol !== null && importedSymbol.from === '@angular/core') { switch (importedSymbol.name) { - case 'ChangeDetectorRef': - resolved = R3ResolvedDependencyType.ChangeDetectorRef; - break; - case 'ElementRef': - resolved = R3ResolvedDependencyType.ElementRef; - break; case 'Injector': resolved = R3ResolvedDependencyType.Injector; break; - case 'TemplateRef': - resolved = R3ResolvedDependencyType.TemplateRef; - break; - case 'ViewContainerRef': - resolved = R3ResolvedDependencyType.ViewContainerRef; - break; case 'Renderer2': resolved = R3ResolvedDependencyType.Renderer2; break; diff --git a/packages/compiler-cli/test/compliance/r3_compiler_compliance_spec.ts b/packages/compiler-cli/test/compliance/r3_compiler_compliance_spec.ts index fdb81fc978..3807942c83 100644 --- a/packages/compiler-cli/test/compliance/r3_compiler_compliance_spec.ts +++ b/packages/compiler-cli/test/compliance/r3_compiler_compliance_spec.ts @@ -648,6 +648,49 @@ describe('compiler compliance', () => { expectEmit(source, HostBindingCompDeclaration, 'Invalid host binding code'); }); + it('should not treat ElementRef, ViewContainerRef, or ChangeDetectorRef specially when injecting', + () => { + const files = { + app: { + 'spec.ts': ` + import {Component, NgModule, ElementRef, ChangeDetectorRef, ViewContainerRef} from '@angular/core'; + + @Component({ + selector: 'my-component', + template: '' + }) + export class MyComponent { + constructor(public el: ElementRef, public vcr: ViewContainerRef, public cdr: ChangeDetectorRef) {} + } + + @NgModule({declarations: [MyComponent]}) + export class MyModule {} + ` + } + }; + + const MyComponentDefinition = ` + … + MyComponent.ngComponentDef = $r3$.ɵdefineComponent({ + type: MyComponent, + selectors: [["my-component"]], + factory: function MyComponent_Factory(t) { + return new (t || MyComponent)( + $r3$.ɵdirectiveInject(ElementRef), $r3$.ɵdirectiveInject(ViewContainerRef), + $r3$.ɵdirectiveInject(ChangeDetectorRef)); + }, + features: [$r3$.ɵPublicFeature], + consts: 0, + vars: 0, + template: function MyComponent_Template(rf, ctx) {} + });`; + + const result = compile(files, angularFiles); + const source = result.source; + + expectEmit(source, MyComponentDefinition, 'Incorrect MyComponent.ngComponentDef'); + }); + it('should support structural directives', () => { const files = { app: { @@ -677,7 +720,7 @@ describe('compiler compliance', () => { IfDirective.ngDirectiveDef = $r3$.ɵdefineDirective({ type: IfDirective, selectors: [["", "if", ""]], - factory: function IfDirective_Factory(t) { return new (t || IfDirective)($r3$.ɵinjectTemplateRef()); }, + factory: function IfDirective_Factory(t) { return new (t || IfDirective)($r3$.ɵdirectiveInject(TemplateRef)); }, features: [$r3$.ɵPublicFeature] });`; const MyComponentDefinition = ` @@ -1703,7 +1746,7 @@ describe('compiler compliance', () => { type: ForOfDirective, selectors: [["", "forOf", ""]], factory: function ForOfDirective_Factory(t) { - return new (t || ForOfDirective)($r3$.ɵinjectViewContainerRef(), $r3$.ɵinjectTemplateRef()); + return new (t || ForOfDirective)($r3$.ɵdirectiveInject(ViewContainerRef), $r3$.ɵdirectiveInject(TemplateRef)); }, features: [$r3$.ɵPublicFeature, $r3$.ɵNgOnChangesFeature], inputs: {forOf: "forOf"} @@ -1779,7 +1822,7 @@ describe('compiler compliance', () => { type: ForOfDirective, selectors: [["", "forOf", ""]], factory: function ForOfDirective_Factory(t) { - return new (t || ForOfDirective)($r3$.ɵinjectViewContainerRef(), $r3$.ɵinjectTemplateRef()); + return new (t || ForOfDirective)($r3$.ɵdirectiveInject(ViewContainerRef), $r3$.ɵdirectiveInject(TemplateRef)); }, features: [$r3$.ɵPublicFeature, $r3$.ɵNgOnChangesFeature], inputs: {forOf: "forOf"} diff --git a/packages/compiler-cli/test/ngtsc/ngtsc_spec.ts b/packages/compiler-cli/test/ngtsc/ngtsc_spec.ts index 05999b40d9..130e1dd8ba 100644 --- a/packages/compiler-cli/test/ngtsc/ngtsc_spec.ts +++ b/packages/compiler-cli/test/ngtsc/ngtsc_spec.ts @@ -480,7 +480,7 @@ describe('ngtsc behavioral tests', () => { const jsContents = getContents('test.js'); expect(jsContents) .toContain( - `factory: function FooCmp_Factory(t) { return new (t || FooCmp)(i0.ɵinjectAttribute("test"), i0.ɵinjectChangeDetectorRef(), i0.ɵinjectElementRef(), i0.ɵdirectiveInject(i0.INJECTOR), i0.ɵinjectRenderer2(), i0.ɵinjectTemplateRef(), i0.ɵinjectViewContainerRef()); }`); + `factory: function FooCmp_Factory(t) { return new (t || FooCmp)(i0.ɵinjectAttribute("test"), i0.ɵdirectiveInject(ChangeDetectorRef), i0.ɵdirectiveInject(ElementRef), i0.ɵdirectiveInject(i0.INJECTOR), i0.ɵinjectRenderer2(), i0.ɵdirectiveInject(TemplateRef), i0.ɵdirectiveInject(ViewContainerRef)); }`); }); it('should generate queries for components', () => { diff --git a/packages/compiler/src/render3/r3_factory.ts b/packages/compiler/src/render3/r3_factory.ts index 2f993b9fe0..a81a42d1ff 100644 --- a/packages/compiler/src/render3/r3_factory.ts +++ b/packages/compiler/src/render3/r3_factory.ts @@ -101,30 +101,10 @@ export enum R3ResolvedDependencyType { */ Injector = 2, - /** - * The dependency is for `ElementRef`. - */ - ElementRef = 3, - - /** - * The dependency is for `TemplateRef`. - */ - TemplateRef = 4, - - /** - * The dependency is for `ViewContainerRef`. - */ - ViewContainerRef = 5, - - /** - * The dependency is for `ChangeDetectorRef`. - */ - ChangeDetectorRef = 6, - /** * The dependency is for `Renderer2`. */ - Renderer2 = 7, + Renderer2 = 3, } /** @@ -282,14 +262,6 @@ function compileInjectDependency( case R3ResolvedDependencyType.Attribute: // In the case of attributes, the attribute name in question is given as the token. return o.importExpr(R3.injectAttribute).callFn([dep.token]); - case R3ResolvedDependencyType.ElementRef: - return o.importExpr(R3.injectElementRef).callFn([]); - case R3ResolvedDependencyType.TemplateRef: - return o.importExpr(R3.injectTemplateRef).callFn([]); - case R3ResolvedDependencyType.ViewContainerRef: - return o.importExpr(R3.injectViewContainerRef).callFn([]); - case R3ResolvedDependencyType.ChangeDetectorRef: - return o.importExpr(R3.injectChangeDetectorRef).callFn([]); case R3ResolvedDependencyType.Renderer2: return o.importExpr(R3.injectRenderer2).callFn([]); default: @@ -320,13 +292,7 @@ export function dependenciesFromGlobalMetadata( if (dependency.token) { const tokenRef = tokenReference(dependency.token); let resolved: R3ResolvedDependencyType = R3ResolvedDependencyType.Token; - if (tokenRef === elementRef) { - resolved = R3ResolvedDependencyType.ElementRef; - } else if (tokenRef === templateRef) { - resolved = R3ResolvedDependencyType.TemplateRef; - } else if (tokenRef === viewContainerRef) { - resolved = R3ResolvedDependencyType.ViewContainerRef; - } else if (tokenRef === injectorRef) { + if (tokenRef === injectorRef) { resolved = R3ResolvedDependencyType.Injector; } else if (tokenRef === renderer2) { resolved = R3ResolvedDependencyType.Renderer2; @@ -363,4 +329,4 @@ function isDelegatedMetadata(meta: R3FactoryMetadata): meta is R3DelegatedFactor function isExpressionFactoryMetadata(meta: R3FactoryMetadata): meta is R3ExpressionFactoryMetadata { return (meta as any).expression !== undefined; -} \ No newline at end of file +} diff --git a/packages/compiler/src/render3/r3_identifiers.ts b/packages/compiler/src/render3/r3_identifiers.ts index 63f18d7707..2c96728765 100644 --- a/packages/compiler/src/render3/r3_identifiers.ts +++ b/packages/compiler/src/render3/r3_identifiers.ts @@ -106,16 +106,6 @@ export class Identifiers { static injectAttribute: o.ExternalReference = {name: 'ɵinjectAttribute', moduleName: CORE}; - static injectElementRef: o.ExternalReference = {name: 'ɵinjectElementRef', moduleName: CORE}; - - static injectTemplateRef: o.ExternalReference = {name: 'ɵinjectTemplateRef', moduleName: CORE}; - - static injectViewContainerRef: - o.ExternalReference = {name: 'ɵinjectViewContainerRef', moduleName: CORE}; - - static injectChangeDetectorRef: - o.ExternalReference = {name: 'ɵinjectChangeDetectorRef', moduleName: CORE}; - static injectRenderer2: o.ExternalReference = {name: 'ɵinjectRenderer2', moduleName: CORE}; static directiveInject: o.ExternalReference = {name: 'ɵdirectiveInject', moduleName: CORE}; diff --git a/packages/compiler/src/render3/view/compiler.ts b/packages/compiler/src/render3/view/compiler.ts index 7da4c7c278..fccdb6b3f3 100644 --- a/packages/compiler/src/render3/view/compiler.ts +++ b/packages/compiler/src/render3/view/compiler.ts @@ -43,7 +43,7 @@ function baseDirectiveFields( definitionMap.set('selectors', createDirectiveSelector(meta.selector !)); - // e.g. `factory: () => new MyApp(injectElementRef())` + // e.g. `factory: () => new MyApp(directiveInject(ElementRef))` const result = compileFactoryFunction({ name: meta.name, type: meta.type, diff --git a/packages/core/BUILD.bazel b/packages/core/BUILD.bazel index 5dbdbfbe4b..ae982c7288 100644 --- a/packages/core/BUILD.bazel +++ b/packages/core/BUILD.bazel @@ -9,9 +9,13 @@ ng_module( "*.ts", "src/**/*.ts", ], - exclude = ["src/ivy_switch.ts"], + exclude = [ + "src/ivy_switch/compiler/index.ts", + "src/ivy_switch/runtime/index.ts", + ], ) + [ - ":ivy_switch", + ":ivy_switch_compiler", + ":ivy_switch_runtime", ], module_name = "@angular/core", deps = [ @@ -42,19 +46,27 @@ ng_package( ## Controls if Ivy is enabled. (Temporary target until we permanently switch over to Ivy) ## -## This file generates `src/ivy_switch.ts` file which reexports symbols for `ViewEngine` or `Ivy.` -## - append `--define=compile=legacy` (default) to `bazel` command to reexport `./ivy_switch_legacy` -## and use `ViewEngine` -## - append `--define=compile=jit` to `bazel` command to rexport `./ivy_switch_jit` and use `Ivy` -## - append `--define=compile=local` to `bazel` command to rexport `./ivy_switch_jit` and use `Ivy` +## This file generates the `src/ivy_switch/compiler/index.ts` and `src/ivy_switch/runtime/index.ts` files which +## reexport symbols for `ViewEngine` or `Ivy.` +## - append `--define=compile=legacy` (default) to `bazel` command to reexport `./legacy` from each folder +## in the 'ivy_switch' directory and use `ViewEngine` +## - append `--define=compile=jit` to `bazel` command to rexport `./jit` from each folder in the `ivy_switch` +## directory and use `Ivy` +## - append `--define=compile=local` to `bazel` command to rexport `./ivy_switch/compiler/jit` and use `Ivy` ## in the local analysis mode. (run as part of `ngtsc`) ## ## NOTE: `--define=compile=jit` works with any `bazel` command or target across the repo. ## ## See: `//tools/bazel.rc` where `--define=ivy=false` is defined as default. -## See: `./src/ivy_switch.ts` for more details. +## See: `./src/ivy_switch/compiler/index.ts` for more details. genrule( - name = "ivy_switch", - outs = ["src/ivy_switch.ts"], - cmd = "echo export '*' from \"'./ivy_switch_$(compile)';\" > $@", + name = "ivy_switch_compiler", + outs = ["src/ivy_switch/compiler/index.ts"], + cmd = "echo export '*' from \"'./$(compile)';\" > $@", +) + +genrule( + name = "ivy_switch_runtime", + outs = ["src/ivy_switch/runtime/index.ts"], + cmd = "echo export '*' from \"'./$(compile)';\" > $@", ) diff --git a/packages/core/src/change_detection/change_detector_ref.ts b/packages/core/src/change_detection/change_detector_ref.ts index 4090d09ef6..235d3a5648 100644 --- a/packages/core/src/change_detection/change_detector_ref.ts +++ b/packages/core/src/change_detection/change_detector_ref.ts @@ -6,6 +6,8 @@ * found in the LICENSE file at https://angular.io/license */ +import {R3_CHANGE_DETECTOR_REF_FACTORY} from '../ivy_switch/runtime/index'; + /** * Base class for Angular Views, provides change detection functionality. * A change-detection tree collects all views that are to be checked for changes. @@ -103,4 +105,7 @@ export abstract class ChangeDetectorRef { * */ abstract reattach(): void; + + /** @internal */ + static __NG_ELEMENT_ID__: () => ChangeDetectorRef = () => R3_CHANGE_DETECTOR_REF_FACTORY(); } diff --git a/packages/core/src/core_private_export.ts b/packages/core/src/core_private_export.ts index 7b0c67165e..2c10a126f6 100644 --- a/packages/core/src/core_private_export.ts +++ b/packages/core/src/core_private_export.ts @@ -16,7 +16,7 @@ export {Console as ɵConsole} from './console'; export {InjectableDef as ɵInjectableDef, InjectorDef as ɵInjectorDef, getInjectableDef as ɵgetInjectableDef} from './di/defs'; export {inject as ɵinject, setCurrentInjector as ɵsetCurrentInjector} from './di/injector'; export {APP_ROOT as ɵAPP_ROOT} from './di/scope'; -export {ivyEnabled as ɵivyEnabled} from './ivy_switch'; +export {ivyEnabled as ɵivyEnabled} from './ivy_switch/compiler/index'; export {ComponentFactory as ɵComponentFactory} from './linker/component_factory'; export {CodegenComponentFactoryResolver as ɵCodegenComponentFactoryResolver} from './linker/component_factory_resolver'; export {resolveComponentResources as ɵresolveComponentResources} from './metadata/resource_loading'; diff --git a/packages/core/src/core_render3_private_export.ts b/packages/core/src/core_render3_private_export.ts index 795274f36f..1182a8cdf9 100644 --- a/packages/core/src/core_render3_private_export.ts +++ b/packages/core/src/core_render3_private_export.ts @@ -21,10 +21,6 @@ export { DirectiveType as ɵDirectiveType, RenderFlags as ɵRenderFlags, directiveInject as ɵdirectiveInject, - injectElementRef as ɵinjectElementRef, - injectTemplateRef as ɵinjectTemplateRef, - injectViewContainerRef as ɵinjectViewContainerRef, - injectChangeDetectorRef as ɵinjectChangeDetectorRef, injectRenderer2 as ɵinjectRenderer2, injectAttribute as ɵinjectAttribute, getFactoryOf as ɵgetFactoryOf, @@ -187,6 +183,15 @@ export { R3_COMPILE_INJECTABLE__POST_NGCC__ as ɵR3_COMPILE_INJECTABLE__POST_NGCC__, R3_COMPILE_NGMODULE__POST_NGCC__ as ɵR3_COMPILE_NGMODULE__POST_NGCC__, R3_COMPILE_PIPE__POST_NGCC__ as ɵR3_COMPILE_PIPE__POST_NGCC__, - ivyEnable__POST_NGCC__ as ɵivyEnable__POST_NGCC__ -} from './ivy_switch_legacy'; + ivyEnable__POST_NGCC__ as ɵivyEnable__POST_NGCC__, +} from './ivy_switch/compiler/legacy'; + +export { + R3_ELEMENT_REF_FACTORY__POST_NGCC__ as ɵR3_ELEMENT_REF_FACTORY__POST_NGCC__, + R3_TEMPLATE_REF_FACTORY__POST_NGCC__ as ɵR3_TEMPLATE_REF_FACTORY__POST_NGCC__, + R3_CHANGE_DETECTOR_REF_FACTORY__POST_NGCC__ as ɵR3_CHANGE_DETECTOR_REF_FACTORY__POST_NGCC__, + R3_VIEW_CONTAINER_REF_FACTORY__POST_NGCC__ as ɵR3_VIEW_CONTAINER_REF_FACTORY__POST_NGCC__, +} from './ivy_switch/runtime/legacy'; + + // clang-format on diff --git a/packages/core/src/di/injectable.ts b/packages/core/src/di/injectable.ts index 43f109aff1..1ca4308bfe 100644 --- a/packages/core/src/di/injectable.ts +++ b/packages/core/src/di/injectable.ts @@ -6,7 +6,7 @@ * found in the LICENSE file at https://angular.io/license */ -import {R3_COMPILE_INJECTABLE} from '../ivy_switch'; +import {R3_COMPILE_INJECTABLE} from '../ivy_switch/compiler/index'; import {Type} from '../type'; import {makeDecorator} from '../util/decorators'; diff --git a/packages/core/src/ivy_switch.ts b/packages/core/src/ivy_switch/compiler/index.ts similarity index 93% rename from packages/core/src/ivy_switch.ts rename to packages/core/src/ivy_switch/compiler/index.ts index 41abdbfddc..f246142249 100644 --- a/packages/core/src/ivy_switch.ts +++ b/packages/core/src/ivy_switch/compiler/index.ts @@ -12,6 +12,6 @@ * For more information on how to run and debug tests with either Ivy or View Engine (legacy), * please see [BAZEL.md](./docs/BAZEL.md). */ -export * from './ivy_switch_legacy'; +export * from './legacy'; // TODO(alxhub): debug why metadata doesn't properly propagate through this file. diff --git a/packages/core/src/ivy_switch_on.ts b/packages/core/src/ivy_switch/compiler/ivy_switch_on.ts similarity index 66% rename from packages/core/src/ivy_switch_on.ts rename to packages/core/src/ivy_switch/compiler/ivy_switch_on.ts index 9c21b13a7b..0a802cf632 100644 --- a/packages/core/src/ivy_switch_on.ts +++ b/packages/core/src/ivy_switch/compiler/ivy_switch_on.ts @@ -6,10 +6,10 @@ * found in the LICENSE file at https://angular.io/license */ -import {compileComponent, compileDirective} from './render3/jit/directive'; -import {compileInjectable} from './render3/jit/injectable'; -import {compileNgModule} from './render3/jit/module'; -import {compilePipe} from './render3/jit/pipe'; +import {compileComponent, compileDirective} from '../../render3/jit/directive'; +import {compileInjectable} from '../../render3/jit/injectable'; +import {compileNgModule} from '../../render3/jit/module'; +import {compilePipe} from '../../render3/jit/pipe'; export const ivyEnabled = true; export const R3_COMPILE_COMPONENT = compileComponent; diff --git a/packages/core/src/ivy_switch_jit.ts b/packages/core/src/ivy_switch/compiler/jit.ts similarity index 100% rename from packages/core/src/ivy_switch_jit.ts rename to packages/core/src/ivy_switch/compiler/jit.ts diff --git a/packages/core/src/ivy_switch_legacy.ts b/packages/core/src/ivy_switch/compiler/legacy.ts similarity index 91% rename from packages/core/src/ivy_switch_legacy.ts rename to packages/core/src/ivy_switch/compiler/legacy.ts index 17bdfe7f28..0452db06d2 100644 --- a/packages/core/src/ivy_switch_legacy.ts +++ b/packages/core/src/ivy_switch/compiler/legacy.ts @@ -6,15 +6,16 @@ * found in the LICENSE file at https://angular.io/license */ -import {InjectableType, InjectorType, defineInjectable, defineInjector, getInjectableDef} from './di/defs'; -import {InjectableProvider} from './di/injectable'; -import {inject, injectArgs} from './di/injector'; -import {ClassSansProvider, ConstructorSansProvider, ExistingSansProvider, FactorySansProvider, StaticClassSansProvider, ValueProvider, ValueSansProvider} from './di/provider'; +import {InjectableType, InjectorType, defineInjectable, defineInjector, getInjectableDef} from '../../di/defs'; +import {InjectableProvider} from '../../di/injectable'; +import {inject, injectArgs} from '../../di/injector'; +import {ClassSansProvider, ConstructorSansProvider, ExistingSansProvider, FactorySansProvider, StaticClassSansProvider, ValueProvider, ValueSansProvider} from '../../di/provider'; +import {NgModule} from '../../metadata'; +import {ReflectionCapabilities} from '../../reflection/reflection_capabilities'; +import {Type} from '../../type'; +import {getClosureSafeProperty} from '../../util/property'; + import * as ivyOn from './ivy_switch_on'; -import {NgModule} from './metadata'; -import {ReflectionCapabilities} from './reflection/reflection_capabilities'; -import {Type} from './type'; -import {getClosureSafeProperty} from './util/property'; function noop() {} @@ -25,6 +26,7 @@ export const R3_COMPILE_DIRECTIVE__POST_NGCC__: DirectiveCompiler = ivyOn.R3_COM export const R3_COMPILE_INJECTABLE__POST_NGCC__: DirectiveCompiler = ivyOn.R3_COMPILE_INJECTABLE; export const R3_COMPILE_NGMODULE__POST_NGCC__: DirectiveCompiler = ivyOn.R3_COMPILE_NGMODULE; export const R3_COMPILE_PIPE__POST_NGCC__: DirectiveCompiler = ivyOn.R3_COMPILE_PIPE; + export const ivyEnable__POST_NGCC__: boolean = ivyOn.ivyEnabled; const R3_COMPILE_COMPONENT__PRE_NGCC__: DirectiveCompiler = noop; @@ -32,6 +34,7 @@ const R3_COMPILE_DIRECTIVE__PRE_NGCC__: DirectiveCompiler = noop; const R3_COMPILE_INJECTABLE__PRE_NGCC__: DirectiveCompiler = preR3InjectableCompile; const R3_COMPILE_NGMODULE__PRE_NGCC__: DirectiveCompiler = preR3NgModuleCompile; const R3_COMPILE_PIPE__PRE_NGCC__: DirectiveCompiler = noop; + const ivyEnable__PRE_NGCC__ = false; export const ivyEnabled = ivyEnable__PRE_NGCC__; @@ -111,4 +114,4 @@ function preR3InjectableCompile( factory: convertInjectableProviderToFactory(injectableType, options), }); } -} \ No newline at end of file +} diff --git a/packages/core/src/ivy_switch_local.ts b/packages/core/src/ivy_switch/compiler/local.ts similarity index 100% rename from packages/core/src/ivy_switch_local.ts rename to packages/core/src/ivy_switch/compiler/local.ts diff --git a/packages/core/src/ivy_switch/runtime/index.ts b/packages/core/src/ivy_switch/runtime/index.ts new file mode 100644 index 0000000000..c69e046061 --- /dev/null +++ b/packages/core/src/ivy_switch/runtime/index.ts @@ -0,0 +1,15 @@ +/** + * @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 + */ + +/** + * This file is used to control if the default rendering pipeline should be `ViewEngine` or `Ivy`. + * + * For more information on how to run and debug tests with either Ivy or View Engine (legacy), + * please see [BAZEL.md](./docs/BAZEL.md). + */ +export * from './legacy'; diff --git a/packages/core/src/ivy_switch/runtime/ivy_switch_on.ts b/packages/core/src/ivy_switch/runtime/ivy_switch_on.ts new file mode 100644 index 0000000000..8733a2a197 --- /dev/null +++ b/packages/core/src/ivy_switch/runtime/ivy_switch_on.ts @@ -0,0 +1,14 @@ +/** + * @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 {injectChangeDetectorRef, injectElementRef, injectTemplateRef, injectViewContainerRef} from '../../render3/view_engine_compatibility'; + +export const R3_ELEMENT_REF_FACTORY = injectElementRef; +export const R3_TEMPLATE_REF_FACTORY = injectTemplateRef; +export const R3_CHANGE_DETECTOR_REF_FACTORY = injectChangeDetectorRef; +export const R3_VIEW_CONTAINER_REF_FACTORY = injectViewContainerRef; diff --git a/packages/core/src/ivy_switch/runtime/jit.ts b/packages/core/src/ivy_switch/runtime/jit.ts new file mode 100644 index 0000000000..e5267d856f --- /dev/null +++ b/packages/core/src/ivy_switch/runtime/jit.ts @@ -0,0 +1,10 @@ + +/** + * @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 + */ + +export * from './ivy_switch_on'; diff --git a/packages/core/src/ivy_switch/runtime/legacy.ts b/packages/core/src/ivy_switch/runtime/legacy.ts new file mode 100644 index 0000000000..660548de32 --- /dev/null +++ b/packages/core/src/ivy_switch/runtime/legacy.ts @@ -0,0 +1,31 @@ +/** + * @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 * as ivyOn from './ivy_switch_on'; + +function noopFactory(...tokens: any[]): any {} + +type FactoryFunction = (...tokens: any[]) => T; + +export const R3_ELEMENT_REF_FACTORY__POST_NGCC__: FactoryFunction = ivyOn.R3_ELEMENT_REF_FACTORY; +export const R3_TEMPLATE_REF_FACTORY__POST_NGCC__: FactoryFunction = ivyOn.R3_TEMPLATE_REF_FACTORY; +export const R3_CHANGE_DETECTOR_REF_FACTORY__POST_NGCC__: FactoryFunction = + ivyOn.R3_CHANGE_DETECTOR_REF_FACTORY; +export const R3_VIEW_CONTAINER_REF_FACTORY__POST_NGCC__: FactoryFunction = + ivyOn.R3_VIEW_CONTAINER_REF_FACTORY; + + +export const R3_ELEMENT_REF_FACTORY__PRE_NGCC__ = noopFactory; +export const R3_TEMPLATE_REF_FACTORY__PRE_NGCC__ = noopFactory; +export const R3_CHANGE_DETECTOR_REF_FACTORY__PRE_NGCC__ = noopFactory; +export const R3_VIEW_CONTAINER_REF_FACTORY__PRE_NGCC__ = noopFactory; + +export let R3_ELEMENT_REF_FACTORY = R3_ELEMENT_REF_FACTORY__PRE_NGCC__; +export let R3_TEMPLATE_REF_FACTORY = R3_TEMPLATE_REF_FACTORY__PRE_NGCC__; +export let R3_CHANGE_DETECTOR_REF_FACTORY = R3_CHANGE_DETECTOR_REF_FACTORY__PRE_NGCC__; +export let R3_VIEW_CONTAINER_REF_FACTORY = R3_VIEW_CONTAINER_REF_FACTORY__PRE_NGCC__; diff --git a/packages/core/src/ivy_switch/runtime/local.ts b/packages/core/src/ivy_switch/runtime/local.ts new file mode 100644 index 0000000000..e5267d856f --- /dev/null +++ b/packages/core/src/ivy_switch/runtime/local.ts @@ -0,0 +1,10 @@ + +/** + * @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 + */ + +export * from './ivy_switch_on'; diff --git a/packages/core/src/linker/element_ref.ts b/packages/core/src/linker/element_ref.ts index 9adc4128bd..626594133e 100644 --- a/packages/core/src/linker/element_ref.ts +++ b/packages/core/src/linker/element_ref.ts @@ -6,6 +6,8 @@ * found in the LICENSE file at https://angular.io/license */ +import {R3_ELEMENT_REF_FACTORY} from '../ivy_switch/runtime/index'; + /** * A wrapper around a native element inside of a View. * @@ -46,4 +48,7 @@ export class ElementRef { public nativeElement: T; constructor(nativeElement: T) { this.nativeElement = nativeElement; } + + /** @internal */ + static __NG_ELEMENT_ID__: () => ElementRef = () => R3_ELEMENT_REF_FACTORY(ElementRef); } diff --git a/packages/core/src/linker/template_ref.ts b/packages/core/src/linker/template_ref.ts index a7880bdd46..580abf4f7b 100644 --- a/packages/core/src/linker/template_ref.ts +++ b/packages/core/src/linker/template_ref.ts @@ -6,6 +6,8 @@ * found in the LICENSE file at https://angular.io/license */ +import {R3_TEMPLATE_REF_FACTORY} from '../ivy_switch/runtime/index'; + import {ElementRef} from './element_ref'; import {EmbeddedViewRef} from './view_ref'; @@ -14,12 +16,12 @@ import {EmbeddedViewRef} from './view_ref'; * Represents an embedded template that can be used to instantiate embedded views. * To instantiate embedded views based on a template, use the `ViewContainerRef` * method `createEmbeddedView()`. - * + * * Access a `TemplateRef` instance by placing a directive on an `` * element (or directive prefixed with `*`). The `TemplateRef` for the embedded view * is injected into the constructor of the directive, * using the `TemplateRef` token. - * + * * You can also use a `Query` to find a `TemplateRef` associated with * a component or a directive. * @@ -48,4 +50,8 @@ export abstract class TemplateRef { * @returns The new view object. */ abstract createEmbeddedView(context: C): EmbeddedViewRef; + + /** @internal */ + static __NG_ELEMENT_ID__: + () => TemplateRef = () => R3_TEMPLATE_REF_FACTORY(TemplateRef, ElementRef) } diff --git a/packages/core/src/linker/view_container_ref.ts b/packages/core/src/linker/view_container_ref.ts index 113555e474..d69d9a1b05 100644 --- a/packages/core/src/linker/view_container_ref.ts +++ b/packages/core/src/linker/view_container_ref.ts @@ -7,6 +7,8 @@ */ import {Injector} from '../di/injector'; +import {R3_VIEW_CONTAINER_REF_FACTORY} from '../ivy_switch/runtime/index'; + import {ComponentFactory, ComponentRef} from './component_factory'; import {ElementRef} from './element_ref'; import {NgModuleRef} from './ng_module_factory'; @@ -139,4 +141,8 @@ export abstract class ViewContainerRef { * If not specified, the last view in the container is detached. */ abstract detach(index?: number): ViewRef|null; + + /** @internal */ + static __NG_ELEMENT_ID__: + () => ViewContainerRef = () => R3_VIEW_CONTAINER_REF_FACTORY(ViewContainerRef, ElementRef) } diff --git a/packages/core/src/metadata/directives.ts b/packages/core/src/metadata/directives.ts index 44a6bbd0db..208423363f 100644 --- a/packages/core/src/metadata/directives.ts +++ b/packages/core/src/metadata/directives.ts @@ -8,7 +8,7 @@ import {ChangeDetectionStrategy} from '../change_detection/constants'; import {Provider} from '../di'; -import {R3_COMPILE_COMPONENT, R3_COMPILE_DIRECTIVE, R3_COMPILE_PIPE} from '../ivy_switch'; +import {R3_COMPILE_COMPONENT, R3_COMPILE_DIRECTIVE, R3_COMPILE_PIPE} from '../ivy_switch/compiler/index'; import {NG_BASE_DEF} from '../render3/fields'; import {Type} from '../type'; import {TypeDecorator, makeDecorator, makePropDecorator} from '../util/decorators'; diff --git a/packages/core/src/metadata/ng_module.ts b/packages/core/src/metadata/ng_module.ts index d2ade2a309..3f98f5b119 100644 --- a/packages/core/src/metadata/ng_module.ts +++ b/packages/core/src/metadata/ng_module.ts @@ -8,7 +8,7 @@ import {ApplicationRef} from '../application_ref'; import {Provider} from '../di/provider'; -import {R3_COMPILE_NGMODULE} from '../ivy_switch'; +import {R3_COMPILE_NGMODULE} from '../ivy_switch/compiler/index'; import {Type} from '../type'; import {TypeDecorator, makeDecorator} from '../util/decorators'; diff --git a/packages/core/src/render3/VIEW_DATA.md b/packages/core/src/render3/VIEW_DATA.md index 01dc9461fc..5356ffd2b8 100644 --- a/packages/core/src/render3/VIEW_DATA.md +++ b/packages/core/src/render3/VIEW_DATA.md @@ -382,17 +382,19 @@ NOTE: An interesting thing about these objects is that they are not memoized `injector.get(ElementRef) !== injector.get(ElementRef)`. This could be considered a bug, it means that we don't have to allocate storage space for them. +We should treat these special objects like any other token. `directiveInject()` already reads a special `NG_ELEMENT_ID` +property set on directives to locate their bit in the bloom filter. We can set this same property on special objects, +but point to a factory function rather than an element ID number. When we check that property in `directiveInject()` +and see that it's a function, we know to invoke the factory function directly instead of searching the node tree. + ```typescript -@Injectable({ - provideIn: '__node__' as any // Special token not available to the developer - useFactory: injectElementRef // existing function which generates ElementRef -}) class ElementRef { ... + static __NG_ELEMENT_ID__ = () => injectElementRef(); } ``` -Consequence of the above is that `injector.get(ElementRef)` returns an instance of `ElementRef` without `Injector` having to know about `ElementRef` at compile time. +Consequence of the above is that `directiveInject(ElementRef)` returns an instance of `ElementRef` without `Injector` having to know about `ElementRef` at compile time. # `EXPANDO` and Injecting the `Injector`. @@ -421,10 +423,7 @@ function inject(token: any): any { let injectableDef; if (typeof token === 'function' && injectableDef = token.ngInjectableDef) { const provideIn = injectableDef.provideIn; - if (provideIn === '__node__') { - // if it is a special object just call its factory - return injectableDef.useFactory(); - } else if (provideIn === '__node_injector__') { + if (provideIn === '__node_injector__') { // if we are injecting `Injector` than create a wrapper object around the inject but which // is bound to the current node. return createInjector(); diff --git a/packages/core/src/render3/component_ref.ts b/packages/core/src/render3/component_ref.ts index 6338487732..365778d6ee 100644 --- a/packages/core/src/render3/component_ref.ts +++ b/packages/core/src/render3/component_ref.ts @@ -24,6 +24,7 @@ import {ComponentDefInternal, RenderFlags} from './interfaces/definition'; import {LElementNode, TElementNode, TNode, TNodeType, TViewNode} from './interfaces/node'; import {RElement, RendererFactory3, domRendererFactory3} from './interfaces/renderer'; import {FLAGS, INJECTOR, LViewData, LViewFlags, RootContext, TVIEW} from './interfaces/view'; +import {createElementRef} from './view_engine_compatibility'; import {RootViewRef, ViewRef} from './view_ref'; export class ComponentFactoryResolver extends viewEngine_ComponentFactoryResolver { @@ -176,8 +177,10 @@ export class ComponentFactory extends viewEngine_ComponentFactory { if (rendererFactory.end) rendererFactory.end(); } - const componentRef = - new ComponentRef(this.componentType, component, rootView, injector, hostNode !); + const componentRef = new ComponentRef( + this.componentType, component, rootView, injector, + createElementRef(viewEngine_ElementRef, tElementNode, rootView)); + if (isInternalRootView) { // The host element of the internal root view is attached to the component's host view node componentRef.hostView._tViewNode !.child = tElementNode; @@ -186,6 +189,19 @@ export class ComponentFactory extends viewEngine_ComponentFactory { } } +const componentFactoryResolver: ComponentFactoryResolver = new ComponentFactoryResolver(); + +/** + * Creates a ComponentFactoryResolver and stores it on the injector. Or, if the + * ComponentFactoryResolver + * already exists, retrieves the existing ComponentFactoryResolver. + * + * @returns The ComponentFactoryResolver instance to use + */ +export function injectComponentFactoryResolver(): viewEngine_ComponentFactoryResolver { + return componentFactoryResolver; +} + /** * Represents an instance of a Component created via a {@link ComponentFactory}. * @@ -196,7 +212,6 @@ export class ComponentFactory extends viewEngine_ComponentFactory { */ export class ComponentRef extends viewEngine_ComponentRef { destroyCbs: (() => void)[]|null = []; - location: viewEngine_ElementRef; injector: Injector; instance: T; hostView: ViewRef; @@ -205,13 +220,12 @@ export class ComponentRef extends viewEngine_ComponentRef { constructor( componentType: Type, instance: T, rootView: LViewData, injector: Injector, - hostNode: RElement) { + public location: viewEngine_ElementRef) { super(); this.instance = instance; this.hostView = this.changeDetectorRef = new RootViewRef(rootView); this.hostView._tViewNode = createNodeAtIndex(-1, TNodeType.View, null, null, null, rootView); this.injector = injector; - this.location = new viewEngine_ElementRef(hostNode); this.componentType = componentType; } diff --git a/packages/core/src/render3/di.ts b/packages/core/src/render3/di.ts index 1299ed2558..4db47834a4 100644 --- a/packages/core/src/render3/di.ts +++ b/packages/core/src/render3/di.ts @@ -9,44 +9,23 @@ // We are temporarily importing the existing viewEngine_from core so we can be sure we are // correctly implementing its interfaces for backwards compatibility. -import {ChangeDetectorRef as viewEngine_ChangeDetectorRef} from '../change_detection/change_detector_ref'; import {getInjectableDef, getInjectorDef} from '../di/defs'; import {InjectionToken} from '../di/injection_token'; import {InjectFlags, Injector, NullInjector, inject, setCurrentInjector} from '../di/injector'; -import {ComponentFactory as viewEngine_ComponentFactory, ComponentRef as viewEngine_ComponentRef} from '../linker/component_factory'; -import {ComponentFactoryResolver as viewEngine_ComponentFactoryResolver} from '../linker/component_factory_resolver'; -import {ElementRef as viewEngine_ElementRef} from '../linker/element_ref'; -import {NgModuleRef as viewEngine_NgModuleRef} from '../linker/ng_module_factory'; -import {TemplateRef as viewEngine_TemplateRef} from '../linker/template_ref'; -import {ViewContainerRef as viewEngine_ViewContainerRef} from '../linker/view_container_ref'; -import {EmbeddedViewRef as viewEngine_EmbeddedViewRef, ViewRef as viewEngine_ViewRef} from '../linker/view_ref'; import {Renderer2} from '../render'; import {Type} from '../type'; import {assertDefined, assertGreaterThan, assertLessThan} from './assert'; -import {ComponentFactoryResolver} from './component_ref'; import {getComponentDef, getDirectiveDef, getPipeDef} from './definition'; -import {_getViewData, addToViewTree, assertPreviousIsParent, createEmbeddedViewAndNode, createLContainer, createLNodeObject, createTNode, getPreviousOrParentNode, getPreviousOrParentTNode, getRenderer, loadElement, renderEmbeddedTemplate, resolveDirective} from './instructions'; -import {LContainer, RENDER_PARENT, VIEWS} from './interfaces/container'; +import {NG_ELEMENT_ID} from './fields'; +import {_getViewData, addToViewTree, assertPreviousIsParent, createEmbeddedViewAndNode, createLContainer, createLNodeObject, createTNode, getPreviousOrParentNode, getPreviousOrParentTNode, getRenderer, loadElement, renderEmbeddedTemplate, resolveDirective, setEnvironment} from './instructions'; import {DirectiveDefInternal, RenderFlags} from './interfaces/definition'; import {LInjector} from './interfaces/injector'; import {AttributeMarker, LContainerNode, LElementContainerNode, LElementNode, LNode, TContainerNode, TElementContainerNode, TElementNode, TNode, TNodeFlags, TNodeType, TViewNode} from './interfaces/node'; -import {LQueries, QueryReadType} from './interfaces/query'; import {Renderer3, isProceduralRenderer} from './interfaces/renderer'; import {CONTEXT, DIRECTIVES, HOST_NODE, INJECTOR, LViewData, QUERIES, RENDERER, TVIEW, TView} from './interfaces/view'; import {assertNodeOfPossibleTypes, assertNodeType} from './node_assert'; import {addRemoveViewFromContainer, appendChild, detachView, findComponentView, getBeforeNodeForView, getHostElementNode, getParentLNode, getParentOrContainerNode, getRenderParent, insertView, removeView} from './node_manipulation'; -import {getLNode, isComponent} from './util'; -import {ViewRef} from './view_ref'; - - - -/** - * If a directive is diPublic, bloomAdd sets a property on the type with this constant as - * the key and the directive's unique ID as the value. This allows us to map directives to their - * bloom filter bit for DI. - */ -const NG_ELEMENT_ID = '__NG_ELEMENT_ID__'; /** * The number of slots in each bloom filter (used by DI). The larger this number, the fewer @@ -196,56 +175,6 @@ export function directiveInject( return getOrCreateInjectable(getOrCreateNodeInjector(), token, flags); } -/** - * Creates an ElementRef and stores it on the injector. - * Or, if the ElementRef already exists, retrieves the existing ElementRef. - * - * @returns The ElementRef instance to use - */ -export function injectElementRef(): viewEngine_ElementRef { - return createElementRef(getPreviousOrParentTNode(), _getViewData()); -} - -/** - * Creates a TemplateRef and stores it on the injector. Or, if the TemplateRef already - * exists, retrieves the existing TemplateRef. - * - * @returns The TemplateRef instance to use - */ -export function injectTemplateRef(): viewEngine_TemplateRef { - return createTemplateRef(getPreviousOrParentTNode(), _getViewData()); -} - -/** - * Creates a ViewContainerRef and stores it on the injector. Or, if the ViewContainerRef - * already exists, retrieves the existing ViewContainerRef. - * - * @returns The ViewContainerRef instance to use - */ -export function injectViewContainerRef(): viewEngine_ViewContainerRef { - const previousTNode = - getPreviousOrParentTNode() as TElementNode | TElementContainerNode | TContainerNode; - return createContainerRef(previousTNode, _getViewData()); -} - -/** Returns a ChangeDetectorRef (a.k.a. a ViewRef) */ -export function injectChangeDetectorRef(): viewEngine_ChangeDetectorRef { - return createViewRef(getPreviousOrParentTNode(), _getViewData(), null); -} - -/** - * Creates a ComponentFactoryResolver and stores it on the injector. Or, if the - * ComponentFactoryResolver - * already exists, retrieves the existing ComponentFactoryResolver. - * - * @returns The ComponentFactoryResolver instance to use - */ -export function injectComponentFactoryResolver(): viewEngine_ComponentFactoryResolver { - return componentFactoryResolver; -} -const componentFactoryResolver: ComponentFactoryResolver = new ComponentFactoryResolver(); - - export function injectRenderer2(): Renderer2 { return getOrCreateRenderer2(getOrCreateNodeInjector()); } @@ -298,27 +227,6 @@ export function injectAttribute(attrNameToInject: string): string|undefined { return undefined; } -/** - * Creates a ViewRef and stores it on the injector as ChangeDetectorRef (public alias). - * - * @param hostTNode The node that is requesting a ChangeDetectorRef - * @param hostView The view to which the node belongs - * @param context The context for this change detector ref - * @returns The ChangeDetectorRef to use - */ -export function createViewRef( - hostTNode: TNode, hostView: LViewData, context: any): viewEngine_ChangeDetectorRef { - if (isComponent(hostTNode)) { - const componentIndex = hostTNode.flags >> TNodeFlags.DirectiveStartingIndexShift; - const componentView = getLNode(hostTNode, hostView).data as LViewData; - return new ViewRef(componentView, context, componentIndex); - } else if (hostTNode.type === TNodeType.Element) { - const hostComponentView = findComponentView(hostView); - return new ViewRef(hostComponentView, hostComponentView[CONTEXT], -1); - } - return null !; -} - function getOrCreateRenderer2(di: LInjector): Renderer2 { const renderer = di.view[RENDERER]; if (isProceduralRenderer(renderer)) { @@ -342,11 +250,14 @@ function getOrCreateRenderer2(di: LInjector): Renderer2 { export function getOrCreateInjectable( nodeInjector: LInjector, token: Type| InjectionToken, flags: InjectFlags = InjectFlags.Default): T|null { - const bloomHash = bloomHashBit(token); + const bloomHash = bloomHashBitOrFactory(token); + // If the ID stored here is a function, this is a special object like ElementRef or TemplateRef + // so just call the factory function to create it. + if (typeof bloomHash === 'function') return bloomHash(); // If the token has a bloom hash, then it is a directive that is public to the injection system // (diPublic) otherwise fall back to the module injector. - if (bloomHash !== null) { + if (bloomHash != null) { let injector: LInjector|null = nodeInjector; while (injector) { @@ -433,9 +344,9 @@ function searchMatchesQueuedForCreation(token: any, hostTView: TView): T|null * @param token the injection token * @returns the matching bit to check in the bloom filter or `null` if the token is not known. */ -function bloomHashBit(token: Type| InjectionToken): number|null { - let id: number|undefined = (token as any)[NG_ELEMENT_ID]; - return typeof id === 'number' ? id & BLOOM_MASK : null; +function bloomHashBitOrFactory(token: Type| InjectionToken): number|Function|undefined { + const tokenId: number|undefined = (token as any)[NG_ELEMENT_ID] || null; + return typeof tokenId === 'number' ? tokenId & BLOOM_MASK : tokenId; } /** @@ -525,258 +436,18 @@ function sameHostView(injector: LInjector): boolean { return !!injector.parent && injector.parent.view === injector.view; } -export class ReadFromInjectorFn { - constructor(readonly read: (tNode: TNode, view: LViewData, directiveIndex?: number) => T) {} -} - -/** - * Creates an ElementRef for a given node injector and stores it on the injector. - * - * @param di The node injector where we should store a created ElementRef - * @returns The ElementRef instance to use - */ -export function createElementRef(tNode: TNode, view: LViewData): viewEngine_ElementRef { - return new ElementRef(getLNode(tNode, view).native); -} - -export const QUERY_READ_TEMPLATE_REF = >>( - new ReadFromInjectorFn>( - (tNode: TNode, view: LViewData) => { return createTemplateRef(tNode, view);}) as any); - -export const QUERY_READ_CONTAINER_REF = >( - new ReadFromInjectorFn( - (tNode: TNode, view: LViewData) => createContainerRef( - tNode as TElementNode | TContainerNode | TElementContainerNode, view)) as any); - -export const QUERY_READ_ELEMENT_REF = - >(new ReadFromInjectorFn( - (tNode: TNode, view: LViewData) => createElementRef(tNode, view)) as any); - -export const QUERY_READ_FROM_NODE = - (new ReadFromInjectorFn((tNode: TNode, view: LViewData, directiveIdx: number) => { - ngDevMode && assertNodeOfPossibleTypes( - tNode, TNodeType.Container, TNodeType.Element, TNodeType.ElementContainer); - if (directiveIdx > -1) { - return view[DIRECTIVES] ![directiveIdx]; - } - if (tNode.type === TNodeType.Element || tNode.type === TNodeType.ElementContainer) { - return createElementRef(tNode, view); - } - if (tNode.type === TNodeType.Container) { - return createTemplateRef(tNode, view); - } - if (ngDevMode) { - // should never happen - throw new Error(`Unexpected node type: ${tNode.type}`); - } - }) as any as QueryReadType); - -/** A ref to a node's native element. */ -class ElementRef extends viewEngine_ElementRef {} - -/** - * Creates a ViewContainerRef and stores it on the injector. - * - * @param hostTNode The node that is requesting a ViewContainerRef - * @param hostView The view to which the node belongs - * @returns The ViewContainerRef instance to use - */ -export function createContainerRef( - hostTNode: TElementNode | TContainerNode | TElementContainerNode, - hostView: LViewData): viewEngine_ViewContainerRef { - const hostLNode = getLNode(hostTNode, hostView); - ngDevMode && assertNodeOfPossibleTypes( - hostTNode, TNodeType.Container, TNodeType.Element, TNodeType.ElementContainer); - - const lContainer = createLContainer(hostView, true); - const comment = hostView[RENDERER].createComment(ngDevMode ? 'container' : ''); - const lContainerNode: LContainerNode = - createLNodeObject(TNodeType.Container, hostLNode.nodeInjector, comment, lContainer); - - lContainer[RENDER_PARENT] = getRenderParent(hostTNode, hostView); - - appendChild(comment, hostTNode, hostView); - - if (!hostTNode.dynamicContainerNode) { - hostTNode.dynamicContainerNode = - createTNode(TNodeType.Container, -1, null, null, hostTNode, null); - } - - hostLNode.dynamicLContainerNode = lContainerNode; - addToViewTree(hostView, hostTNode.index as number, lContainer); - - return new ViewContainerRef( - lContainer, hostTNode.dynamicContainerNode as TContainerNode, hostTNode, hostView); -} - export class NodeInjector implements Injector { constructor(private _lInjector: LInjector) {} get(token: any): any { - if (token === viewEngine_TemplateRef) { - return createTemplateRef(this._lInjector.tNode, this._lInjector.view); - } - if (token === viewEngine_ViewContainerRef) { - return createContainerRef(this._lInjector.tNode, this._lInjector.view); - } - if (token === viewEngine_ElementRef) { - return createElementRef(this._lInjector.tNode, this._lInjector.view); - } - if (token === viewEngine_ChangeDetectorRef) { - return createViewRef(this._lInjector.tNode, this._lInjector.view, null); - } if (token === Renderer2) { return getOrCreateRenderer2(this._lInjector); } + setEnvironment(this._lInjector.tNode, this._lInjector.view); return getOrCreateInjectable(this._lInjector, token); } } - -/** - * A ref to a container that enables adding and removing views from that container - * imperatively. - */ -class ViewContainerRef extends viewEngine_ViewContainerRef { - private _viewRefs: viewEngine_ViewRef[] = []; - - constructor( - private _lContainer: LContainer, private _tContainerNode: TContainerNode, - private _hostTNode: TElementNode|TContainerNode|TElementContainerNode, - private _hostView: LViewData) { - super(); - } - - get element(): ElementRef { - // TODO: Remove LNode lookup when removing LNode.nodeInjector - const injector = - getOrCreateNodeInjectorForNode(this._getHostNode(), this._hostTNode, this._hostView); - return createElementRef(injector.tNode, injector.view); - } - - get injector(): Injector { - // TODO: Remove LNode lookup when removing LNode.nodeInjector - const injector = - getOrCreateNodeInjectorForNode(this._getHostNode(), this._hostTNode, this._hostView); - return new NodeInjector(injector); - } - - /** @deprecated No replacement */ - get parentInjector(): Injector { - const parentLInjector = getParentLNode(this._hostTNode, this._hostView) !.nodeInjector; - return parentLInjector ? new NodeInjector(parentLInjector) : new NullInjector(); - } - - clear(): void { - while (this._lContainer[VIEWS].length) { - this.remove(0); - } - } - - get(index: number): viewEngine_ViewRef|null { return this._viewRefs[index] || null; } - - get length(): number { return this._lContainer[VIEWS].length; } - - createEmbeddedView(templateRef: viewEngine_TemplateRef, context?: C, index?: number): - viewEngine_EmbeddedViewRef { - const adjustedIdx = this._adjustIndex(index); - const viewRef = (templateRef as TemplateRef) - .createEmbeddedView( - context || {}, this._lContainer, this._tContainerNode, - this._hostView, adjustedIdx); - (viewRef as ViewRef).attachToViewContainerRef(this); - this._viewRefs.splice(adjustedIdx, 0, viewRef); - return viewRef; - } - - createComponent( - componentFactory: viewEngine_ComponentFactory, index?: number|undefined, - injector?: Injector|undefined, projectableNodes?: any[][]|undefined, - ngModuleRef?: viewEngine_NgModuleRef|undefined): viewEngine_ComponentRef { - const contextInjector = injector || this.parentInjector; - if (!ngModuleRef && contextInjector) { - ngModuleRef = contextInjector.get(viewEngine_NgModuleRef, null); - } - - const componentRef = - componentFactory.create(contextInjector, projectableNodes, undefined, ngModuleRef); - this.insert(componentRef.hostView, index); - return componentRef; - } - - insert(viewRef: viewEngine_ViewRef, index?: number): viewEngine_ViewRef { - if (viewRef.destroyed) { - throw new Error('Cannot insert a destroyed View in a ViewContainer!'); - } - const lView = (viewRef as ViewRef)._view !; - const adjustedIdx = this._adjustIndex(index); - - insertView( - lView, this._lContainer, this._hostView, adjustedIdx, this._tContainerNode.parent !.index); - - const container = this._getHostNode().dynamicLContainerNode !; - const beforeNode = getBeforeNodeForView(adjustedIdx, this._lContainer[VIEWS], container); - addRemoveViewFromContainer(lView, true, beforeNode); - - (viewRef as ViewRef).attachToViewContainerRef(this); - this._viewRefs.splice(adjustedIdx, 0, viewRef); - - return viewRef; - } - - move(viewRef: viewEngine_ViewRef, newIndex: number): viewEngine_ViewRef { - const index = this.indexOf(viewRef); - this.detach(index); - this.insert(viewRef, this._adjustIndex(newIndex)); - return viewRef; - } - - indexOf(viewRef: viewEngine_ViewRef): number { return this._viewRefs.indexOf(viewRef); } - - remove(index?: number): void { - const adjustedIdx = this._adjustIndex(index, -1); - removeView(this._lContainer, this._tContainerNode as TContainerNode, adjustedIdx); - this._viewRefs.splice(adjustedIdx, 1); - } - - detach(index?: number): viewEngine_ViewRef|null { - const adjustedIdx = this._adjustIndex(index, -1); - detachView(this._lContainer, adjustedIdx, !!this._tContainerNode.detached); - return this._viewRefs.splice(adjustedIdx, 1)[0] || null; - } - - private _adjustIndex(index?: number, shift: number = 0) { - if (index == null) { - return this._lContainer[VIEWS].length + shift; - } - if (ngDevMode) { - assertGreaterThan(index, -1, 'index must be positive'); - // +1 because it's legal to insert at the end. - assertLessThan(index, this._lContainer[VIEWS].length + 1 + shift, 'index'); - } - return index; - } - - private _getHostNode() { return getLNode(this._hostTNode, this._hostView); } -} - -/** - * Creates a TemplateRef and stores it on the injector. - * - * @param hostTNode The node that is requesting a TemplateRef - * @param hostView The view to which the node belongs - * @returns The TemplateRef instance to use - */ -export function createTemplateRef( - hostTNode: TNode, hostView: LViewData): viewEngine_TemplateRef { - const hostNode = getLNode(hostTNode, hostView); - ngDevMode && assertNodeType(hostTNode, TNodeType.Container); - ngDevMode && assertDefined(hostTNode.tViews, 'TView must be allocated'); - return new TemplateRef( - hostView, createElementRef(hostTNode, hostView), hostTNode.tViews as TView, getRenderer(), - hostNode.data ![QUERIES]); -} - export function getFactoryOf(type: Type): ((type?: Type) => T)|null { const typeAny = type as any; const def = getComponentDef(typeAny) || getDirectiveDef(typeAny) || @@ -800,33 +471,3 @@ export function getInheritedFactory(type: Type): (type: Type) => T { return (t) => new t(); } } - -class TemplateRef extends viewEngine_TemplateRef { - constructor( - private _declarationParentView: LViewData, readonly elementRef: viewEngine_ElementRef, - private _tView: TView, private _renderer: Renderer3, private _queries: LQueries|null) { - super(); - } - - createEmbeddedView( - context: T, container?: LContainer, tContainerNode?: TContainerNode, hostView?: LViewData, - index?: number): viewEngine_EmbeddedViewRef { - const lView = createEmbeddedViewAndNode( - this._tView, context, this._declarationParentView, this._renderer, this._queries); - if (container) { - insertView(lView, container, hostView !, index !, tContainerNode !.parent !.index); - } - renderEmbeddedTemplate(lView, this._tView, context, RenderFlags.Create); - const viewRef = new ViewRef(lView, context, -1); - viewRef._tViewNode = lView[HOST_NODE] as TViewNode; - return viewRef; - } -} - -/** - * Retrieves `TemplateRef` instance from `Injector` when a local reference is placed on the - * `` element. - */ -export function templateRefExtractor(tNode: TContainerNode, currentView: LViewData) { - return createTemplateRef(tNode, currentView); -} diff --git a/packages/core/src/render3/fields.ts b/packages/core/src/render3/fields.ts index 90c62010fc..3a187f0856 100644 --- a/packages/core/src/render3/fields.ts +++ b/packages/core/src/render3/fields.ts @@ -15,3 +15,10 @@ export const NG_INJECTOR_DEF = getClosureSafeProperty({ngInjectorDef: getClosure export const NG_PIPE_DEF = getClosureSafeProperty({ngPipeDef: getClosureSafeProperty}); export const NG_MODULE_DEF = getClosureSafeProperty({ngModuleDef: getClosureSafeProperty}); export const NG_BASE_DEF = getClosureSafeProperty({ngBaseDef: getClosureSafeProperty}); + +/** + * If a directive is diPublic, bloomAdd sets a property on the type with this constant as + * the key and the directive's unique ID as the value. This allows us to map directives to their + * bloom filter bit for DI. + */ +export const NG_ELEMENT_ID = getClosureSafeProperty({__NG_ELEMENT_ID__: getClosureSafeProperty}); diff --git a/packages/core/src/render3/index.ts b/packages/core/src/render3/index.ts index 0628054d3b..9641e59adc 100644 --- a/packages/core/src/render3/index.ts +++ b/packages/core/src/render3/index.ts @@ -13,8 +13,8 @@ import {NgOnChangesFeature} from './features/ng_onchanges_feature'; import {PublicFeature} from './features/public_feature'; import {BaseDef, ComponentDef, ComponentDefInternal, ComponentTemplate, ComponentType, DirectiveDef, DirectiveDefFlags, DirectiveDefInternal, DirectiveType, PipeDef} from './interfaces/definition'; -export {ComponentFactory, ComponentFactoryResolver, ComponentRef, WRAP_RENDERER_FACTORY2} from './component_ref'; -export {QUERY_READ_CONTAINER_REF, QUERY_READ_ELEMENT_REF, QUERY_READ_FROM_NODE, QUERY_READ_TEMPLATE_REF, directiveInject, getFactoryOf, getInheritedFactory, injectAttribute, injectChangeDetectorRef, injectComponentFactoryResolver, injectElementRef, injectRenderer2, injectTemplateRef, injectViewContainerRef, templateRefExtractor} from './di'; +export {ComponentFactory, ComponentFactoryResolver, ComponentRef, WRAP_RENDERER_FACTORY2, injectComponentFactoryResolver} from './component_ref'; +export {directiveInject, getFactoryOf, getInheritedFactory, injectAttribute, injectRenderer2} from './di'; export {RenderFlags} from './interfaces/definition'; export {CssSelectorList} from './interfaces/projection'; @@ -119,6 +119,7 @@ export { QueryList, query, queryRefresh, + QUERY_READ_CONTAINER_REF, QUERY_READ_ELEMENT_REF, QUERY_READ_FROM_NODE, QUERY_READ_TEMPLATE_REF } from './query'; export { registerContentQuery, @@ -138,6 +139,8 @@ export { pureFunctionV, } from './pure_function'; +export {templateRefExtractor} from './view_engine_compatibility'; + // clang-format on diff --git a/packages/core/src/render3/instructions.ts b/packages/core/src/render3/instructions.ts index 87667b7903..72a707c331 100644 --- a/packages/core/src/render3/instructions.ts +++ b/packages/core/src/render3/instructions.ts @@ -30,7 +30,6 @@ import {isNodeMatchingSelectorList, matchingSelectorIndex} from './node_selector import {StylingContext, allocStylingContext, createStylingContextTemplate, renderStyling as renderElementStyles, updateClassProp as updateElementClassProp, updateStyleProp as updateElementStyleProp, updateStylingMap} from './styling'; import {assertDataInRangeInternal, getLNode, isContentQueryHost, isDifferent, loadElementInternal, loadInternal, stringify} from './util'; - /** * A permanent marker promise which signifies that the current CD tree is * clean. @@ -138,6 +137,10 @@ export function getPreviousOrParentTNode(): TNode { return previousOrParentTNode; } +export function setEnvironment(tNode: TNode, view: LViewData) { + previousOrParentTNode = tNode; + viewData = view; +} /** * If `isParent` is: diff --git a/packages/core/src/render3/jit/environment.ts b/packages/core/src/render3/jit/environment.ts index 328027eca3..2851737dcc 100644 --- a/packages/core/src/render3/jit/environment.ts +++ b/packages/core/src/render3/jit/environment.ts @@ -30,10 +30,6 @@ export const angularCoreEnv: {[name: string]: Function} = { 'ɵgetInheritedFactory': r3.getInheritedFactory, 'inject': inject, 'ɵinjectAttribute': r3.injectAttribute, - 'ɵinjectChangeDetectorRef': r3.injectChangeDetectorRef, - 'ɵinjectElementRef': r3.injectElementRef, - 'ɵinjectTemplateRef': r3.injectTemplateRef, - 'ɵinjectViewContainerRef': r3.injectViewContainerRef, 'ɵtemplateRefExtractor': r3.templateRefExtractor, 'ɵinjectRenderer2': r3.injectRenderer2, 'ɵNgOnChangesFeature': r3.NgOnChangesFeature, diff --git a/packages/core/src/render3/jit/util.ts b/packages/core/src/render3/jit/util.ts index 485ebd5dc9..7cbde9c6cd 100644 --- a/packages/core/src/render3/jit/util.ts +++ b/packages/core/src/render3/jit/util.ts @@ -42,14 +42,8 @@ function reflectDependency(dep: any | any[]): R3DependencyMetadata { }; function setTokenAndResolvedType(token: any): void { - if (token === ElementRef) { - meta.resolved = R3ResolvedDependencyType.ElementRef; - } else if (token === Injector) { + if (token === Injector) { meta.resolved = R3ResolvedDependencyType.Injector; - } else if (token === TemplateRef) { - meta.resolved = R3ResolvedDependencyType.TemplateRef; - } else if (token === ViewContainerRef) { - meta.resolved = R3ResolvedDependencyType.ViewContainerRef; } else { meta.resolved = R3ResolvedDependencyType.Token; } diff --git a/packages/core/src/render3/query.ts b/packages/core/src/render3/query.ts index 14b3a9cd32..4d5a808ebf 100644 --- a/packages/core/src/render3/query.ts +++ b/packages/core/src/render3/query.ts @@ -11,19 +11,23 @@ import {Observable} from 'rxjs'; import {EventEmitter} from '../event_emitter'; +import {ElementRef as ViewEngine_ElementRef} from '../linker/element_ref'; import {QueryList as viewEngine_QueryList} from '../linker/query_list'; +import {TemplateRef as ViewEngine_TemplateRef} from '../linker/template_ref'; +import {ViewContainerRef as ViewEngine_ViewContainerRef} from '../linker/view_container_ref'; import {Type} from '../type'; import {getSymbolIterator} from '../util'; import {assertDefined, assertEqual} from './assert'; -import {ReadFromInjectorFn, getOrCreateNodeInjectorForNode} from './di'; import {_getViewData, assertPreviousIsParent, getOrCreateCurrentQueries, store, storeCleanupWithContext} from './instructions'; import {DirectiveDefInternal, unusedValueExportToPlacateAjd as unused1} from './interfaces/definition'; import {LInjector, unusedValueExportToPlacateAjd as unused2} from './interfaces/injector'; -import {LContainerNode, LElementNode, TContainerNode, TElementContainerNode, TElementNode, TNode, TNodeFlags, unusedValueExportToPlacateAjd as unused3} from './interfaces/node'; +import {LContainerNode, LElementNode, TContainerNode, TElementContainerNode, TElementNode, TNode, TNodeFlags, TNodeType, unusedValueExportToPlacateAjd as unused3} from './interfaces/node'; import {LQueries, QueryReadType, unusedValueExportToPlacateAjd as unused4} from './interfaces/query'; import {DIRECTIVES, LViewData, TVIEW} from './interfaces/view'; +import {assertNodeOfPossibleTypes} from './node_assert'; import {flatten, getLNode, isContentQueryHost} from './util'; +import {createContainerRef, createElementRef, createTemplateRef} from './view_engine_compatibility'; const unusedValueToPlacateAjd = unused1 + unused2 + unused3 + unused4; @@ -467,3 +471,63 @@ export function queryRefresh(queryList: QueryList): boolean { } return false; } + +export class ReadFromInjectorFn { + constructor(readonly read: (tNode: TNode, view: LViewData, directiveIndex?: number) => T) {} +} + +// TODO: Remove wrapper function with TemplateRef when we turn on Ivy +// Necessary for now to avoid a circular dependency +export const QUERY_READ_TEMPLATE_REF = + (TemplateRefConstructor: typeof ViewEngine_TemplateRef, + ElementRefConstructor: typeof ViewEngine_ElementRef) => { + return new ReadFromInjectorFn>( + (tNode: TNode, view: LViewData) => { + return createTemplateRef(TemplateRefConstructor, ElementRefConstructor, tNode, view); + }) as any; + }; + +// TODO: Remove wrapper function with ViewContainerRef when we turn on Ivy +// Necessary for now to avoid a circular dependency +export const QUERY_READ_CONTAINER_REF = + (ViewContainerRefConstructor: typeof ViewEngine_ViewContainerRef, + ElementRefConstructor: typeof ViewEngine_ElementRef) => { + return >( + new ReadFromInjectorFn( + (tNode: TNode, view: LViewData) => createContainerRef( + ViewContainerRefConstructor, ElementRefConstructor, + tNode as TElementNode | TContainerNode | TElementContainerNode, view)) as any); + + }; + +// TODO: Remove wrapper function with ElementRef when we turn on Ivy +// Necessary for now to avoid a circular dependency +export const QUERY_READ_ELEMENT_REF = (ElementRefConstructor: typeof ViewEngine_ElementRef) => { + return >( + new ReadFromInjectorFn((tNode: TNode, view: LViewData) => { + return createElementRef(ElementRefConstructor, tNode, view); + }) as any); +}; + +// TODO: Remove wrapper function with TemplateRef when we turn on Ivy +export const QUERY_READ_FROM_NODE = + (TemplateRefConstructor: typeof ViewEngine_TemplateRef, + ElementRefConstructor: typeof ViewEngine_ElementRef) => { + return new ReadFromInjectorFn((tNode: TNode, view: LViewData, directiveIdx: number) => { + ngDevMode && assertNodeOfPossibleTypes( + tNode, TNodeType.Container, TNodeType.Element, TNodeType.ElementContainer); + if (directiveIdx > -1) { + return view[DIRECTIVES] ![directiveIdx]; + } + if (tNode.type === TNodeType.Element || tNode.type === TNodeType.ElementContainer) { + return createElementRef(ElementRefConstructor, tNode, view); + } + if (tNode.type === TNodeType.Container) { + return createTemplateRef(TemplateRefConstructor, ElementRefConstructor, tNode, view); + } + if (ngDevMode) { + // should never happen + throw new Error(`Unexpected node type: ${tNode.type}`); + } + }) as any as QueryReadType; + }; diff --git a/packages/core/src/render3/view_engine_compatibility.ts b/packages/core/src/render3/view_engine_compatibility.ts new file mode 100644 index 0000000000..003328c9f6 --- /dev/null +++ b/packages/core/src/render3/view_engine_compatibility.ts @@ -0,0 +1,351 @@ +/** + * @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 {ChangeDetectorRef as ViewEngine_ChangeDetectorRef} from '../change_detection/change_detector_ref'; +import {Injector, NullInjector} from '../di/injector'; +import {ComponentFactory as viewEngine_ComponentFactory, ComponentRef as viewEngine_ComponentRef} from '../linker/component_factory'; +import {ElementRef as ViewEngine_ElementRef} from '../linker/element_ref'; +import {NgModuleRef as viewEngine_NgModuleRef} from '../linker/ng_module_factory'; +import {TemplateRef as ViewEngine_TemplateRef} from '../linker/template_ref'; +import {ViewContainerRef as ViewEngine_ViewContainerRef} from '../linker/view_container_ref'; +import {EmbeddedViewRef as viewEngine_EmbeddedViewRef, ViewRef as viewEngine_ViewRef} from '../linker/view_ref'; + +import {assertDefined, assertGreaterThan, assertLessThan} from './assert'; +import {NodeInjector, getOrCreateNodeInjectorForNode} from './di'; +import {_getViewData, addToViewTree, createEmbeddedViewAndNode, createLContainer, createLNodeObject, createTNode, getPreviousOrParentTNode, getRenderer, renderEmbeddedTemplate} from './instructions'; +import {LContainer, RENDER_PARENT, VIEWS} from './interfaces/container'; +import {RenderFlags} from './interfaces/definition'; +import {LContainerNode, TContainerNode, TElementContainerNode, TElementNode, TNode, TNodeFlags, TNodeType, TViewNode} from './interfaces/node'; +import {LQueries} from './interfaces/query'; +import {RComment, RElement, Renderer3} from './interfaces/renderer'; +import {CONTEXT, HOST_NODE, LViewData, QUERIES, RENDERER, TView} from './interfaces/view'; +import {assertNodeOfPossibleTypes, assertNodeType} from './node_assert'; +import {addRemoveViewFromContainer, appendChild, detachView, findComponentView, getBeforeNodeForView, getParentLNode, getRenderParent, insertView, removeView} from './node_manipulation'; +import {getLNode, isComponent} from './util'; +import {ViewRef} from './view_ref'; + + + +/** + * Creates an ElementRef from the most recent node. + * + * @returns The ElementRef instance to use + */ +export function injectElementRef(ElementRefToken: typeof ViewEngine_ElementRef): + ViewEngine_ElementRef { + return createElementRef(ElementRefToken, getPreviousOrParentTNode(), _getViewData()); +} + +let R3ElementRef: {new (native: RElement | RComment): ViewEngine_ElementRef}; + +/** + * Creates an ElementRef given a node. + * + * @param ElementRefToken The ElementRef type + * @param tNode The node for which you'd like an ElementRef + * @param view The view to which the node belongs + * @returns The ElementRef instance to use + */ +export function createElementRef( + ElementRefToken: typeof ViewEngine_ElementRef, tNode: TNode, + view: LViewData): ViewEngine_ElementRef { + if (!R3ElementRef) { + // TODO: Fix class name, should be ElementRef, but there appears to be a rollup bug + R3ElementRef = class ElementRef_ extends ElementRefToken {}; + } + return new R3ElementRef(getLNode(tNode, view).native); +} + +let R3TemplateRef: { + new ( + _declarationParentView: LViewData, elementRef: ViewEngine_ElementRef, _tView: TView, + _renderer: Renderer3, _queries: LQueries | null): ViewEngine_TemplateRef +}; + +/** + * Creates a TemplateRef given a node. + * + * @returns The TemplateRef instance to use + */ +export function injectTemplateRef( + TemplateRefToken: typeof ViewEngine_TemplateRef, + ElementRefToken: typeof ViewEngine_ElementRef): ViewEngine_TemplateRef { + return createTemplateRef( + TemplateRefToken, ElementRefToken, getPreviousOrParentTNode(), _getViewData()); +} + +/** + * Creates a TemplateRef and stores it on the injector. + * + * @param TemplateRefToken The TemplateRef type + * @param ElementRefToken The ElementRef type + * @param hostTNode The node that is requesting a TemplateRef + * @param hostView The view to which the node belongs + * @returns The TemplateRef instance to use + */ +export function createTemplateRef( + TemplateRefToken: typeof ViewEngine_TemplateRef, ElementRefToken: typeof ViewEngine_ElementRef, + hostTNode: TNode, hostView: LViewData): ViewEngine_TemplateRef { + if (!R3TemplateRef) { + // TODO: Fix class name, should be TemplateRef, but there appears to be a rollup bug + R3TemplateRef = class TemplateRef_ extends TemplateRefToken { + constructor( + private _declarationParentView: LViewData, readonly elementRef: ViewEngine_ElementRef, + private _tView: TView, private _renderer: Renderer3, private _queries: LQueries|null) { + super(); + } + + createEmbeddedView( + context: T, container?: LContainer, tContainerNode?: TContainerNode, hostView?: LViewData, + index?: number): viewEngine_EmbeddedViewRef { + const lView = createEmbeddedViewAndNode( + this._tView, context, this._declarationParentView, this._renderer, this._queries); + if (container) { + insertView(lView, container, hostView !, index !, tContainerNode !.parent !.index); + } + renderEmbeddedTemplate(lView, this._tView, context, RenderFlags.Create); + const viewRef = new ViewRef(lView, context, -1); + viewRef._tViewNode = lView[HOST_NODE] as TViewNode; + return viewRef; + } + }; + } + + + const hostNode = getLNode(hostTNode, hostView); + ngDevMode && assertNodeType(hostTNode, TNodeType.Container); + ngDevMode && assertDefined(hostTNode.tViews, 'TView must be allocated'); + return new R3TemplateRef( + hostView, createElementRef(ElementRefToken, hostTNode, hostView), hostTNode.tViews as TView, + getRenderer(), hostNode.data ![QUERIES]); +} + +/** + * Retrieves `TemplateRef` instance from `Injector` when a local reference is placed on the + * `` element. + */ +export function templateRefExtractor( + TemplateRefToken: typeof ViewEngine_TemplateRef, + ElementRefToken: typeof ViewEngine_ElementRef) { + return (tNode: TNode, currentView: LViewData) => { + return createTemplateRef(TemplateRefToken, ElementRefToken, tNode, currentView); + }; +} + + +let R3ViewContainerRef: { + new ( + lContainer: LContainer, tContainerNode: TContainerNode, + hostTNode: TElementNode | TContainerNode | TElementContainerNode, hostView: LViewData): + ViewEngine_ViewContainerRef +}; + +/** + * Creates a ViewContainerRef and stores it on the injector. Or, if the ViewContainerRef + * already exists, retrieves the existing ViewContainerRef. + * + * @returns The ViewContainerRef instance to use + */ +export function injectViewContainerRef( + ViewContainerRefToken: typeof ViewEngine_ViewContainerRef, + ElementRefToken: typeof ViewEngine_ElementRef): ViewEngine_ViewContainerRef { + const previousTNode = + getPreviousOrParentTNode() as TElementNode | TElementContainerNode | TContainerNode; + return createContainerRef(ViewContainerRefToken, ElementRefToken, previousTNode, _getViewData()); +} + + +/** + * Creates a ViewContainerRef and stores it on the injector. + * + * @param ViewContainerRefToken The ViewContainerRef type + * @param ElementRefToken The ElementRef type + * @param hostTNode The node that is requesting a ViewContainerRef + * @param hostView The view to which the node belongs + * @returns The ViewContainerRef instance to use + */ +export function createContainerRef( + ViewContainerRefToken: typeof ViewEngine_ViewContainerRef, + ElementRefToken: typeof ViewEngine_ElementRef, + hostTNode: TElementNode|TContainerNode|TElementContainerNode, + hostView: LViewData): ViewEngine_ViewContainerRef { + if (!R3ViewContainerRef) { + // TODO: Fix class name, should be ViewContainerRef, but there appears to be a rollup bug + R3ViewContainerRef = class ViewContainerRef_ extends ViewContainerRefToken { + private _viewRefs: viewEngine_ViewRef[] = []; + + constructor( + private _lContainer: LContainer, private _tContainerNode: TContainerNode, + private _hostTNode: TElementNode|TContainerNode|TElementContainerNode, + private _hostView: LViewData) { + super(); + } + + get element(): ViewEngine_ElementRef { + return createElementRef(ElementRefToken, this._hostTNode, this._hostView); + } + + get injector(): Injector { + // TODO: Remove LNode lookup when removing LNode.nodeInjector + const injector = + getOrCreateNodeInjectorForNode(this._getHostNode(), this._hostTNode, this._hostView); + return new NodeInjector(injector); + } + + /** @deprecated No replacement */ + get parentInjector(): Injector { + const parentLInjector = getParentLNode(this._hostTNode, this._hostView) !.nodeInjector; + return parentLInjector ? new NodeInjector(parentLInjector) : new NullInjector(); + } + + clear(): void { + while (this._lContainer[VIEWS].length) { + this.remove(0); + } + } + + get(index: number): viewEngine_ViewRef|null { return this._viewRefs[index] || null; } + + get length(): number { return this._lContainer[VIEWS].length; } + + createEmbeddedView(templateRef: ViewEngine_TemplateRef, context?: C, index?: number): + viewEngine_EmbeddedViewRef { + const adjustedIdx = this._adjustIndex(index); + const viewRef = (templateRef as any) + .createEmbeddedView( + context || {}, this._lContainer, this._tContainerNode, + this._hostView, adjustedIdx); + (viewRef as ViewRef).attachToViewContainerRef(this); + this._viewRefs.splice(adjustedIdx, 0, viewRef); + return viewRef; + } + + createComponent( + componentFactory: viewEngine_ComponentFactory, index?: number|undefined, + injector?: Injector|undefined, projectableNodes?: any[][]|undefined, + ngModuleRef?: viewEngine_NgModuleRef|undefined): viewEngine_ComponentRef { + const contextInjector = injector || this.parentInjector; + if (!ngModuleRef && contextInjector) { + ngModuleRef = contextInjector.get(viewEngine_NgModuleRef, null); + } + + const componentRef = + componentFactory.create(contextInjector, projectableNodes, undefined, ngModuleRef); + this.insert(componentRef.hostView, index); + return componentRef; + } + + insert(viewRef: viewEngine_ViewRef, index?: number): viewEngine_ViewRef { + if (viewRef.destroyed) { + throw new Error('Cannot insert a destroyed View in a ViewContainer!'); + } + const lView = (viewRef as ViewRef)._view !; + const adjustedIdx = this._adjustIndex(index); + + insertView( + lView, this._lContainer, this._hostView, adjustedIdx, + this._tContainerNode.parent !.index); + + const container = this._getHostNode().dynamicLContainerNode !; + const beforeNode = getBeforeNodeForView(adjustedIdx, this._lContainer[VIEWS], container); + addRemoveViewFromContainer(lView, true, beforeNode); + + (viewRef as ViewRef).attachToViewContainerRef(this); + this._viewRefs.splice(adjustedIdx, 0, viewRef); + + return viewRef; + } + + move(viewRef: viewEngine_ViewRef, newIndex: number): viewEngine_ViewRef { + const index = this.indexOf(viewRef); + this.detach(index); + this.insert(viewRef, this._adjustIndex(newIndex)); + return viewRef; + } + + indexOf(viewRef: viewEngine_ViewRef): number { return this._viewRefs.indexOf(viewRef); } + + remove(index?: number): void { + const adjustedIdx = this._adjustIndex(index, -1); + removeView(this._lContainer, this._tContainerNode as TContainerNode, adjustedIdx); + this._viewRefs.splice(adjustedIdx, 1); + } + + detach(index?: number): viewEngine_ViewRef|null { + const adjustedIdx = this._adjustIndex(index, -1); + detachView(this._lContainer, adjustedIdx, !!this._tContainerNode.detached); + return this._viewRefs.splice(adjustedIdx, 1)[0] || null; + } + + private _adjustIndex(index?: number, shift: number = 0) { + if (index == null) { + return this._lContainer[VIEWS].length + shift; + } + if (ngDevMode) { + assertGreaterThan(index, -1, 'index must be positive'); + // +1 because it's legal to insert at the end. + assertLessThan(index, this._lContainer[VIEWS].length + 1 + shift, 'index'); + } + return index; + } + + private _getHostNode() { return getLNode(this._hostTNode, this._hostView); } + }; + } + + const hostLNode = getLNode(hostTNode, hostView); + ngDevMode && assertNodeOfPossibleTypes( + hostTNode, TNodeType.Container, TNodeType.Element, TNodeType.ElementContainer); + + const lContainer = createLContainer(hostView, true); + const comment = hostView[RENDERER].createComment(ngDevMode ? 'container' : ''); + const lContainerNode: LContainerNode = + createLNodeObject(TNodeType.Container, hostLNode.nodeInjector, comment, lContainer); + + lContainer[RENDER_PARENT] = getRenderParent(hostTNode, hostView); + + appendChild(comment, hostTNode, hostView); + + if (!hostTNode.dynamicContainerNode) { + hostTNode.dynamicContainerNode = + createTNode(TNodeType.Container, -1, null, null, hostTNode, null); + } + + hostLNode.dynamicLContainerNode = lContainerNode; + addToViewTree(hostView, hostTNode.index as number, lContainer); + + return new R3ViewContainerRef( + lContainer, hostTNode.dynamicContainerNode as TContainerNode, hostTNode, hostView); +} + + +/** Returns a ChangeDetectorRef (a.k.a. a ViewRef) */ +export function injectChangeDetectorRef(): ViewEngine_ChangeDetectorRef { + return createViewRef(getPreviousOrParentTNode(), _getViewData(), null); +} + +/** + * Creates a ViewRef and stores it on the injector as ChangeDetectorRef (public alias). + * + * @param hostTNode The node that is requesting a ChangeDetectorRef + * @param hostView The view to which the node belongs + * @param context The context for this change detector ref + * @returns The ChangeDetectorRef to use + */ +export function createViewRef( + hostTNode: TNode, hostView: LViewData, context: any): ViewEngine_ChangeDetectorRef { + if (isComponent(hostTNode)) { + const componentIndex = hostTNode.flags >> TNodeFlags.DirectiveStartingIndexShift; + const componentView = getLNode(hostTNode, hostView).data as LViewData; + return new ViewRef(componentView, context, componentIndex); + } else if (hostTNode.type === TNodeType.Element) { + const hostComponentView = findComponentView(hostView); + return new ViewRef(hostComponentView, hostComponentView[CONTEXT], -1); + } + return null !; +} diff --git a/packages/core/test/bundling/animation_world/bundle.golden_symbols.json b/packages/core/test/bundling/animation_world/bundle.golden_symbols.json index 5f542c7f2a..7789c4db0c 100644 --- a/packages/core/test/bundling/animation_world/bundle.golden_symbols.json +++ b/packages/core/test/bundling/animation_world/bundle.golden_symbols.json @@ -32,9 +32,6 @@ { "name": "ChangeDetectionStrategy" }, - { - "name": "ChangeDetectorRef" - }, { "name": "CorePlayerHandler" }, @@ -65,9 +62,6 @@ { "name": "ElementRef" }, - { - "name": "ElementRef$1" - }, { "name": "FLAGS" }, @@ -146,6 +140,15 @@ { "name": "QUERIES" }, + { + "name": "R3_ELEMENT_REF_FACTORY" + }, + { + "name": "R3_TEMPLATE_REF_FACTORY" + }, + { + "name": "R3_VIEW_CONTAINER_REF_FACTORY" + }, { "name": "RENDERER" }, @@ -179,18 +182,12 @@ { "name": "TemplateRef" }, - { - "name": "TemplateRef$1" - }, { "name": "VIEWS" }, { "name": "ViewContainerRef" }, - { - "name": "ViewContainerRef$1" - }, { "name": "ViewEncapsulation$1" }, @@ -300,7 +297,7 @@ "name": "bloomFindPossibleInjector" }, { - "name": "bloomHashBit" + "name": "bloomHashBitOrFactory" }, { "name": "buildAnimationPlayer" @@ -401,9 +398,6 @@ { "name": "createViewQuery" }, - { - "name": "createViewRef" - }, { "name": "defineComponent" }, @@ -719,9 +713,6 @@ { "name": "isClassBased" }, - { - "name": "isComponent" - }, { "name": "isComponentInstance" }, @@ -926,6 +917,9 @@ { "name": "setDirty" }, + { + "name": "setEnvironment" + }, { "name": "setFlag" }, diff --git a/packages/core/test/bundling/hello_world_r2/bundle.golden_symbols.json b/packages/core/test/bundling/hello_world_r2/bundle.golden_symbols.json index 536af7008d..7522f0dad5 100644 --- a/packages/core/test/bundling/hello_world_r2/bundle.golden_symbols.json +++ b/packages/core/test/bundling/hello_world_r2/bundle.golden_symbols.json @@ -1238,9 +1238,21 @@ { "name": "Quote" }, + { + "name": "R3_CHANGE_DETECTOR_REF_FACTORY$1" + }, { "name": "R3_COMPILE_INJECTABLE$1" }, + { + "name": "R3_ELEMENT_REF_FACTORY$1" + }, + { + "name": "R3_TEMPLATE_REF_FACTORY$1" + }, + { + "name": "R3_VIEW_CONTAINER_REF_FACTORY$1" + }, { "name": "ReadKeyExpr" }, @@ -3389,6 +3401,9 @@ { "name": "noop$3" }, + { + "name": "noopFactory" + }, { "name": "noopScope" }, diff --git a/packages/core/test/bundling/todo/bundle.golden_symbols.json b/packages/core/test/bundling/todo/bundle.golden_symbols.json index a634c912b7..c4bc4adc9e 100644 --- a/packages/core/test/bundling/todo/bundle.golden_symbols.json +++ b/packages/core/test/bundling/todo/bundle.golden_symbols.json @@ -26,9 +26,6 @@ { "name": "ChangeDetectionStrategy" }, - { - "name": "ChangeDetectorRef" - }, { "name": "DECLARATION_VIEW" }, @@ -50,9 +47,6 @@ { "name": "ElementRef" }, - { - "name": "ElementRef$1" - }, { "name": "FLAGS" }, @@ -137,6 +131,15 @@ { "name": "QUERIES" }, + { + "name": "R3_ELEMENT_REF_FACTORY" + }, + { + "name": "R3_TEMPLATE_REF_FACTORY" + }, + { + "name": "R3_VIEW_CONTAINER_REF_FACTORY" + }, { "name": "RENDERER" }, @@ -167,9 +170,6 @@ { "name": "TemplateRef" }, - { - "name": "TemplateRef$1" - }, { "name": "ToDoAppComponent_footer_Template_6" }, @@ -200,9 +200,6 @@ { "name": "ViewContainerRef" }, - { - "name": "ViewContainerRef$1" - }, { "name": "ViewEncapsulation$1" }, @@ -366,7 +363,7 @@ "name": "bloomFindPossibleInjector" }, { - "name": "bloomHashBit" + "name": "bloomHashBitOrFactory" }, { "name": "cacheMatchingDirectivesForNode" @@ -464,9 +461,6 @@ { "name": "createViewQuery" }, - { - "name": "createViewRef" - }, { "name": "defineComponent" }, @@ -722,6 +716,9 @@ { "name": "inject" }, + { + "name": "injectElementRef" + }, { "name": "injectTemplateRef" }, @@ -740,9 +737,6 @@ { "name": "invertObject" }, - { - "name": "isComponent" - }, { "name": "isContentQueryHost" }, @@ -944,6 +938,9 @@ { "name": "setDirty" }, + { + "name": "setEnvironment" + }, { "name": "setFlag" }, diff --git a/packages/core/test/bundling/todo_r2/bundle.golden_symbols.json b/packages/core/test/bundling/todo_r2/bundle.golden_symbols.json index 165106c70d..c3b5d0ef79 100644 --- a/packages/core/test/bundling/todo_r2/bundle.golden_symbols.json +++ b/packages/core/test/bundling/todo_r2/bundle.golden_symbols.json @@ -281,9 +281,6 @@ { "name": "ElementRef" }, - { - "name": "ElementRef$1" - }, { "name": "EmulatedEncapsulationDomRenderer2" }, @@ -662,6 +659,18 @@ { "name": "R3Injector" }, + { + "name": "R3_CHANGE_DETECTOR_REF_FACTORY" + }, + { + "name": "R3_ELEMENT_REF_FACTORY" + }, + { + "name": "R3_TEMPLATE_REF_FACTORY" + }, + { + "name": "R3_VIEW_CONTAINER_REF_FACTORY" + }, { "name": "REMOVE_EVENT_LISTENER" }, @@ -809,9 +818,6 @@ { "name": "TemplateRef" }, - { - "name": "TemplateRef$1" - }, { "name": "Testability" }, @@ -890,9 +896,6 @@ { "name": "ViewContainerRef" }, - { - "name": "ViewContainerRef$1" - }, { "name": "ViewEncapsulation$1" }, @@ -1200,7 +1203,7 @@ "name": "bloomFindPossibleInjector" }, { - "name": "bloomHashBit" + "name": "bloomHashBitOrFactory" }, { "name": "cacheMatchingDirectivesForNode" @@ -2288,6 +2291,9 @@ { "name": "setDirty" }, + { + "name": "setEnvironment" + }, { "name": "setFlag" }, diff --git a/packages/core/test/render3/animations/players_spec.ts b/packages/core/test/render3/animations/players_spec.ts index 4d59be1b6a..a5c21d75d1 100644 --- a/packages/core/test/render3/animations/players_spec.ts +++ b/packages/core/test/render3/animations/players_spec.ts @@ -5,7 +5,9 @@ * 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 {ElementRef, TemplateRef} from '@angular/core'; import {RenderFlags} from '@angular/core/src/render3'; + import {AnimationContext, PlayState, Player, PlayerHandler} from '../../../src/render3/animations/interfaces'; import {addPlayer, getOrCreateAnimationContext, getPlayers} from '../../../src/render3/animations/players'; import {QUERY_READ_FROM_NODE, defineComponent, getHostElement} from '../../../src/render3/index'; @@ -13,6 +15,7 @@ import {element, elementEnd, elementStart, elementStyling, elementStylingApply, import {RElement} from '../../../src/render3/interfaces/renderer'; import {QueryList, query, queryRefresh} from '../../../src/render3/query'; import {ComponentFixture} from '../render_util'; + import {MockPlayer} from './mock_player'; describe('animation player access', () => { @@ -286,7 +289,7 @@ class SuperComp { }, viewQuery: function(rf: RenderFlags, ctx: SuperComp) { if (rf & RenderFlags.Create) { - query(0, ['child'], true, QUERY_READ_FROM_NODE); + query(0, ['child'], true, QUERY_READ_FROM_NODE(TemplateRef, ElementRef)); } if (rf & RenderFlags.Update) { let tmp: any; diff --git a/packages/core/test/render3/change_detection_spec.ts b/packages/core/test/render3/change_detection_spec.ts index 44ab58f0ee..368310e8c1 100644 --- a/packages/core/test/render3/change_detection_spec.ts +++ b/packages/core/test/render3/change_detection_spec.ts @@ -10,7 +10,8 @@ import {withBody} from '@angular/private/testing'; import {ChangeDetectionStrategy, ChangeDetectorRef, DoCheck, RendererType2} from '../../src/core'; import {getRenderedText, whenRendered} from '../../src/render3/component'; -import {LifecycleHooksFeature, defineComponent, defineDirective, injectChangeDetectorRef} from '../../src/render3/index'; +import {directiveInject} from '../../src/render3/di'; +import {LifecycleHooksFeature, defineComponent, defineDirective} from '../../src/render3/index'; import {bind, container, containerRefreshEnd, containerRefreshStart, detectChanges, element, elementEnd, elementProperty, elementStart, embeddedViewEnd, embeddedViewStart, interpolation1, interpolation2, listener, markDirty, text, textBinding, tick} from '../../src/render3/instructions'; import {RenderFlags} from '../../src/render3/interfaces/definition'; import {RElement, Renderer3, RendererFactory3} from '../../src/render3/interfaces/renderer'; @@ -18,7 +19,6 @@ import {RElement, Renderer3, RendererFactory3} from '../../src/render3/interface import {containerEl, createComponent, renderComponent, requestAnimationFrame} from './render_util'; describe('change detection', () => { - describe('markDirty, detectChanges, whenRendered, getRenderedText', () => { class MyComponent implements DoCheck { value: string = 'works'; @@ -326,7 +326,7 @@ describe('change detection', () => { static ngComponentDef = defineComponent({ type: MyComp, selectors: [['my-comp']], - factory: () => myComp = new MyComp(injectChangeDetectorRef()), + factory: () => myComp = new MyComp(directiveInject(ChangeDetectorRef as any)), consts: 1, vars: 1, /** {{ name }} */ @@ -352,7 +352,7 @@ describe('change detection', () => { static ngComponentDef = defineComponent({ type: ParentComp, selectors: [['parent-comp']], - factory: () => new ParentComp(injectChangeDetectorRef()), + factory: () => new ParentComp(directiveInject(ChangeDetectorRef as any)), consts: 2, vars: 1, /** @@ -378,7 +378,7 @@ describe('change detection', () => { static ngDirectiveDef = defineDirective({ type: Dir, selectors: [['', 'dir', '']], - factory: () => dir = new Dir(injectChangeDetectorRef()) + factory: () => dir = new Dir(directiveInject(ChangeDetectorRef as any)) }); } @@ -487,7 +487,7 @@ describe('change detection', () => { static ngComponentDef = defineComponent({ type: MyApp, selectors: [['my-app']], - factory: () => new MyApp(injectChangeDetectorRef()), + factory: () => new MyApp(directiveInject(ChangeDetectorRef as any)), consts: 2, vars: 1, /** @@ -542,7 +542,7 @@ describe('change detection', () => { static ngComponentDef = defineComponent({ type: DetectChangesComp, selectors: [['detect-changes-comp']], - factory: () => new DetectChangesComp(injectChangeDetectorRef()), + factory: () => new DetectChangesComp(directiveInject(ChangeDetectorRef as any)), consts: 1, vars: 1, /** {{ value }} */ @@ -575,7 +575,7 @@ describe('change detection', () => { static ngComponentDef = defineComponent({ type: DetectChangesComp, selectors: [['detect-changes-comp']], - factory: () => new DetectChangesComp(injectChangeDetectorRef()), + factory: () => new DetectChangesComp(directiveInject(ChangeDetectorRef as any)), consts: 1, vars: 1, /** {{ doCheckCount }} */ @@ -605,7 +605,7 @@ describe('change detection', () => { static ngComponentDef = defineComponent({ type: MyApp, selectors: [['my-app']], - factory: () => new MyApp(injectChangeDetectorRef()), + factory: () => new MyApp(directiveInject(ChangeDetectorRef as any)), consts: 1, vars: 0, /** */ @@ -629,7 +629,7 @@ describe('change detection', () => { static ngComponentDef = defineComponent({ type: DetachedComp, selectors: [['detached-comp']], - factory: () => comp = new DetachedComp(injectChangeDetectorRef()), + factory: () => comp = new DetachedComp(directiveInject(ChangeDetectorRef as any)), consts: 1, vars: 1, /** {{ value }} */ @@ -730,7 +730,7 @@ describe('change detection', () => { static ngComponentDef = defineComponent({ type: OnPushComp, selectors: [['on-push-comp']], - factory: () => onPushComp = new OnPushComp(injectChangeDetectorRef()), + factory: () => onPushComp = new OnPushComp(directiveInject(ChangeDetectorRef as any)), consts: 1, vars: 1, /** {{ value }} */ @@ -791,7 +791,7 @@ describe('change detection', () => { static ngComponentDef = defineComponent({ type: OnPushComp, selectors: [['on-push-comp']], - factory: () => comp = new OnPushComp(injectChangeDetectorRef()), + factory: () => comp = new OnPushComp(directiveInject(ChangeDetectorRef as any)), consts: 1, vars: 1, /** {{ value }} */ @@ -960,7 +960,7 @@ describe('change detection', () => { static ngComponentDef = defineComponent({ type: NoChangesComp, selectors: [['no-changes-comp']], - factory: () => comp = new NoChangesComp(injectChangeDetectorRef()), + factory: () => comp = new NoChangesComp(directiveInject(ChangeDetectorRef as any)), consts: 1, vars: 1, template: (rf: RenderFlags, ctx: NoChangesComp) => { @@ -982,7 +982,7 @@ describe('change detection', () => { static ngComponentDef = defineComponent({ type: AppComp, selectors: [['app-comp']], - factory: () => new AppComp(injectChangeDetectorRef()), + factory: () => new AppComp(directiveInject(ChangeDetectorRef as any)), consts: 2, vars: 1, /** @@ -1045,7 +1045,7 @@ describe('change detection', () => { static ngComponentDef = defineComponent({ type: EmbeddedViewApp, selectors: [['embedded-view-app']], - factory: () => new EmbeddedViewApp(injectChangeDetectorRef()), + factory: () => new EmbeddedViewApp(directiveInject(ChangeDetectorRef as any)), consts: 1, vars: 0, /** diff --git a/packages/core/test/render3/common_integration_spec.ts b/packages/core/test/render3/common_integration_spec.ts index 10bdf36cac..d91d747477 100644 --- a/packages/core/test/render3/common_integration_spec.ts +++ b/packages/core/test/render3/common_integration_spec.ts @@ -7,6 +7,7 @@ */ import {NgForOfContext} from '@angular/common'; +import {ElementRef, TemplateRef} from '@angular/core'; import {AttributeMarker, defineComponent, templateRefExtractor} from '../../src/render3/index'; import {bind, template, elementEnd, elementProperty, elementStart, getCurrentView, interpolation1, interpolation2, interpolation3, interpolationV, listener, load, nextContext, restoreView, text, textBinding, elementContainerStart, elementContainerEnd, reference} from '../../src/render3/instructions'; @@ -896,11 +897,15 @@ describe('@angular/common integration', () => { */ template: (rf: RenderFlags, myApp: MyApp) => { if (rf & RenderFlags.Create) { - template(0, (rf1: RenderFlags) => { - if (rf1 & RenderFlags.Create) { - text(0, 'from tpl'); - } - }, 1, 0, undefined, undefined, ['tpl', ''], templateRefExtractor); + template( + 0, + (rf1: RenderFlags) => { + if (rf1 & RenderFlags.Create) { + text(0, 'from tpl'); + } + }, + 1, 0, undefined, undefined, ['tpl', ''], + templateRefExtractor(TemplateRef, ElementRef)); template(2, null, 0, 0, null, [AttributeMarker.SelectOnly, 'ngTemplateOutlet']); } if (rf & RenderFlags.Update) { @@ -939,11 +944,15 @@ describe('@angular/common integration', () => { */ template: (rf: RenderFlags, myApp: MyApp) => { if (rf & RenderFlags.Create) { - template(0, (rf1: RenderFlags) => { - if (rf1 & RenderFlags.Create) { - text(0, 'from tpl'); - } - }, 1, 0, undefined, undefined, ['tpl', ''], templateRefExtractor); + template( + 0, + (rf1: RenderFlags) => { + if (rf1 & RenderFlags.Create) { + text(0, 'from tpl'); + } + }, + 1, 0, undefined, undefined, ['tpl', ''], + templateRefExtractor(TemplateRef, ElementRef)); elementContainerStart(2, [AttributeMarker.SelectOnly, 'ngTemplateOutlet']); elementContainerEnd(); } diff --git a/packages/core/test/render3/common_with_def.ts b/packages/core/test/render3/common_with_def.ts index b030cd9ddf..73cbda6bf8 100644 --- a/packages/core/test/render3/common_with_def.ts +++ b/packages/core/test/render3/common_with_def.ts @@ -7,9 +7,9 @@ */ import {NgForOf as NgForOfDef, NgIf as NgIfDef, NgTemplateOutlet as NgTemplateOutletDef} from '@angular/common'; -import {IterableDiffers} from '@angular/core'; +import {IterableDiffers, TemplateRef, ViewContainerRef} from '@angular/core'; -import {DirectiveType, NgOnChangesFeature, defineDirective, directiveInject, injectTemplateRef, injectViewContainerRef} from '../../src/render3/index'; +import {DirectiveType, NgOnChangesFeature, defineDirective, directiveInject} from '../../src/render3/index'; export const NgForOf: DirectiveType> = NgForOfDef as any; export const NgIf: DirectiveType = NgIfDef as any; @@ -19,7 +19,8 @@ NgForOf.ngDirectiveDef = defineDirective({ type: NgForOfDef, selectors: [['', 'ngForOf', '']], factory: () => new NgForOfDef( - injectViewContainerRef(), injectTemplateRef(), directiveInject(IterableDiffers)), + directiveInject(ViewContainerRef as any), directiveInject(TemplateRef as any), + directiveInject(IterableDiffers)), inputs: { ngForOf: 'ngForOf', ngForTrackBy: 'ngForTrackBy', @@ -30,14 +31,15 @@ NgForOf.ngDirectiveDef = defineDirective({ (NgIf as any).ngDirectiveDef = defineDirective({ type: NgIfDef, selectors: [['', 'ngIf', '']], - factory: () => new NgIfDef(injectViewContainerRef(), injectTemplateRef()), + factory: () => new NgIfDef( + directiveInject(ViewContainerRef as any), directiveInject(TemplateRef as any)), inputs: {ngIf: 'ngIf', ngIfThen: 'ngIfThen', ngIfElse: 'ngIfElse'} }); (NgTemplateOutlet as any).ngDirectiveDef = defineDirective({ type: NgTemplateOutletDef, selectors: [['', 'ngTemplateOutlet', '']], - factory: () => new NgTemplateOutletDef(injectViewContainerRef()), + factory: () => new NgTemplateOutletDef(directiveInject(ViewContainerRef as any)), features: [NgOnChangesFeature], inputs: {ngTemplateOutlet: 'ngTemplateOutlet', ngTemplateOutletContext: 'ngTemplateOutletContext'} diff --git a/packages/core/test/render3/compiler_canonical/component_directives_spec.ts b/packages/core/test/render3/compiler_canonical/component_directives_spec.ts index 680a78b988..7d0dfdac7a 100644 --- a/packages/core/test/render3/compiler_canonical/component_directives_spec.ts +++ b/packages/core/test/render3/compiler_canonical/component_directives_spec.ts @@ -394,7 +394,7 @@ describe('components & directives', () => { static ngDirectiveDef = $r3$.ɵdefineDirective({ type: IfDirective, selectors: [['', 'if', '']], - factory: () => new IfDirective($r3$.ɵinjectTemplateRef()), + factory: () => new IfDirective($r3$.ɵdirectiveInject(TemplateRef as any)), }); // /NORMATIVE } diff --git a/packages/core/test/render3/compiler_canonical/injection_spec.ts b/packages/core/test/render3/compiler_canonical/injection_spec.ts index 706b39d290..a67aec795c 100644 --- a/packages/core/test/render3/compiler_canonical/injection_spec.ts +++ b/packages/core/test/render3/compiler_canonical/injection_spec.ts @@ -6,7 +6,7 @@ * found in the LICENSE file at https://angular.io/license */ -import {Attribute, ChangeDetectionStrategy, ChangeDetectorRef, Component, ContentChild, ContentChildren, Directive, HostBinding, HostListener, INJECTOR, Inject, InjectFlags, Injectable, Injector, Input, NgModule, OnDestroy, Optional, Pipe, PipeTransform, QueryList, SimpleChanges, SkipSelf, TemplateRef, ViewChild, ViewChildren, ViewContainerRef, defineInjectable, defineInjector, inject} from '../../../src/core'; +import {Attribute, ChangeDetectorRef, Component, INJECTOR, Inject, InjectFlags, Injectable, Injector, SkipSelf, defineInjectable, inject} from '../../../src/core'; import * as $r3$ from '../../../src/core_render3_private_export'; import {renderComponent, toHtml} from '../render_util'; @@ -32,7 +32,7 @@ describe('injection', () => { type: MyComp, selectors: [['my-comp']], factory: function MyComp_Factory() { - return new MyComp($r3$.ɵinjectChangeDetectorRef()); + return new MyComp($r3$.ɵdirectiveInject(ChangeDetectorRef as any)); }, consts: 1, vars: 1, diff --git a/packages/core/test/render3/compiler_canonical/local_reference_spec.ts b/packages/core/test/render3/compiler_canonical/local_reference_spec.ts index 190b506f67..ea9f58dff6 100644 --- a/packages/core/test/render3/compiler_canonical/local_reference_spec.ts +++ b/packages/core/test/render3/compiler_canonical/local_reference_spec.ts @@ -6,10 +6,13 @@ * found in the LICENSE file at https://angular.io/license */ +import {ElementRef, TemplateRef} from '@angular/core'; + import {Component} from '../../../src/core'; import * as $r3$ from '../../../src/core_render3_private_export'; import {ComponentFixture} from '../render_util'; + /// See: `normative.md` describe('local references', () => { type $RenderFlags$ = $r3$.ɵRenderFlags; @@ -69,7 +72,7 @@ describe('local references', () => { if (rf & 1) { $r3$.ɵtemplate( 0, MyComponent_Template_0, 0, 0, null, null, ['tpl', ''], - $r3$.ɵtemplateRefExtractor); + $r3$.ɵtemplateRefExtractor(TemplateRef, ElementRef)); $r3$.ɵtext(2); } if (rf & 2) { diff --git a/packages/core/test/render3/compiler_canonical/pipes_spec.ts b/packages/core/test/render3/compiler_canonical/pipes_spec.ts index da77065c10..8819a564c3 100644 --- a/packages/core/test/render3/compiler_canonical/pipes_spec.ts +++ b/packages/core/test/render3/compiler_canonical/pipes_spec.ts @@ -6,12 +6,13 @@ * found in the LICENSE file at https://angular.io/license */ -import {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 {Component, Directive, Input, OnDestroy, Pipe, PipeTransform, TemplateRef, ViewContainerRef} from '../../../src/core'; import * as $r3$ from '../../../src/core_render3_private_export'; import {ComponentDefInternal} from '../../../src/render3/interfaces/definition'; import {containerEl, renderComponent, toHtml} from '../render_util'; + /// See: `normative.md` describe('pipes', () => { type $any$ = any; @@ -145,7 +146,9 @@ describe('pipes', () => { static ngDirectiveDef = $r3$.ɵdefineDirective({ type: OneTimeIf, selectors: [['', 'oneTimeIf', '']], - factory: () => new OneTimeIf($r3$.ɵinjectViewContainerRef(), $r3$.ɵinjectTemplateRef()), + factory: () => new OneTimeIf( + $r3$.ɵdirectiveInject(ViewContainerRef as any), + $r3$.ɵdirectiveInject(TemplateRef as any)), inputs: {oneTimeIf: 'oneTimeIf'} }); // /NORMATIVE diff --git a/packages/core/test/render3/compiler_canonical/template_variables_spec.ts b/packages/core/test/render3/compiler_canonical/template_variables_spec.ts index 294a59caaa..d32d03344f 100644 --- a/packages/core/test/render3/compiler_canonical/template_variables_spec.ts +++ b/packages/core/test/render3/compiler_canonical/template_variables_spec.ts @@ -6,12 +6,13 @@ * found in the LICENSE file at https://angular.io/license */ -import {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 {Component, Directive, Input, SimpleChanges, TemplateRef, ViewContainerRef, inject} from '../../../src/core'; import * as $r3$ from '../../../src/core_render3_private_export'; import {ComponentDefInternal} from '../../../src/render3/interfaces/definition'; import {renderComponent, toHtml} from '../render_util'; + /// See: `normative.md` describe('template variables', () => { type $any$ = any; @@ -69,7 +70,9 @@ describe('template variables', () => { type: ForOfDirective, selectors: [['', 'forOf', '']], factory: function ForOfDirective_Factory() { - return new ForOfDirective($r3$.ɵinjectViewContainerRef(), $r3$.ɵinjectTemplateRef()); + return new ForOfDirective( + $r3$.ɵdirectiveInject(ViewContainerRef as any), + $r3$.ɵdirectiveInject(TemplateRef as any)); }, // TODO(chuckj): Enable when ngForOf enabling lands. // features: [NgOnChangesFeature], diff --git a/packages/core/test/render3/content_spec.ts b/packages/core/test/render3/content_spec.ts index 4507348643..fe162f833e 100644 --- a/packages/core/test/render3/content_spec.ts +++ b/packages/core/test/render3/content_spec.ts @@ -8,9 +8,6 @@ import {SelectorFlags} from '@angular/core/src/render3/interfaces/projection'; -import {Input, TemplateRef, ViewContainerRef, ViewRef} from '../../src/core'; -import {defineDirective} from '../../src/render3/definition'; -import {injectTemplateRef, injectViewContainerRef} from '../../src/render3/di'; import {AttributeMarker, detectChanges} from '../../src/render3/index'; import {bind, container, containerRefreshEnd, containerRefreshStart, element, elementContainerEnd, elementContainerStart, elementEnd, elementProperty, elementStart, embeddedViewEnd, embeddedViewStart, loadDirective, projection, projectionDef, template, text} from '../../src/render3/instructions'; import {RenderFlags} from '../../src/render3/interfaces/definition'; diff --git a/packages/core/test/render3/di_spec.ts b/packages/core/test/render3/di_spec.ts index d9cfe95038..9a87e4c76b 100644 --- a/packages/core/test/render3/di_spec.ts +++ b/packages/core/test/render3/di_spec.ts @@ -11,7 +11,8 @@ import {RenderFlags} from '@angular/core/src/render3/interfaces/definition'; import {defineComponent} from '../../src/render3/definition'; import {bloomAdd, bloomFindPossibleInjector, getOrCreateNodeInjector, injectAttribute} from '../../src/render3/di'; -import {PublicFeature, defineDirective, directiveInject, injectChangeDetectorRef, injectElementRef, injectRenderer2, injectTemplateRef, injectViewContainerRef, load} from '../../src/render3/index'; +import {PublicFeature, defineDirective, directiveInject, injectRenderer2, load} from '../../src/render3/index'; + import {bind, container, containerRefreshEnd, containerRefreshStart, createNodeAtIndex, createLViewData, createTView, element, elementEnd, elementStart, embeddedViewEnd, embeddedViewStart, enterView, interpolation2, leaveView, projection, projectionDef, reference, template, text, textBinding, loadDirective, elementContainerStart, elementContainerEnd} from '../../src/render3/instructions'; import {LInjector} from '../../src/render3/interfaces/injector'; import {isProceduralRenderer} from '../../src/render3/interfaces/renderer'; @@ -786,445 +787,456 @@ describe('di', () => { }); - describe('ElementRef', () => { - it('should create directive with ElementRef dependencies', () => { - let dir !: Directive; - let dirSameInstance !: DirectiveSameInstance; - let divNode !: LElementNode; + describe('Special tokens', () => { + + describe('ElementRef', () => { + + it('should create directive with ElementRef dependencies', () => { + let dir !: Directive; + let dirSameInstance !: DirectiveSameInstance; + let divNode !: LElementNode; + + class Directive { + value: string; + constructor(public elementRef: ElementRef) { + this.value = (elementRef.constructor as any).name; + } + static ngDirectiveDef = defineDirective({ + type: Directive, + selectors: [['', 'dir', '']], + factory: () => dir = new Directive(directiveInject(ElementRef)), + features: [PublicFeature], + exportAs: 'dir' + }); + } + + class DirectiveSameInstance { + isSameInstance: boolean; + constructor(public elementRef: ElementRef, directive: Directive) { + this.isSameInstance = elementRef === directive.elementRef; + } + static ngDirectiveDef = defineDirective({ + type: DirectiveSameInstance, + selectors: [['', 'dirSame', '']], + factory: () => dirSameInstance = new DirectiveSameInstance( + directiveInject(ElementRef), directiveInject(Directive)), + exportAs: 'dirSame' + }); + } + + /**
*/ + const App = createComponent('app', function(rf: RenderFlags, ctx: any) { + if (rf & RenderFlags.Create) { + elementStart(0, 'div', ['dir', '', 'dirSame', '']); + elementEnd(); + divNode = load(0); + } + }, 1, 0, [Directive, DirectiveSameInstance]); + + const fixture = new ComponentFixture(App); + expect(dir.value).toContain('ElementRef'); + expect(dir.elementRef.nativeElement).toEqual(divNode.native); + expect(dirSameInstance.elementRef.nativeElement).toEqual(divNode.native); + + // Each ElementRef instance should be unique + expect(dirSameInstance.isSameInstance).toBe(false); + }); + + it('should create ElementRef with comment if requesting directive is on node', + () => { + let dir !: Directive; + let commentNode !: LContainerNode; + + class Directive { + value: string; + constructor(public elementRef: ElementRef) { + this.value = (elementRef.constructor as any).name; + } + static ngDirectiveDef = defineDirective({ + type: Directive, + selectors: [['', 'dir', '']], + factory: () => dir = new Directive(directiveInject(ElementRef)), + features: [PublicFeature], + exportAs: 'dir' + }); + } + + /** */ + const App = createComponent('app', function(rf: RenderFlags, ctx: any) { + if (rf & RenderFlags.Create) { + template(0, () => {}, 0, 0, null, ['dir', '']); + commentNode = load(0); + } + }, 1, 0, [Directive]); + + const fixture = new ComponentFixture(App); + expect(dir.value).toContain('ElementRef'); + expect(dir.elementRef.nativeElement).toEqual(commentNode.native); + }); + }); + + describe('TemplateRef', () => { + it('should create directive with TemplateRef dependencies', () => { + + class Directive { + value: string; + constructor(public templateRef: TemplateRef) { + this.value = (templateRef.constructor as any).name; + } + static ngDirectiveDef = defineDirective({ + type: Directive, + selectors: [['', 'dir', '']], + factory: () => new Directive(directiveInject(TemplateRef as any)), + features: [PublicFeature], + exportAs: 'dir' + }); + } + + class DirectiveSameInstance { + isSameInstance: boolean; + constructor(templateRef: TemplateRef, directive: Directive) { + this.isSameInstance = templateRef === directive.templateRef; + } + static ngDirectiveDef = defineDirective({ + type: DirectiveSameInstance, + selectors: [['', 'dirSame', '']], + factory: () => new DirectiveSameInstance( + directiveInject(TemplateRef as any), directiveInject(Directive)), + exportAs: 'dirSame' + }); + } + + /** + * + * {{ dir.value }} - {{ dirSame.value }} + * + */ + const App = createComponent('app', function(rf: RenderFlags, ctx: any) { + if (rf & RenderFlags.Create) { + template(0, function() { + }, 0, 0, undefined, ['dir', '', 'dirSame', ''], ['dir', 'dir', 'dirSame', 'dirSame']); + text(3); + } + if (rf & RenderFlags.Update) { + const tmp1 = reference(1) as any; + const tmp2 = reference(2) as any; + textBinding(3, interpolation2('', tmp1.value, '-', tmp2.isSameInstance, '')); + } + }, 4, 2, [Directive, DirectiveSameInstance]); + + const fixture = new ComponentFixture(App); + // Each TemplateRef instance should be unique + expect(fixture.html).toContain('TemplateRef'); + expect(fixture.html).toContain('false'); + }); + }); + + describe('ViewContainerRef', () => { + it('should create directive with ViewContainerRef dependencies', () => { + class Directive { + value: string; + constructor(public viewContainerRef: ViewContainerRef) { + this.value = (viewContainerRef.constructor as any).name; + } + static ngDirectiveDef = defineDirective({ + type: Directive, + selectors: [['', 'dir', '']], + factory: () => new Directive(directiveInject(ViewContainerRef as any)), + features: [PublicFeature], + exportAs: 'dir' + }); + } + + class DirectiveSameInstance { + isSameInstance: boolean; + constructor(viewContainerRef: ViewContainerRef, directive: Directive) { + this.isSameInstance = viewContainerRef === directive.viewContainerRef; + } + static ngDirectiveDef = defineDirective({ + type: DirectiveSameInstance, + selectors: [['', 'dirSame', '']], + factory: () => new DirectiveSameInstance( + directiveInject(ViewContainerRef as any), directiveInject(Directive)), + exportAs: 'dirSame' + }); + } + + /** + *
+ * {{ dir.value }} - {{ dirSame.value }} + *
+ */ + const App = createComponent('app', function(rf: RenderFlags, ctx: any) { + if (rf & RenderFlags.Create) { + elementStart( + 0, 'div', ['dir', '', 'dirSame', ''], ['dir', 'dir', 'dirSame', 'dirSame']); + { text(3); } + elementEnd(); + } + if (rf & RenderFlags.Update) { + const tmp1 = reference(1) as any; + const tmp2 = reference(2) as any; + textBinding(3, interpolation2('', tmp1.value, '-', tmp2.isSameInstance, '')); + } + }, 4, 2, [Directive, DirectiveSameInstance]); + + const fixture = new ComponentFixture(App); + // Each ViewContainerRef instance should be unique + expect(fixture.html).toContain('ViewContainerRef'); + expect(fixture.html).toContain('false'); + }); + }); + + describe('ChangeDetectorRef', () => { + let dir: Directive; + let dirSameInstance: DirectiveSameInstance; + let comp: MyComp; + + class MyComp { + constructor(public cdr: ChangeDetectorRef) {} + + static ngComponentDef = defineComponent({ + type: MyComp, + selectors: [['my-comp']], + factory: () => comp = new MyComp(directiveInject(ChangeDetectorRef as any)), + consts: 1, + vars: 0, + template: function(rf: RenderFlags, ctx: MyComp) { + if (rf & RenderFlags.Create) { + projectionDef(); + projection(0); + } + } + }); + } class Directive { value: string; - constructor(public elementRef: ElementRef) { - this.value = (elementRef.constructor as any).name; - } + + constructor(public cdr: ChangeDetectorRef) { this.value = (cdr.constructor as any).name; } + static ngDirectiveDef = defineDirective({ type: Directive, selectors: [['', 'dir', '']], - factory: () => dir = new Directive(injectElementRef()), + factory: () => dir = new Directive(directiveInject(ChangeDetectorRef as any)), features: [PublicFeature], exportAs: 'dir' }); } class DirectiveSameInstance { - isSameInstance: boolean; - constructor(public elementRef: ElementRef, directive: Directive) { - this.isSameInstance = elementRef === directive.elementRef; - } + constructor(public cdr: ChangeDetectorRef) {} + static ngDirectiveDef = defineDirective({ type: DirectiveSameInstance, selectors: [['', 'dirSame', '']], factory: () => dirSameInstance = - new DirectiveSameInstance(injectElementRef(), directiveInject(Directive)), - exportAs: 'dirSame' + new DirectiveSameInstance(directiveInject(ChangeDetectorRef as any)) }); } - /**
*/ - const App = createComponent('app', function(rf: RenderFlags, ctx: any) { - if (rf & RenderFlags.Create) { - elementStart(0, 'div', ['dir', '', 'dirSame', '']); - elementEnd(); - divNode = load(0); - } - }, 1, 0, [Directive, DirectiveSameInstance]); + const directives = [MyComp, Directive, DirectiveSameInstance, NgIf]; - const fixture = new ComponentFixture(App); - expect(dir.value).toEqual('ElementRef'); - expect(dir.elementRef.nativeElement).toEqual(divNode.native); - expect(dirSameInstance.elementRef.nativeElement).toEqual(divNode.native); + it('should inject current component ChangeDetectorRef into directives on the same node as components', + () => { + /** {{ dir.value }} */ + const MyApp = createComponent('my-app', function(rf: RenderFlags, ctx: any) { + if (rf & RenderFlags.Create) { + element(0, 'my-comp', ['dir', '', 'dirSame', ''], ['dir', 'dir']); + text(2); + } + if (rf & RenderFlags.Update) { + const tmp = reference(1) as any; + textBinding(2, bind(tmp.value)); + } + }, 3, 1, directives); - // Each ElementRef instance should be unique - expect(dirSameInstance.isSameInstance).toBe(false); - }); + const app = renderComponent(MyApp); + // ChangeDetectorRef is the token, ViewRef has historically been the constructor + expect(toHtml(app)).toEqual('ViewRef'); + expect((comp !.cdr as ViewRef).context).toBe(comp); - it('should create ElementRef with comment if requesting directive is on node', - () => { - let dir !: Directive; - let commentNode !: LContainerNode; + // Each ChangeDetectorRef instance should be unique + expect(dir !.cdr).not.toBe(comp !.cdr); + expect(dir !.cdr).not.toBe(dirSameInstance !.cdr); + }); - class Directive { - value: string; - constructor(public elementRef: ElementRef) { - this.value = (elementRef.constructor as any).name; + it('should inject host component ChangeDetectorRef into directives on normal elements', + () => { + + class MyApp { + constructor(public cdr: ChangeDetectorRef) {} + + static ngComponentDef = defineComponent({ + type: MyApp, + selectors: [['my-app']], + consts: 3, + vars: 1, + factory: () => new MyApp(directiveInject(ChangeDetectorRef as any)), + /**
{{ dir.value }}
*/ + template: function(rf: RenderFlags, ctx: any) { + if (rf & RenderFlags.Create) { + elementStart(0, 'div', ['dir', '', 'dirSame', ''], ['dir', 'dir']); + { text(2); } + elementEnd(); + } + if (rf & RenderFlags.Update) { + const tmp = reference(1) as any; + textBinding(2, bind(tmp.value)); + } + }, + directives: directives + }); } - static ngDirectiveDef = defineDirective({ - type: Directive, - selectors: [['', 'dir', '']], - factory: () => dir = new Directive(injectElementRef()), - features: [PublicFeature], - exportAs: 'dir' - }); - } - /** */ - const App = createComponent('app', function(rf: RenderFlags, ctx: any) { - if (rf & RenderFlags.Create) { - template(0, () => {}, 0, 0, null, ['dir', '']); - commentNode = load(0); + const app = renderComponent(MyApp); + expect(toHtml(app)).toEqual('
ViewRef
'); + expect((app !.cdr as ViewRef).context).toBe(app); + + // Each ChangeDetectorRef instance should be unique + expect(dir !.cdr).not.toBe(app.cdr); + expect(dir !.cdr).not.toBe(dirSameInstance !.cdr); + }); + + it('should inject host component ChangeDetectorRef into directives in a component\'s ContentChildren', + () => { + class MyApp { + constructor(public cdr: ChangeDetectorRef) {} + + static ngComponentDef = defineComponent({ + type: MyApp, + selectors: [['my-app']], + consts: 4, + vars: 1, + factory: () => new MyApp(directiveInject(ChangeDetectorRef as any)), + /** + * + *
+ *
+ * {{ dir.value }} + */ + template: function(rf: RenderFlags, ctx: any) { + if (rf & RenderFlags.Create) { + elementStart(0, 'my-comp'); + { element(1, 'div', ['dir', '', 'dirSame', ''], ['dir', 'dir']); } + elementEnd(); + text(3); + } + if (rf & RenderFlags.Update) { + const tmp = reference(2) as any; + textBinding(3, bind(tmp.value)); + } + }, + directives: directives + }); } - }, 1, 0, [Directive]); - const fixture = new ComponentFixture(App); - expect(dir.value).toEqual('ElementRef'); - expect(dir.elementRef.nativeElement).toEqual(commentNode.native); - }); - }); + const app = renderComponent(MyApp); + expect(toHtml(app)).toEqual('
ViewRef'); + expect((app !.cdr as ViewRef).context).toBe(app); - describe('TemplateRef', () => { - it('should create directive with TemplateRef dependencies', () => { - class Directive { - value: string; - constructor(public templateRef: TemplateRef) { - this.value = (templateRef.constructor as any).name; - } - static ngDirectiveDef = defineDirective({ - type: Directive, - selectors: [['', 'dir', '']], - factory: () => new Directive(injectTemplateRef()), - features: [PublicFeature], - exportAs: 'dir' - }); - } + // Each ChangeDetectorRef instance should be unique + expect(dir !.cdr).not.toBe(app !.cdr); + expect(dir !.cdr).not.toBe(dirSameInstance !.cdr); + }); - class DirectiveSameInstance { - isSameInstance: boolean; - constructor(templateRef: TemplateRef, directive: Directive) { - this.isSameInstance = templateRef === directive.templateRef; - } - static ngDirectiveDef = defineDirective({ - type: DirectiveSameInstance, - selectors: [['', 'dirSame', '']], - factory: () => new DirectiveSameInstance(injectTemplateRef(), directiveInject(Directive)), - exportAs: 'dirSame' - }); - } + it('should inject host component ChangeDetectorRef into directives in embedded views', () => { - /** - * - * {{ dir.value }} - {{ dirSame.value }} - * - */ - const App = createComponent('app', function(rf: RenderFlags, ctx: any) { - if (rf & RenderFlags.Create) { - template(0, function() { - }, 0, 0, undefined, ['dir', '', 'dirSame', ''], ['dir', 'dir', 'dirSame', 'dirSame']); - text(3); - } - if (rf & RenderFlags.Update) { - const tmp1 = reference(1) as any; - const tmp2 = reference(2) as any; - textBinding(3, interpolation2('', tmp1.value, '-', tmp2.isSameInstance, '')); - } - }, 4, 2, [Directive, DirectiveSameInstance]); + class MyApp { + showing = true; - const fixture = new ComponentFixture(App); - // Each TemplateRef instance should be unique - expect(fixture.html).toEqual('TemplateRef-false'); - }); - }); + constructor(public cdr: ChangeDetectorRef) {} - describe('ViewContainerRef', () => { - it('should create directive with ViewContainerRef dependencies', () => { - class Directive { - value: string; - constructor(public viewContainerRef: ViewContainerRef) { - this.value = (viewContainerRef.constructor as any).name; - } - static ngDirectiveDef = defineDirective({ - type: Directive, - selectors: [['', 'dir', '']], - factory: () => new Directive(injectViewContainerRef()), - features: [PublicFeature], - exportAs: 'dir' - }); - } - - class DirectiveSameInstance { - isSameInstance: boolean; - constructor(viewContainerRef: ViewContainerRef, directive: Directive) { - this.isSameInstance = viewContainerRef === directive.viewContainerRef; - } - static ngDirectiveDef = defineDirective({ - type: DirectiveSameInstance, - selectors: [['', 'dirSame', '']], - factory: - () => new DirectiveSameInstance(injectViewContainerRef(), directiveInject(Directive)), - exportAs: 'dirSame' - }); - } - - /** - *
- * {{ dir.value }} - {{ dirSame.value }} - *
- */ - const App = createComponent('app', function(rf: RenderFlags, ctx: any) { - if (rf & RenderFlags.Create) { - elementStart(0, 'div', ['dir', '', 'dirSame', ''], ['dir', 'dir', 'dirSame', 'dirSame']); - { text(3); } - elementEnd(); - } - if (rf & RenderFlags.Update) { - const tmp1 = reference(1) as any; - const tmp2 = reference(2) as any; - textBinding(3, interpolation2('', tmp1.value, '-', tmp2.isSameInstance, '')); - } - }, 4, 2, [Directive, DirectiveSameInstance]); - - const fixture = new ComponentFixture(App); - // Each ViewContainerRef instance should be unique - expect(fixture.html).toEqual('
ViewContainerRef-false
'); - }); - }); - - describe('ChangeDetectorRef', () => { - let dir: Directive; - let dirSameInstance: DirectiveSameInstance; - let comp: MyComp; - - class MyComp { - constructor(public cdr: ChangeDetectorRef) {} - - static ngComponentDef = defineComponent({ - type: MyComp, - selectors: [['my-comp']], - factory: () => comp = new MyComp(injectChangeDetectorRef()), - consts: 1, - vars: 0, - template: function(rf: RenderFlags, ctx: MyComp) { - if (rf & RenderFlags.Create) { - projectionDef(); - projection(0); - } - } - }); - } - - class Directive { - value: string; - - constructor(public cdr: ChangeDetectorRef) { this.value = (cdr.constructor as any).name; } - - static ngDirectiveDef = defineDirective({ - type: Directive, - selectors: [['', 'dir', '']], - factory: () => dir = new Directive(injectChangeDetectorRef()), - features: [PublicFeature], - exportAs: 'dir' - }); - } - - class DirectiveSameInstance { - constructor(public cdr: ChangeDetectorRef) {} - - static ngDirectiveDef = defineDirective({ - type: DirectiveSameInstance, - selectors: [['', 'dirSame', '']], - factory: () => dirSameInstance = new DirectiveSameInstance(injectChangeDetectorRef()) - }); - } - - const directives = [MyComp, Directive, DirectiveSameInstance, NgIf]; - - it('should inject current component ChangeDetectorRef into directives on the same node as components', - () => { - /** {{ dir.value }} */ - const MyApp = createComponent('my-app', function(rf: RenderFlags, ctx: any) { - if (rf & RenderFlags.Create) { - element(0, 'my-comp', ['dir', '', 'dirSame', ''], ['dir', 'dir']); - text(2); - } - if (rf & RenderFlags.Update) { - const tmp = reference(1) as any; - textBinding(2, bind(tmp.value)); - } - }, 3, 1, directives); - - const app = renderComponent(MyApp); - // ChangeDetectorRef is the token, ViewRef has historically been the constructor - expect(toHtml(app)).toEqual('ViewRef'); - expect((comp !.cdr as ViewRef).context).toBe(comp); - - // Each ChangeDetectorRef instance should be unique - expect(dir !.cdr).not.toBe(comp !.cdr); - expect(dir !.cdr).not.toBe(dirSameInstance !.cdr); - }); - - it('should inject host component ChangeDetectorRef into directives on normal elements', () => { - - class MyApp { - constructor(public cdr: ChangeDetectorRef) {} - - static ngComponentDef = defineComponent({ - type: MyApp, - selectors: [['my-app']], - consts: 3, - vars: 1, - factory: () => new MyApp(injectChangeDetectorRef()), - /**
{{ dir.value }}
*/ - template: function(rf: RenderFlags, ctx: any) { - if (rf & RenderFlags.Create) { - elementStart(0, 'div', ['dir', '', 'dirSame', ''], ['dir', 'dir']); - { text(2); } - elementEnd(); - } - if (rf & RenderFlags.Update) { - const tmp = reference(1) as any; - textBinding(2, bind(tmp.value)); - } - }, - directives: directives - }); - } - - const app = renderComponent(MyApp); - expect(toHtml(app)).toEqual('
ViewRef
'); - expect((app !.cdr as ViewRef).context).toBe(app); - - // Each ChangeDetectorRef instance should be unique - expect(dir !.cdr).not.toBe(app.cdr); - expect(dir !.cdr).not.toBe(dirSameInstance !.cdr); - }); - - it('should inject host component ChangeDetectorRef into directives in a component\'s ContentChildren', - () => { - class MyApp { - constructor(public cdr: ChangeDetectorRef) {} - - static ngComponentDef = defineComponent({ - type: MyApp, - selectors: [['my-app']], - consts: 4, - vars: 1, - factory: () => new MyApp(injectChangeDetectorRef()), - /** - * - *
- *
- * {{ dir.value }} - */ - template: function(rf: RenderFlags, ctx: any) { - if (rf & RenderFlags.Create) { - elementStart(0, 'my-comp'); - { element(1, 'div', ['dir', '', 'dirSame', ''], ['dir', 'dir']); } - elementEnd(); - text(3); - } - if (rf & RenderFlags.Update) { - const tmp = reference(2) as any; - textBinding(3, bind(tmp.value)); - } - }, - directives: directives - }); - } - - const app = renderComponent(MyApp); - expect(toHtml(app)).toEqual('
ViewRef'); - expect((app !.cdr as ViewRef).context).toBe(app); - - // Each ChangeDetectorRef instance should be unique - expect(dir !.cdr).not.toBe(app !.cdr); - expect(dir !.cdr).not.toBe(dirSameInstance !.cdr); - }); - - it('should inject host component ChangeDetectorRef into directives in embedded views', () => { - - class MyApp { - showing = true; - - constructor(public cdr: ChangeDetectorRef) {} - - static ngComponentDef = defineComponent({ - type: MyApp, - selectors: [['my-app']], - factory: () => new MyApp(injectChangeDetectorRef()), - consts: 1, - vars: 0, - /** - * % if (showing) { + static ngComponentDef = defineComponent({ + type: MyApp, + selectors: [['my-app']], + factory: () => new MyApp(directiveInject(ChangeDetectorRef as any)), + consts: 1, + vars: 0, + /** + * % if (showing) { *
{{ dir.value }}
* % } - */ - template: function(rf: RenderFlags, ctx: MyApp) { - if (rf & RenderFlags.Create) { - container(0); - } - if (rf & RenderFlags.Update) { - containerRefreshStart(0); - { - if (ctx.showing) { - let rf1 = embeddedViewStart(0, 3, 1); - if (rf1 & RenderFlags.Create) { - elementStart(0, 'div', ['dir', '', 'dirSame', ''], ['dir', 'dir']); - { text(2); } - elementEnd(); - } - if (rf1 & RenderFlags.Update) { - const tmp = reference(1) as any; - textBinding(2, bind(tmp.value)); - } - } - embeddedViewEnd(); + */ + template: function(rf: RenderFlags, ctx: MyApp) { + if (rf & RenderFlags.Create) { + container(0); } - containerRefreshEnd(); - } - }, - directives: directives - }); - } - - const app = renderComponent(MyApp); - expect(toHtml(app)).toEqual('
ViewRef
'); - expect((app !.cdr as ViewRef).context).toBe(app); - - // Each ChangeDetectorRef instance should be unique - expect(dir !.cdr).not.toBe(app.cdr); - expect(dir !.cdr).not.toBe(dirSameInstance !.cdr); - }); - - it('should inject host component ChangeDetectorRef into directives on containers', () => { - function C1(rf1: RenderFlags, ctx1: any) { - if (rf1 & RenderFlags.Create) { - elementStart(0, 'div', ['dir', '', 'dirSame', ''], ['dir', 'dir']); - { text(2); } - elementEnd(); + if (rf & RenderFlags.Update) { + containerRefreshStart(0); + { + if (ctx.showing) { + let rf1 = embeddedViewStart(0, 3, 1); + if (rf1 & RenderFlags.Create) { + elementStart(0, 'div', ['dir', '', 'dirSame', ''], ['dir', 'dir']); + { text(2); } + elementEnd(); + } + if (rf1 & RenderFlags.Update) { + const tmp = reference(1) as any; + textBinding(2, bind(tmp.value)); + } + } + embeddedViewEnd(); + } + containerRefreshEnd(); + } + }, + directives: directives + }); } - if (rf1 & RenderFlags.Update) { - const tmp = reference(1) as any; - textBinding(2, bind(tmp.value)); + + const app = renderComponent(MyApp); + expect(toHtml(app)).toEqual('
ViewRef
'); + expect((app !.cdr as ViewRef).context).toBe(app); + + // Each ChangeDetectorRef instance should be unique + expect(dir !.cdr).not.toBe(app.cdr); + expect(dir !.cdr).not.toBe(dirSameInstance !.cdr); + }); + + it('should inject host component ChangeDetectorRef into directives on containers', () => { + function C1(rf1: RenderFlags, ctx1: any) { + if (rf1 & RenderFlags.Create) { + elementStart(0, 'div', ['dir', '', 'dirSame', ''], ['dir', 'dir']); + { text(2); } + elementEnd(); + } + if (rf1 & RenderFlags.Update) { + const tmp = reference(1) as any; + textBinding(2, bind(tmp.value)); + } } - } - class MyApp { - showing = true; + class MyApp { + showing = true; - constructor(public cdr: ChangeDetectorRef) {} + constructor(public cdr: ChangeDetectorRef) {} - static ngComponentDef = defineComponent({ - type: MyApp, - selectors: [['my-app']], - factory: () => new MyApp(injectChangeDetectorRef()), - consts: 1, - vars: 0, - /**
{{ dir.value }}
*/ - template: function(rf: RenderFlags, ctx: MyApp) { - if (rf & RenderFlags.Create) { - template(0, C1, 3, 1, null, ['ngIf', 'showing']); - } - }, - directives: directives - }); - } + static ngComponentDef = defineComponent({ + type: MyApp, + selectors: [['my-app']], + factory: () => new MyApp(directiveInject(ChangeDetectorRef as any)), + consts: 1, + vars: 0, + /**
{{ dir.value }}
*/ + template: function(rf: RenderFlags, ctx: MyApp) { + if (rf & RenderFlags.Create) { + template(0, C1, 3, 1, null, ['ngIf', 'showing']); + } + }, + directives: directives + }); + } - const app = renderComponent(MyApp); - expect(toHtml(app)).toEqual('
ViewRef
'); - expect((app !.cdr as ViewRef).context).toBe(app); + const app = renderComponent(MyApp); + expect(toHtml(app)).toEqual('
ViewRef
'); + expect((app !.cdr as ViewRef).context).toBe(app); - // Each ChangeDetectorRef instance should be unique - expect(dir !.cdr).not.toBe(app.cdr); - expect(dir !.cdr).not.toBe(dirSameInstance !.cdr); + // Each ChangeDetectorRef instance should be unique + expect(dir !.cdr).not.toBe(app.cdr); + expect(dir !.cdr).not.toBe(dirSameInstance !.cdr); + }); }); }); diff --git a/packages/core/test/render3/integration_spec.ts b/packages/core/test/render3/integration_spec.ts index 15ba6895f8..5e959cb163 100644 --- a/packages/core/test/render3/integration_spec.ts +++ b/packages/core/test/render3/integration_spec.ts @@ -10,8 +10,7 @@ import {ElementRef, TemplateRef, ViewContainerRef} from '@angular/core'; import {RenderFlags} from '@angular/core/src/render3'; import {RendererStyleFlags2, RendererType2} from '../../src/render/api'; -import {createTemplateRef, getOrCreateNodeInjectorForNode} from '../../src/render3/di'; -import {AttributeMarker, defineComponent, defineDirective, injectElementRef, injectTemplateRef, injectViewContainerRef} from '../../src/render3/index'; +import {AttributeMarker, defineComponent, defineDirective} from '../../src/render3/index'; import {NO_CHANGE, bind, container, containerRefreshEnd, containerRefreshStart, element, elementAttribute, elementClassProp, elementContainerEnd, elementContainerStart, elementEnd, elementProperty, elementStart, elementStyleProp, elementStyling, elementStylingApply, embeddedViewEnd, embeddedViewStart, interpolation1, interpolation2, interpolation3, interpolation4, interpolation5, interpolation6, interpolation7, interpolation8, interpolationV, listener, load, loadDirective, projection, projectionDef, text, textBinding, template} from '../../src/render3/instructions'; import {InitialStylingFlags} from '../../src/render3/interfaces/definition'; @@ -21,9 +20,10 @@ import {sanitizeUrl} from '../../src/sanitization/sanitization'; import {Sanitizer, SecurityContext} from '../../src/sanitization/security'; import {NgIf} from './common_with_def'; -import {ComponentFixture, TemplateFixture, containerEl, createComponent, renderToHtml} from './render_util'; +import {ComponentFixture, TemplateFixture, createComponent, renderToHtml} from './render_util'; import {MONKEY_PATCH_KEY_NAME, getContext} from '../../src/render3/context_discovery'; import {StylingIndex} from '../../src/render3/styling'; +import {directiveInject} from '../../src/render3/di'; describe('render3 integration test', () => { @@ -649,7 +649,9 @@ describe('render3 integration test', () => { static ngDirectiveDef = defineDirective({ type: TestDirective, selectors: [['', 'testDirective', '']], - factory: () => new TestDirective(injectTemplateRef(), injectViewContainerRef()), + factory: + () => new TestDirective( + directiveInject(TemplateRef as any), directiveInject(ViewContainerRef as any)), }); } @@ -754,7 +756,9 @@ describe('render3 integration test', () => { static ngDirectiveDef = defineDirective({ type: TestDirective, selectors: [['', 'testDirective', '']], - factory: () => new TestDirective(injectTemplateRef(), injectViewContainerRef()), + factory: + () => new TestDirective( + directiveInject(TemplateRef as any), directiveInject(ViewContainerRef as any)), }); } @@ -822,7 +826,7 @@ describe('render3 integration test', () => { static ngDirectiveDef = defineDirective({ type: Directive, selectors: [['', 'dir', '']], - factory: () => new Directive(injectElementRef()), + factory: () => new Directive(directiveInject(ElementRef)), }); } diff --git a/packages/core/test/render3/ivy/jit_spec.ts b/packages/core/test/render3/ivy/jit_spec.ts index 9018299f75..6a8689c888 100644 --- a/packages/core/test/render3/ivy/jit_spec.ts +++ b/packages/core/test/render3/ivy/jit_spec.ts @@ -11,7 +11,7 @@ import 'reflect-metadata'; 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'; -import {ivyEnabled} from '@angular/core/src/ivy_switch'; +import {ivyEnabled} from '@angular/core/src/ivy_switch/compiler/index'; import {Component, HostBinding, HostListener, Input, Output, Pipe} from '@angular/core/src/metadata/directives'; import {NgModule, NgModuleDefInternal} from '@angular/core/src/metadata/ng_module'; import {ComponentDefInternal, PipeDefInternal} from '@angular/core/src/render3/interfaces/definition'; diff --git a/packages/core/test/render3/query_spec.ts b/packages/core/test/render3/query_spec.ts index 3fb5fc285c..00178fdee1 100644 --- a/packages/core/test/render3/query_spec.ts +++ b/packages/core/test/render3/query_spec.ts @@ -10,11 +10,14 @@ import {NgForOfContext} from '@angular/common'; import {ElementRef, TemplateRef, ViewContainerRef} from '@angular/core'; import {EventEmitter} from '../..'; -import {QUERY_READ_CONTAINER_REF, QUERY_READ_ELEMENT_REF, QUERY_READ_FROM_NODE, QUERY_READ_TEMPLATE_REF, templateRefExtractor} from '../../src/render3/di'; -import {AttributeMarker, QueryList, defineComponent, defineDirective, detectChanges, injectTemplateRef, injectViewContainerRef} from '../../src/render3/index'; +import {directiveInject} from '../../src/render3/di'; + +import {AttributeMarker, QueryList, defineComponent, defineDirective, detectChanges} from '../../src/render3/index'; + import {bind, container, containerRefreshEnd, containerRefreshStart, element, elementContainerEnd, elementContainerStart, elementEnd, elementProperty, elementStart, embeddedViewEnd, embeddedViewStart, load, loadDirective, loadElement, loadQueryList, reference, registerContentQuery, template} from '../../src/render3/instructions'; import {RenderFlags} from '../../src/render3/interfaces/definition'; -import {query, queryRefresh} from '../../src/render3/query'; +import {query, queryRefresh, QUERY_READ_CONTAINER_REF, QUERY_READ_ELEMENT_REF, QUERY_READ_FROM_NODE, QUERY_READ_TEMPLATE_REF} from '../../src/render3/query'; +import {templateRefExtractor} from '../../src/render3/view_engine_compatibility'; import {NgForOf, NgIf, NgTemplateOutlet} from './common_with_def'; import {ComponentFixture, TemplateFixture, createComponent, createDirective, renderComponent} from './render_util'; @@ -118,7 +121,7 @@ describe('query', () => { 2, 0, [Child], [], function(rf: RenderFlags, ctx: any) { if (rf & RenderFlags.Create) { - query(0, Child, false, QUERY_READ_ELEMENT_REF); + query(0, Child, false, QUERY_READ_ELEMENT_REF(ElementRef)); } if (rf & RenderFlags.Update) { let tmp: any; @@ -226,7 +229,7 @@ describe('query', () => { 4, 0, [], [], function(rf: RenderFlags, ctx: any) { if (rf & RenderFlags.Create) { - query(0, ['foo'], false, QUERY_READ_FROM_NODE); + query(0, ['foo'], false, QUERY_READ_FROM_NODE(TemplateRef, ElementRef)); } if (rf & RenderFlags.Update) { let tmp: any; @@ -263,8 +266,8 @@ describe('query', () => { 6, 0, [], [], function(rf: RenderFlags, ctx: any) { if (rf & RenderFlags.Create) { - query(0, ['foo'], false, QUERY_READ_FROM_NODE); - query(1, ['bar'], false, QUERY_READ_FROM_NODE); + query(0, ['foo'], false, QUERY_READ_FROM_NODE(TemplateRef, ElementRef)); + query(1, ['bar'], false, QUERY_READ_FROM_NODE(TemplateRef, ElementRef)); } if (rf & RenderFlags.Update) { let tmp: any; @@ -312,7 +315,7 @@ describe('query', () => { 6, 0, [], [], function(rf: RenderFlags, ctx: any) { if (rf & RenderFlags.Create) { - query(0, ['foo', 'bar'], undefined, QUERY_READ_FROM_NODE); + query(0, ['foo', 'bar'], undefined, QUERY_READ_FROM_NODE(TemplateRef, ElementRef)); } if (rf & RenderFlags.Update) { let tmp: any; @@ -349,7 +352,7 @@ describe('query', () => { 4, 0, [], [], function(rf: RenderFlags, ctx: any) { if (rf & RenderFlags.Create) { - query(0, ['foo'], false, QUERY_READ_ELEMENT_REF); + query(0, ['foo'], false, QUERY_READ_ELEMENT_REF(ElementRef)); } if (rf & RenderFlags.Update) { let tmp: any; @@ -385,7 +388,7 @@ describe('query', () => { 3, 0, [], [], function(rf: RenderFlags, ctx: any) { if (rf & RenderFlags.Create) { - query(0, ['foo'], false, QUERY_READ_ELEMENT_REF); + query(0, ['foo'], false, QUERY_READ_ELEMENT_REF(ElementRef)); } if (rf & RenderFlags.Update) { let tmp: any; @@ -421,7 +424,7 @@ describe('query', () => { 3, 0, [], [], function(rf: RenderFlags, ctx: any) { if (rf & RenderFlags.Create) { - query(0, ['foo'], true, QUERY_READ_FROM_NODE); + query(0, ['foo'], true, QUERY_READ_FROM_NODE(TemplateRef, ElementRef)); } if (rf & RenderFlags.Update) { let tmp: any; @@ -485,8 +488,8 @@ describe('query', () => { 5, 0, [], [], function(rf: RenderFlags, ctx: any) { if (rf & RenderFlags.Create) { - query(0, ['foo'], true, QUERY_READ_ELEMENT_REF); - query(1, ['foo'], false, QUERY_READ_ELEMENT_REF); + query(0, ['foo'], true, QUERY_READ_ELEMENT_REF(ElementRef)); + query(1, ['foo'], false, QUERY_READ_ELEMENT_REF(ElementRef)); } if (rf & RenderFlags.Update) { let tmp: any; @@ -520,7 +523,7 @@ describe('query', () => { 3, 0, [], [], function(rf: RenderFlags, ctx: any) { if (rf & RenderFlags.Create) { - query(0, ['foo'], false, QUERY_READ_CONTAINER_REF); + query(0, ['foo'], false, QUERY_READ_CONTAINER_REF(ViewContainerRef, ElementRef)); } if (rf & RenderFlags.Update) { let tmp: any; @@ -551,7 +554,7 @@ describe('query', () => { 3, 0, [], [], function(rf: RenderFlags, ctx: any) { if (rf & RenderFlags.Create) { - query(0, ['foo'], false, QUERY_READ_CONTAINER_REF); + query(0, ['foo'], false, QUERY_READ_CONTAINER_REF(ViewContainerRef, ElementRef)); } if (rf & RenderFlags.Update) { let tmp: any; @@ -584,7 +587,7 @@ describe('query', () => { function(rf: RenderFlags, ctx: any) { if (rf & RenderFlags.Create) { - query(0, ['foo'], false, QUERY_READ_ELEMENT_REF); + query(0, ['foo'], false, QUERY_READ_ELEMENT_REF(ElementRef)); } if (rf & RenderFlags.Update) { let tmp: any; @@ -618,7 +621,7 @@ describe('query', () => { 3, 0, [], [], function(rf: RenderFlags, ctx: any) { if (rf & RenderFlags.Create) { - query(0, ['foo'], undefined, QUERY_READ_FROM_NODE); + query(0, ['foo'], undefined, QUERY_READ_FROM_NODE(TemplateRef, ElementRef)); } if (rf & RenderFlags.Update) { let tmp: any; @@ -650,7 +653,7 @@ describe('query', () => { 3, 0, [], [], function(rf: RenderFlags, ctx: any) { if (rf & RenderFlags.Create) { - query(0, ['foo'], false, QUERY_READ_TEMPLATE_REF); + query(0, ['foo'], false, QUERY_READ_TEMPLATE_REF(TemplateRef, ElementRef)); } if (rf & RenderFlags.Update) { let tmp: any; @@ -687,7 +690,7 @@ describe('query', () => { 3, 0, [Child], [], function(rf: RenderFlags, ctx: any) { if (rf & RenderFlags.Create) { - query(0, ['foo'], true, QUERY_READ_FROM_NODE); + query(0, ['foo'], true, QUERY_READ_FROM_NODE(TemplateRef, ElementRef)); } if (rf & RenderFlags.Update) { let tmp: any; @@ -732,7 +735,7 @@ describe('query', () => { 3, 0, [Child], [], function(rf: RenderFlags, ctx: any) { if (rf & RenderFlags.Create) { - query(0, ['foo'], true, QUERY_READ_FROM_NODE); + query(0, ['foo'], true, QUERY_READ_FROM_NODE(TemplateRef, ElementRef)); } if (rf & RenderFlags.Update) { let tmp: any; @@ -770,7 +773,7 @@ describe('query', () => { 3, 0, [Child], [], function(rf: RenderFlags, ctx: any) { if (rf & RenderFlags.Create) { - query(0, ['foo'], true, QUERY_READ_FROM_NODE); + query(0, ['foo'], true, QUERY_READ_FROM_NODE(TemplateRef, ElementRef)); } if (rf & RenderFlags.Update) { let tmp: any; @@ -810,7 +813,7 @@ describe('query', () => { 4, 0, [Child1, Child2], [], function(rf: RenderFlags, ctx: any) { if (rf & RenderFlags.Create) { - query(0, ['foo', 'bar'], true, QUERY_READ_FROM_NODE); + query(0, ['foo', 'bar'], true, QUERY_READ_FROM_NODE(TemplateRef, ElementRef)); } if (rf & RenderFlags.Update) { let tmp: any; @@ -849,8 +852,8 @@ describe('query', () => { 5, 0, [Child], [], function(rf: RenderFlags, ctx: any) { if (rf & RenderFlags.Create) { - query(0, ['foo'], true, QUERY_READ_FROM_NODE); - query(1, ['bar'], true, QUERY_READ_FROM_NODE); + query(0, ['foo'], true, QUERY_READ_FROM_NODE(TemplateRef, ElementRef)); + query(1, ['bar'], true, QUERY_READ_FROM_NODE(TemplateRef, ElementRef)); } if (rf & RenderFlags.Update) { let tmp: any; @@ -893,7 +896,7 @@ describe('query', () => { 3, 0, [Child], [], function(rf: RenderFlags, ctx: any) { if (rf & RenderFlags.Create) { - query(0, ['foo'], undefined, QUERY_READ_ELEMENT_REF); + query(0, ['foo'], undefined, QUERY_READ_ELEMENT_REF(ElementRef)); } if (rf & RenderFlags.Update) { let tmp: any; @@ -931,7 +934,7 @@ describe('query', () => { 4, 0, [Child], [], function(rf: RenderFlags, ctx: any) { if (rf & RenderFlags.Create) { - query(0, ['foo', 'bar'], undefined, QUERY_READ_FROM_NODE); + query(0, ['foo', 'bar'], undefined, QUERY_READ_FROM_NODE(TemplateRef, ElementRef)); } if (rf & RenderFlags.Update) { let tmp: any; @@ -993,7 +996,7 @@ describe('query', () => { selectors: [['', 'vc', '']], factory: () => { const directiveInstance = - new ViewContainerManipulatorDirective(injectViewContainerRef()); + new ViewContainerManipulatorDirective(directiveInject(ViewContainerRef as any)); directiveInstances.push(directiveInstance); return directiveInstance; } @@ -1039,7 +1042,7 @@ describe('query', () => { 3, 1, [NgIf], [], function(rf: RenderFlags, ctx: any) { if (rf & RenderFlags.Create) { - query(0, ['foo'], true, QUERY_READ_FROM_NODE); + query(0, ['foo'], true, QUERY_READ_FROM_NODE(TemplateRef, ElementRef)); } if (rf & RenderFlags.Update) { let tmp: any; @@ -1100,7 +1103,7 @@ describe('query', () => { viewQuery: function(rf: RenderFlags, ctx: Cmpt) { let tmp: any; if (rf & RenderFlags.Create) { - query(0, ['foo'], true, QUERY_READ_FROM_NODE); + query(0, ['foo'], true, QUERY_READ_FROM_NODE(TemplateRef, ElementRef)); } if (rf & RenderFlags.Update) { queryRefresh(tmp = load>(0)) && (ctx.query = tmp as QueryList); @@ -1170,10 +1173,12 @@ describe('query', () => { function(rf: RenderFlags, ctx: any) { if (rf & RenderFlags.Create) { template( - 1, Cmpt_Template_1, 2, 1, null, null, ['tpl1', ''], templateRefExtractor); + 1, Cmpt_Template_1, 2, 1, null, null, ['tpl1', ''], + templateRefExtractor(TemplateRef, ElementRef)); element(3, 'div', ['id', 'middle'], ['foo', '']); template( - 5, Cmpt_Template_5, 2, 1, null, null, ['tpl2', ''], templateRefExtractor); + 5, Cmpt_Template_5, 2, 1, null, null, ['tpl2', ''], + templateRefExtractor(TemplateRef, ElementRef)); template(7, null, 0, 0, null, [AttributeMarker.SelectOnly, 'vc']); } @@ -1186,7 +1191,7 @@ describe('query', () => { 9, 0, [ViewContainerManipulatorDirective], [], function(rf: RenderFlags, ctx: any) { if (rf & RenderFlags.Create) { - query(0, ['foo'], true, QUERY_READ_FROM_NODE); + query(0, ['foo'], true, QUERY_READ_FROM_NODE(TemplateRef, ElementRef)); } if (rf & RenderFlags.Update) { let tmp: any; @@ -1268,7 +1273,9 @@ describe('query', () => { template: function(rf: RenderFlags, ctx: any) { let tmp: any; if (rf & RenderFlags.Create) { - template(1, Cmpt_Template_1, 2, 1, null, [], ['tpl', ''], templateRefExtractor); + template( + 1, Cmpt_Template_1, 2, 1, null, [], ['tpl', ''], + templateRefExtractor(TemplateRef, ElementRef)); template(3, null, 0, 0, null, [AttributeMarker.SelectOnly, 'vc']); template(4, null, 0, 0, null, [AttributeMarker.SelectOnly, 'vc']); } @@ -1281,7 +1288,7 @@ describe('query', () => { viewQuery: (rf: RenderFlags, cmpt: Cmpt) => { let tmp: any; if (rf & RenderFlags.Create) { - query(0, ['foo'], true, QUERY_READ_FROM_NODE); + query(0, ['foo'], true, QUERY_READ_FROM_NODE(TemplateRef, ElementRef)); } if (rf & RenderFlags.Update) { queryRefresh(tmp = load>(0)) && @@ -1341,7 +1348,7 @@ describe('query', () => { if (rf & RenderFlags.Create) { template( 1, MyApp_Template_1, 2, 0, undefined, undefined, ['tpl', ''], - templateRefExtractor); + templateRefExtractor(TemplateRef, ElementRef)); template(3, null, 0, 0, null, [AttributeMarker.SelectOnly, 'ngTemplateOutlet']); } if (rf & RenderFlags.Update) { @@ -1353,7 +1360,7 @@ describe('query', () => { viewQuery: (rf: RenderFlags, myApp: MyApp) => { let tmp: any; if (rf & RenderFlags.Create) { - query(0, ['foo'], true, QUERY_READ_FROM_NODE); + query(0, ['foo'], true, QUERY_READ_FROM_NODE(TemplateRef, ElementRef)); } if (rf & RenderFlags.Update) { queryRefresh(tmp = load>(0)) && @@ -1418,7 +1425,7 @@ describe('query', () => { 2, 0, [], [], function(rf: RenderFlags, ctx: any) { if (rf & RenderFlags.Create) { - query(0, ['foo'], true, QUERY_READ_FROM_NODE); + query(0, ['foo'], true, QUERY_READ_FROM_NODE(TemplateRef, ElementRef)); } if (rf & RenderFlags.Update) { let tmp: any; @@ -1483,7 +1490,7 @@ describe('query', () => { 6, 0, [], [], function(rf: RenderFlags, ctx: any) { if (rf & RenderFlags.Create) { - query(0, ['foo'], true, QUERY_READ_FROM_NODE); + query(0, ['foo'], true, QUERY_READ_FROM_NODE(TemplateRef, ElementRef)); } if (rf & RenderFlags.Update) { let tmp: any; @@ -1560,7 +1567,7 @@ describe('query', () => { 2, 0, [], [], function(rf: RenderFlags, ctx: any) { if (rf & RenderFlags.Create) { - query(0, ['foo'], true, QUERY_READ_FROM_NODE); + query(0, ['foo'], true, QUERY_READ_FROM_NODE(TemplateRef, ElementRef)); } if (rf & RenderFlags.Update) { let tmp: any; @@ -1640,7 +1647,7 @@ describe('query', () => { 2, 0, [], [], function(rf: RenderFlags, ctx: any) { if (rf & RenderFlags.Create) { - query(0, ['foo'], true, QUERY_READ_FROM_NODE); + query(0, ['foo'], true, QUERY_READ_FROM_NODE(TemplateRef, ElementRef)); } if (rf & RenderFlags.Update) { let tmp: any; @@ -1709,8 +1716,8 @@ describe('query', () => { 5, 0, [], [], function(rf: RenderFlags, ctx: any) { if (rf & RenderFlags.Create) { - query(0, ['foo'], true, QUERY_READ_FROM_NODE); - query(1, ['foo'], false, QUERY_READ_FROM_NODE); + query(0, ['foo'], true, QUERY_READ_FROM_NODE(TemplateRef, ElementRef)); + query(1, ['foo'], false, QUERY_READ_FROM_NODE(TemplateRef, ElementRef)); } if (rf & RenderFlags.Update) { let tmp: any; @@ -1790,7 +1797,7 @@ describe('query', () => { 2, 0, [], [], function(rf: RenderFlags, ctx: any) { if (rf & RenderFlags.Create) { - query(0, ['foo'], false, QUERY_READ_FROM_NODE); + query(0, ['foo'], false, QUERY_READ_FROM_NODE(TemplateRef, ElementRef)); } if (rf & RenderFlags.Update) { let tmp: any; @@ -1844,7 +1851,8 @@ describe('query', () => { static ngDirectiveDef = defineDirective({ type: SomeDir, selectors: [['', 'someDir', '']], - factory: () => new SomeDir(injectViewContainerRef(), injectTemplateRef()) + factory: () => new SomeDir( + directiveInject(ViewContainerRef as any), directiveInject(TemplateRef as any)) }); } @@ -1870,7 +1878,7 @@ describe('query', () => { 4, 0, [SomeDir], [], function(rf: RenderFlags, ctx: any) { if (rf & RenderFlags.Create) { - query(0, ['foo'], true, QUERY_READ_FROM_NODE); + query(0, ['foo'], true, QUERY_READ_FROM_NODE(TemplateRef, ElementRef)); } if (rf & RenderFlags.Update) { let tmp: any; @@ -1907,8 +1915,10 @@ describe('query', () => { type: WithContentDirective, selectors: [['', 'with-content', '']], factory: () => new WithContentDirective(), - contentQueries: - () => { registerContentQuery(query(null, ['foo'], true, QUERY_READ_FROM_NODE)); }, + contentQueries: () => { + registerContentQuery( + query(null, ['foo'], true, QUERY_READ_FROM_NODE(TemplateRef, ElementRef))); + }, contentQueriesRefresh: (dirIndex: number, queryStartIdx: number) => { let tmp: any; withContentInstance = loadDirective(dirIndex); @@ -1929,8 +1939,10 @@ describe('query', () => { template: function(rf: RenderFlags, ctx: any) {}, consts: 0, vars: 0, - contentQueries: - () => { registerContentQuery(query(null, ['foo'], false, QUERY_READ_FROM_NODE)); }, + contentQueries: () => { + registerContentQuery( + query(null, ['foo'], false, QUERY_READ_FROM_NODE(TemplateRef, ElementRef))); + }, contentQueriesRefresh: (dirIndex: number, queryStartIdx: number) => { let tmp: any; shallowCompInstance = loadDirective(dirIndex); @@ -2046,7 +2058,7 @@ describe('query', () => { 6, 0, [WithContentDirective], [], function(rf: RenderFlags, ctx: any) { if (rf & RenderFlags.Create) { - query(0, ['foo', 'bar'], true, QUERY_READ_FROM_NODE); + query(0, ['foo', 'bar'], true, QUERY_READ_FROM_NODE(TemplateRef, ElementRef)); } if (rf & RenderFlags.Update) { let tmp: any; @@ -2086,7 +2098,7 @@ describe('query', () => { 6, 0, [WithContentDirective], [], function(rf: RenderFlags, ctx: any) { if (rf & RenderFlags.Create) { - query(0, ['bar'], true, QUERY_READ_FROM_NODE); + query(0, ['bar'], true, QUERY_READ_FROM_NODE(TemplateRef, ElementRef)); } if (rf & RenderFlags.Update) { let tmp: any; @@ -2110,7 +2122,8 @@ describe('query', () => { contentQueries: () => { // @ContentChildren('foo, bar, baz', {descendants: true}) fooBars: // QueryList; - registerContentQuery(query(null, ['foo', 'bar', 'baz'], true, QUERY_READ_FROM_NODE)); + registerContentQuery(query( + null, ['foo', 'bar', 'baz'], true, QUERY_READ_FROM_NODE(TemplateRef, ElementRef))); }, contentQueriesRefresh: (dirIndex: number, queryStartIdx: number) => { let tmp: any; @@ -2174,7 +2187,8 @@ describe('query', () => { contentQueries: () => { // @ContentChildren('foo, bar, baz', {descendants: true}) fooBars: // QueryList; - registerContentQuery(query(null, ['foo'], false, QUERY_READ_FROM_NODE)); + registerContentQuery( + query(null, ['foo'], false, QUERY_READ_FROM_NODE(TemplateRef, ElementRef))); }, contentQueriesRefresh: (dirIndex: number, queryStartIdx: number) => { let tmp: any; @@ -2227,7 +2241,8 @@ describe('query', () => { factory: () => new ShallowQueryDirective(), contentQueries: () => { // @ContentChildren('foo', {descendants: false}) foos: QueryList; - registerContentQuery(query(null, ['foo'], false, QUERY_READ_FROM_NODE)); + registerContentQuery( + query(null, ['foo'], false, QUERY_READ_FROM_NODE(TemplateRef, ElementRef))); }, contentQueriesRefresh: (dirIndex: number, queryStartIdx: number) => { let tmp: any; @@ -2247,7 +2262,8 @@ describe('query', () => { factory: () => new DeepQueryDirective(), contentQueries: () => { // @ContentChildren('foo', {descendants: false}) foos: QueryList; - registerContentQuery(query(null, ['foo'], true, QUERY_READ_FROM_NODE)); + registerContentQuery( + query(null, ['foo'], true, QUERY_READ_FROM_NODE(TemplateRef, ElementRef))); }, contentQueriesRefresh: (dirIndex: number, queryStartIdx: number) => { let tmp: any; diff --git a/packages/core/test/render3/render_util.ts b/packages/core/test/render3/render_util.ts index 71b3e96aab..0582ad8c9e 100644 --- a/packages/core/test/render3/render_util.ts +++ b/packages/core/test/render3/render_util.ts @@ -6,13 +6,19 @@ * found in the LICENSE file at https://angular.io/license */ +import {ChangeDetectorRef} from '@angular/core/src/change_detection/change_detector_ref'; +import {ElementRef} from '@angular/core/src/linker/element_ref'; +import {TemplateRef} from '@angular/core/src/linker/template_ref'; +import {ViewContainerRef} from '@angular/core/src/linker/view_container_ref'; import {stringifyElement} from '@angular/platform-browser/testing/src/browser_util'; import {Injector} from '../../src/di/injector'; +import {R3_CHANGE_DETECTOR_REF_FACTORY, R3_ELEMENT_REF_FACTORY, R3_TEMPLATE_REF_FACTORY, R3_VIEW_CONTAINER_REF_FACTORY} from '../../src/ivy_switch/runtime/ivy_switch_on'; import {PlayerHandler} from '../../src/render3/animations/interfaces'; import {CreateComponentOptions} from '../../src/render3/component'; import {getContext, isComponentInstance} from '../../src/render3/context_discovery'; import {extractDirectiveDef, extractPipeDef} from '../../src/render3/definition'; +import {NG_ELEMENT_ID} from '../../src/render3/fields'; import {ComponentTemplate, ComponentType, DirectiveDefInternal, DirectiveType, PublicFeature, RenderFlags, defineComponent, defineDirective, renderComponent as _renderComponent, tick} from '../../src/render3/index'; import {renderTemplate} from '../../src/render3/instructions'; import {DirectiveDefList, DirectiveTypesOrFactory, PipeDefInternal, PipeDefList, PipeTypesOrFactory} from '../../src/render3/interfaces/definition'; @@ -208,6 +214,10 @@ function toDefs( beforeEach(resetDOM); +// This is necessary so we can switch between the Render2 version and the Ivy version +// of special objects like ElementRef and TemplateRef. +beforeEach(enableIvyInjectableFactories); + /** * @deprecated use `TemplateFixture` or `ComponentFixture` */ @@ -285,3 +295,17 @@ export function createDirective( export const renderer: Renderer3 = null as any as Document; export const element: RElement = null as any as HTMLElement; export const text: RText = null as any as Text; + + +/** + * Switches between Render2 version of special objects like ElementRef and the Ivy version + * of these objects. It's necessary to keep them separate so that we don't pull in fns + * like injectElementRef() prematurely. + */ +export function enableIvyInjectableFactories() { + (ElementRef as any)[NG_ELEMENT_ID] = () => R3_ELEMENT_REF_FACTORY(ElementRef); + (TemplateRef as any)[NG_ELEMENT_ID] = () => R3_TEMPLATE_REF_FACTORY(TemplateRef, ElementRef); + (ViewContainerRef as any)[NG_ELEMENT_ID] = () => + R3_VIEW_CONTAINER_REF_FACTORY(ViewContainerRef, ElementRef); + (ChangeDetectorRef as any)[NG_ELEMENT_ID] = () => R3_CHANGE_DETECTOR_REF_FACTORY(); +} diff --git a/packages/core/test/render3/view_container_ref_spec.ts b/packages/core/test/render3/view_container_ref_spec.ts index 64876de9e8..dfa355a5ff 100644 --- a/packages/core/test/render3/view_container_ref_spec.ts +++ b/packages/core/test/render3/view_container_ref_spec.ts @@ -8,11 +8,12 @@ import {Component, ComponentFactoryResolver, ElementRef, EmbeddedViewRef, NgModuleRef, Pipe, PipeTransform, RendererFactory2, TemplateRef, ViewContainerRef, createInjector, defineInjector, ɵAPP_ROOT as APP_ROOT, ɵNgModuleDef as NgModuleDef} from '../../src/core'; import {ViewEncapsulation} from '../../src/metadata'; -import {templateRefExtractor} from '../../src/render3/di'; -import {AttributeMarker, NgOnChangesFeature, defineComponent, defineDirective, definePipe, injectComponentFactoryResolver, injectTemplateRef, injectViewContainerRef} from '../../src/render3/index'; +import {directiveInject} from '../../src/render3/di'; +import {AttributeMarker, NgOnChangesFeature, defineComponent, defineDirective, definePipe, injectComponentFactoryResolver} from '../../src/render3/index'; import {bind, container, containerRefreshEnd, containerRefreshStart, element, elementEnd, elementProperty, elementStart, embeddedViewEnd, embeddedViewStart, interpolation1, interpolation3, loadDirective, nextContext, projection, projectionDef, reference, template, text, textBinding} from '../../src/render3/instructions'; import {RenderFlags} from '../../src/render3/interfaces/definition'; +import {templateRefExtractor} from '../../src/render3/view_engine_compatibility'; import {NgModuleFactory} from '../../src/render3/ng_module_ref'; import {pipe, pipeBind1} from '../../src/render3/pipe'; import {NgForOf} from '../../test/render3/common_with_def'; @@ -23,7 +24,7 @@ import {ComponentFixture, TemplateFixture, createComponent} from './render_util' describe('ViewContainerRef', () => { let directiveInstance: DirectiveWithVCRef|null; - beforeEach(() => { directiveInstance = null; }); + beforeEach(() => directiveInstance = null); class DirectiveWithVCRef { static ngDirectiveDef = defineDirective({ @@ -31,7 +32,7 @@ describe('ViewContainerRef', () => { selectors: [['', 'vcref', '']], factory: () => directiveInstance = new DirectiveWithVCRef( - injectViewContainerRef(), injectComponentFactoryResolver()), + directiveInject(ViewContainerRef as any), injectComponentFactoryResolver()), inputs: {tplRef: 'tplRef'} }); @@ -66,7 +67,9 @@ describe('ViewContainerRef', () => { *

*/ function createTemplate() { - template(0, embeddedTemplate, 1, 1, null, null, ['tplRef', ''], templateRefExtractor); + template( + 0, embeddedTemplate, 1, 1, null, null, ['tplRef', ''], + templateRefExtractor(TemplateRef, ElementRef)); element(2, 'p', ['vcref', '']); } @@ -83,7 +86,9 @@ describe('ViewContainerRef', () => { *
*/ function createTemplate() { - template(0, embeddedTemplate, 1, 1, null, null, ['tplRef', ''], templateRefExtractor); + template( + 0, embeddedTemplate, 1, 1, null, null, ['tplRef', ''], + templateRefExtractor(TemplateRef, ElementRef)); element(2, 'header', ['vcref', '']); element(3, 'footer'); } @@ -119,7 +124,9 @@ describe('ViewContainerRef', () => { *
*/ function createTemplate() { - template(0, embeddedTemplate, 1, 1, null, [], ['tplRef', ''], templateRefExtractor); + template( + 0, embeddedTemplate, 1, 1, null, [], ['tplRef', ''], + templateRefExtractor(TemplateRef, ElementRef)); element(2, 'header-cmp', ['vcref', '']); element(3, 'footer'); } @@ -155,7 +162,9 @@ describe('ViewContainerRef', () => { *
*/ function createTemplate() { - template(0, embeddedTemplate, 1, 1, null, null, ['tplRef', ''], templateRefExtractor); + template( + 0, embeddedTemplate, 1, 1, null, null, ['tplRef', ''], + templateRefExtractor(TemplateRef, ElementRef)); element(2, 'div', ['vcref', '']); element(3, 'div', ['vcref', '']); @@ -186,7 +195,8 @@ describe('ViewContainerRef', () => { */ function createTemplate() { template( - 0, embeddedTemplate, 1, 1, null, ['vcref', ''], ['tplRef', ''], templateRefExtractor); + 0, embeddedTemplate, 1, 1, null, ['vcref', ''], ['tplRef', ''], + templateRefExtractor(TemplateRef, ElementRef)); element(2, 'footer'); } @@ -225,7 +235,8 @@ describe('ViewContainerRef', () => { type: TestDirective, selectors: [['', 'testdir', '']], factory: () => { - const instance = new TestDirective(injectViewContainerRef(), injectTemplateRef()); + const instance = new TestDirective( + directiveInject(ViewContainerRef as any), directiveInject(TemplateRef as any)); directiveInstances.push(instance); @@ -299,8 +310,9 @@ describe('ViewContainerRef', () => { static ngDirectiveDef = defineDirective({ type: TestDirective, selectors: [['', 'testdir', '']], - factory: () => directiveInstance = - new TestDirective(injectViewContainerRef(), injectTemplateRef()) + factory: () => directiveInstance = new TestDirective( + directiveInject(ViewContainerRef as any), + directiveInject(TemplateRef as any)) }); constructor(private _vcRef: ViewContainerRef, private _tplRef: TemplateRef<{}>) {} @@ -455,7 +467,8 @@ describe('ViewContainerRef', () => { template: (rf: RenderFlags, cmp: SomeComponent) => { if (rf & RenderFlags.Create) { template( - 0, SomeComponent_Template_0, 2, 3, null, [], ['foo', ''], templateRefExtractor); + 0, SomeComponent_Template_0, 2, 3, null, [], ['foo', ''], + templateRefExtractor(TemplateRef, ElementRef)); pipe(2, 'starPipe'); element(3, 'child', ['vcref', '']); pipe(4, 'starPipe'); @@ -495,7 +508,7 @@ describe('ViewContainerRef', () => { static ngDirectiveDef = defineDirective({ type: InsertionDir, selectors: [['', 'tplDir', '']], - factory: () => new InsertionDir(injectViewContainerRef()), + factory: () => new InsertionDir(directiveInject(ViewContainerRef as any)), inputs: {tplDir: 'tplDir'} }); } @@ -546,7 +559,9 @@ describe('ViewContainerRef', () => { */ const Parent = createComponent('parent', function(rf: RenderFlags, parent: any) { if (rf & RenderFlags.Create) { - template(0, fooTemplate, 2, 1, null, null, ['foo', ''], templateRefExtractor); + template( + 0, fooTemplate, 2, 1, null, null, ['foo', ''], + templateRefExtractor(TemplateRef, ElementRef)); element(2, 'child'); } @@ -637,7 +652,9 @@ describe('ViewContainerRef', () => { */ const Parent = createComponent('parent', function(rf: RenderFlags, parent: any) { if (rf & RenderFlags.Create) { - template(0, rowTemplate, 3, 2, null, null, ['rowTemplate', ''], templateRefExtractor); + template( + 0, rowTemplate, 3, 2, null, null, ['rowTemplate', ''], + templateRefExtractor(TemplateRef, ElementRef)); element(2, 'loop-comp'); } @@ -651,7 +668,9 @@ describe('ViewContainerRef', () => { function rowTemplate(rf: RenderFlags, ctx: any) { if (rf & RenderFlags.Create) { - template(0, cellTemplate, 2, 3, null, null, ['cellTemplate', ''], templateRefExtractor); + template( + 0, cellTemplate, 2, 3, null, null, ['cellTemplate', ''], + templateRefExtractor(TemplateRef, ElementRef)); element(2, 'loop-comp'); } @@ -1196,7 +1215,9 @@ describe('ViewContainerRef', () => { vars: 2, template: (rf: RenderFlags, cmp: Parent) => { if (rf & RenderFlags.Create) { - template(0, embeddedTemplate, 2, 1, null, null, ['foo', ''], templateRefExtractor); + template( + 0, embeddedTemplate, 2, 1, null, null, ['foo', ''], + templateRefExtractor(TemplateRef, ElementRef)); elementStart(2, 'child'); { elementStart(3, 'header', ['vcref', '']); @@ -1291,7 +1312,8 @@ describe('ViewContainerRef', () => { template: (rf: RenderFlags, cmp: Parent) => { if (rf & RenderFlags.Create) { template( - 0, embeddedTemplate, 2, 1, null, undefined, ['foo', ''], templateRefExtractor); + 0, embeddedTemplate, 2, 1, null, undefined, ['foo', ''], + templateRefExtractor(TemplateRef, ElementRef)); elementStart(2, 'child-with-view'); text(3, 'Before projected'); elementStart(4, 'header', ['vcref', '']); @@ -1375,7 +1397,8 @@ describe('ViewContainerRef', () => { let tplRef: any; if (rf & RenderFlags.Create) { template( - 0, embeddedTemplate, 2, 1, null, null, ['foo', ''], templateRefExtractor); + 0, embeddedTemplate, 2, 1, null, null, ['foo', ''], + templateRefExtractor(TemplateRef, ElementRef)); elementStart(2, 'child-with-selector'); elementStart(3, 'header', ['vcref', '']); text(4, 'blah'); @@ -1428,7 +1451,8 @@ describe('ViewContainerRef', () => { let tplRef: any; if (rf & RenderFlags.Create) { template( - 0, embeddedTemplate, 2, 1, null, null, ['foo', ''], templateRefExtractor); + 0, embeddedTemplate, 2, 1, null, null, ['foo', ''], + templateRefExtractor(TemplateRef, ElementRef)); elementStart(2, 'child-with-selector'); elementStart(3, 'footer', ['vcref', '']); text(4, 'blah'); @@ -1533,7 +1557,8 @@ describe('ViewContainerRef', () => { template: (rf: RenderFlags, cmp: SomeComponent) => { if (rf & RenderFlags.Create) { template( - 0, SomeComponent_Template_0, 1, 1, null, [], ['foo', ''], templateRefExtractor); + 0, SomeComponent_Template_0, 1, 1, null, [], ['foo', ''], + templateRefExtractor(TemplateRef, ElementRef)); element(2, 'hooks', ['vcref', '']); element(3, 'hooks'); }