mirror of
https://github.com/angular/angular
synced 2026-05-24 09:28:37 +00:00
Revert "refactor(core): remove ComponentFactoryResolver & ComponentFactory from the api surface"
This reverts commit 9d76ac8229.
g3 cleanup not complete
This commit is contained in:
parent
4a174b89c8
commit
ecae525970
21 changed files with 364 additions and 95 deletions
|
|
@ -134,6 +134,8 @@ export class ApplicationRef {
|
|||
constructor();
|
||||
attachView(viewRef: ViewRef): void;
|
||||
bootstrap<C>(component: Type<C>, rootSelectorOrNode?: string | any): ComponentRef<C>;
|
||||
// @deprecated
|
||||
bootstrap<C>(componentFactory: ComponentFactory<C>, rootSelectorOrNode?: string | any): ComponentRef<C>;
|
||||
readonly components: ComponentRef<any>[];
|
||||
readonly componentTypes: Type<any>[];
|
||||
destroy(): void;
|
||||
|
|
@ -291,6 +293,31 @@ export interface ComponentDecorator {
|
|||
new (obj: Component): Component;
|
||||
}
|
||||
|
||||
// @public @deprecated
|
||||
export abstract class ComponentFactory<C> {
|
||||
abstract get componentType(): Type<any>;
|
||||
abstract create(injector: Injector, projectableNodes?: any[][], rootSelectorOrNode?: string | any, environmentInjector?: EnvironmentInjector | NgModuleRef<any>, directives?: (Type<unknown> | DirectiveWithBindings<unknown>)[], bindings?: Binding[]): ComponentRef<C>;
|
||||
abstract get inputs(): {
|
||||
propName: string;
|
||||
templateName: string;
|
||||
transform?: (value: any) => any;
|
||||
isSignal: boolean;
|
||||
}[];
|
||||
abstract get ngContentSelectors(): string[];
|
||||
abstract get outputs(): {
|
||||
propName: string;
|
||||
templateName: string;
|
||||
}[];
|
||||
abstract get selector(): string;
|
||||
}
|
||||
|
||||
// @public @deprecated
|
||||
export abstract class ComponentFactoryResolver {
|
||||
// (undocumented)
|
||||
static NULL: ComponentFactoryResolver;
|
||||
abstract resolveComponentFactory<T>(component: Type<T>): ComponentFactory<T>;
|
||||
}
|
||||
|
||||
// @public
|
||||
export interface ComponentMirror<C> {
|
||||
get inputs(): ReadonlyArray<{
|
||||
|
|
@ -2045,6 +2072,8 @@ export abstract class ViewContainerRef {
|
|||
directives?: (Type<unknown> | DirectiveWithBindings<unknown>)[];
|
||||
bindings?: Binding[];
|
||||
}): ComponentRef<C>;
|
||||
// @deprecated
|
||||
abstract createComponent<C>(componentFactory: ComponentFactory<C>, index?: number, injector?: Injector, projectableNodes?: any[][], environmentInjector?: EnvironmentInjector | NgModuleRef<any>, directives?: (Type<unknown> | DirectiveWithBindings<unknown>)[], bindings?: Binding[]): ComponentRef<C>;
|
||||
abstract createEmbeddedView<C>(templateRef: TemplateRef<C>, context?: C, options?: {
|
||||
index?: number;
|
||||
injector?: Injector;
|
||||
|
|
|
|||
|
|
@ -10,13 +10,13 @@ import '../util/ng_hmr_mode';
|
|||
import '../util/ng_jit_mode';
|
||||
import '../util/ng_server_mode';
|
||||
|
||||
import {type Observable, Subject, type Subscription} from 'rxjs';
|
||||
import {map} from 'rxjs/operators';
|
||||
import {
|
||||
getActiveConsumer,
|
||||
setActiveConsumer,
|
||||
getActiveConsumer,
|
||||
setThrowInvalidWriteToSignalError,
|
||||
} from '../../primitives/signals';
|
||||
import {type Observable, Subject, type Subscription} from 'rxjs';
|
||||
import {map} from 'rxjs/operators';
|
||||
|
||||
import {ZONELESS_ENABLED} from '../change_detection/scheduling/zoneless_scheduling';
|
||||
import {Console} from '../console';
|
||||
|
|
@ -25,8 +25,8 @@ import {Injectable} from '../di/injectable';
|
|||
import {InjectionToken} from '../di/injection_token';
|
||||
import {Injector} from '../di/injector';
|
||||
import {EnvironmentInjector, type R3Injector} from '../di/r3_injector';
|
||||
import {INTERNAL_APPLICATION_ERROR_HANDLER} from '../error_handler';
|
||||
import {formatRuntimeError, RuntimeError, RuntimeErrorCode} from '../errors';
|
||||
import {INTERNAL_APPLICATION_ERROR_HANDLER} from '../error_handler';
|
||||
import {Type} from '../interface/type';
|
||||
import {ComponentFactory, ComponentRef} from '../linker/component_factory';
|
||||
import {ComponentFactoryResolver} from '../linker/component_factory_resolver';
|
||||
|
|
@ -44,10 +44,10 @@ import {ViewRef as InternalViewRef} from '../render3/view_ref';
|
|||
import {TESTABILITY} from '../testability/testability';
|
||||
import {NgZone} from '../zone/ng_zone';
|
||||
|
||||
import {ProfilerEvent} from '../../primitives/devtools';
|
||||
import {profiler} from '../render3/profiler';
|
||||
import {isReactiveLViewConsumer} from '../render3/reactive_lview_consumer';
|
||||
import {ProfilerEvent} from '../../primitives/devtools';
|
||||
import {EffectScheduler} from '../render3/reactivity/root_effect_scheduler';
|
||||
import {isReactiveLViewConsumer} from '../render3/reactive_lview_consumer';
|
||||
import {ApplicationInitStatus} from './application_init';
|
||||
import {TracingAction, TracingService, TracingSnapshot} from './tracing';
|
||||
|
||||
|
|
@ -441,13 +441,61 @@ export class ApplicationRef {
|
|||
* While in this example, we are providing reference to a DOM node.
|
||||
*
|
||||
* {@example core/ts/platform/platform.ts region='domNode'}
|
||||
*
|
||||
* @deprecated Passing Component factories as the `Application.bootstrap` function argument is
|
||||
* deprecated. Pass Component Types instead.
|
||||
*/
|
||||
bootstrap<C>(component: Type<C>, rootSelectorOrNode?: string | any): ComponentRef<C> {
|
||||
return this.bootstrapImpl(component, rootSelectorOrNode);
|
||||
bootstrap<C>(
|
||||
componentFactory: ComponentFactory<C>,
|
||||
rootSelectorOrNode?: string | any,
|
||||
): ComponentRef<C>;
|
||||
|
||||
/**
|
||||
* Bootstrap a component onto the element identified by its selector or, optionally, to a
|
||||
* specified element.
|
||||
*
|
||||
* @usageNotes
|
||||
* ### Bootstrap process
|
||||
*
|
||||
* When bootstrapping a component, Angular mounts it onto a target DOM element
|
||||
* and kicks off automatic change detection. The target DOM element can be
|
||||
* provided using the `rootSelectorOrNode` argument.
|
||||
*
|
||||
* If the target DOM element is not provided, Angular tries to find one on a page
|
||||
* using the `selector` of the component that is being bootstrapped
|
||||
* (first matched element is used).
|
||||
*
|
||||
* ### Example
|
||||
*
|
||||
* Generally, we define the component to bootstrap in the `bootstrap` array of `NgModule`,
|
||||
* but it requires us to know the component while writing the application code.
|
||||
*
|
||||
* Imagine a situation where we have to wait for an API call to decide about the component to
|
||||
* bootstrap. We can use the `ngDoBootstrap` hook of the `NgModule` and call this method to
|
||||
* dynamically bootstrap a component.
|
||||
*
|
||||
* {@example core/ts/platform/platform.ts region='componentSelector'}
|
||||
*
|
||||
* Optionally, a component can be mounted onto a DOM element that does not match the
|
||||
* selector of the bootstrapped component.
|
||||
*
|
||||
* In the following example, we are providing a CSS selector to match the target element.
|
||||
*
|
||||
* {@example core/ts/platform/platform.ts region='cssSelector'}
|
||||
*
|
||||
* While in this example, we are providing reference to a DOM node.
|
||||
*
|
||||
* {@example core/ts/platform/platform.ts region='domNode'}
|
||||
*/
|
||||
bootstrap<C>(
|
||||
componentOrFactory: ComponentFactory<C> | Type<C>,
|
||||
rootSelectorOrNode?: string | any,
|
||||
): ComponentRef<C> {
|
||||
return this.bootstrapImpl(componentOrFactory, rootSelectorOrNode);
|
||||
}
|
||||
|
||||
private bootstrapImpl<C>(
|
||||
component: Type<C>,
|
||||
componentOrFactory: ComponentFactory<C> | Type<C>,
|
||||
rootSelectorOrNode?: string | any,
|
||||
injector: Injector = Injector.NULL,
|
||||
): ComponentRef<C> {
|
||||
|
|
@ -456,12 +504,13 @@ export class ApplicationRef {
|
|||
profiler(ProfilerEvent.BootstrapComponentStart);
|
||||
|
||||
(typeof ngDevMode === 'undefined' || ngDevMode) && warnIfDestroyed(this._destroyed);
|
||||
const isComponentFactory = componentOrFactory instanceof ComponentFactory;
|
||||
const initStatus = this._injector.get(ApplicationInitStatus);
|
||||
|
||||
if (!initStatus.done) {
|
||||
let errorMessage = '';
|
||||
if (typeof ngDevMode === 'undefined' || ngDevMode) {
|
||||
const standalone = isStandalone(component);
|
||||
const standalone = !isComponentFactory && isStandalone(componentOrFactory);
|
||||
errorMessage =
|
||||
'Cannot bootstrap as there are still asynchronous initializers running.' +
|
||||
(standalone
|
||||
|
|
@ -471,8 +520,13 @@ export class ApplicationRef {
|
|||
throw new RuntimeError(RuntimeErrorCode.ASYNC_INITIALIZERS_STILL_RUNNING, errorMessage);
|
||||
}
|
||||
|
||||
const resolver = this._injector.get(ComponentFactoryResolver);
|
||||
const componentFactory = resolver.resolveComponentFactory(component)!;
|
||||
let componentFactory: ComponentFactory<C>;
|
||||
if (isComponentFactory) {
|
||||
componentFactory = componentOrFactory;
|
||||
} else {
|
||||
const resolver = this._injector.get(ComponentFactoryResolver);
|
||||
componentFactory = resolver.resolveComponentFactory(componentOrFactory)!;
|
||||
}
|
||||
this.componentTypes.push(componentFactory.componentType);
|
||||
|
||||
// Create a factory associated with the current module if it's not bound to some other
|
||||
|
|
|
|||
|
|
@ -14,8 +14,8 @@ export {
|
|||
CompilerOptions,
|
||||
ModuleWithComponentFactories,
|
||||
} from './linker/compiler';
|
||||
export {ComponentRef, ComponentFactory as ɵComponentFactory} from './linker/component_factory';
|
||||
export {ComponentFactoryResolver as ɵComponentFactoryResolver} from './linker/component_factory_resolver';
|
||||
export {ComponentFactory, ComponentRef} from './linker/component_factory';
|
||||
export {ComponentFactoryResolver} from './linker/component_factory_resolver';
|
||||
export {DestroyRef} from './linker/destroy_ref';
|
||||
export {ElementRef} from './linker/element_ref';
|
||||
export {NgModuleFactory, NgModuleRef} from './linker/ng_module_factory';
|
||||
|
|
|
|||
|
|
@ -85,6 +85,11 @@ export abstract class ComponentRef<C> {
|
|||
* Base class for a factory that can create a component dynamically.
|
||||
* Instantiate a factory for a given type of component with `resolveComponentFactory()`.
|
||||
* Use the resulting `ComponentFactory.create()` method to create a component of that type.
|
||||
*
|
||||
* @publicApi
|
||||
*
|
||||
* @deprecated Angular no longer requires Component factories. Please use other APIs where
|
||||
* Component class can be used directly.
|
||||
*/
|
||||
export abstract class ComponentFactory<C> {
|
||||
/**
|
||||
|
|
|
|||
|
|
@ -32,6 +32,11 @@ class _NullComponentFactoryResolver implements ComponentFactoryResolver {
|
|||
* Note: since v13, dynamic component creation via
|
||||
* [`ViewContainerRef.createComponent`](api/core/ViewContainerRef#createComponent)
|
||||
* does **not** require resolving component factory: component class can be used directly.
|
||||
*
|
||||
* @publicApi
|
||||
*
|
||||
* @deprecated Angular no longer requires Component factories. Please use other APIs where
|
||||
* Component class can be used directly.
|
||||
*/
|
||||
export abstract class ComponentFactoryResolver {
|
||||
static NULL: ComponentFactoryResolver = /* @__PURE__ */ new _NullComponentFactoryResolver();
|
||||
|
|
|
|||
|
|
@ -17,7 +17,7 @@ import {
|
|||
markRNodeAsClaimedByHydration,
|
||||
} from '../hydration/utils';
|
||||
import {findMatchingDehydratedView, locateDehydratedViewsInContainer} from '../hydration/views';
|
||||
import {Type} from '../interface/type';
|
||||
import {isType, Type} from '../interface/type';
|
||||
import {assertNodeInjector} from '../render3/assert';
|
||||
import {ComponentFactory as R3ComponentFactory} from '../render3/component_ref';
|
||||
import {getComponentDef} from '../render3/def_getters';
|
||||
|
|
@ -74,7 +74,7 @@ import {RuntimeError, RuntimeErrorCode} from '../errors';
|
|||
import {Binding, DirectiveWithBindings} from '../render3/dynamic_bindings';
|
||||
import {addToEndOfViewTree} from '../render3/view/construction';
|
||||
import {addLViewToLContainer, createLContainer, detachView} from '../render3/view/container';
|
||||
import {ComponentRef} from './component_factory';
|
||||
import {ComponentFactory, ComponentRef} from './component_factory';
|
||||
import {createElementRef, ElementRef} from './element_ref';
|
||||
import {NgModuleRef} from './ng_module_factory';
|
||||
import {TemplateRef} from './template_ref';
|
||||
|
|
@ -244,6 +244,36 @@ export abstract class ViewContainerRef {
|
|||
},
|
||||
): ComponentRef<C>;
|
||||
|
||||
/**
|
||||
* Instantiates a single component and inserts its host view into this container.
|
||||
*
|
||||
* @param componentFactory Component factory to use.
|
||||
* @param index The index at which to insert the new component's host view into this container.
|
||||
* If not specified, appends the new view as the last entry.
|
||||
* @param injector The injector to use as the parent for the new component.
|
||||
* @param projectableNodes List of DOM nodes that should be projected through
|
||||
* [`<ng-content>`](api/core/ng-content) of the new component instance.
|
||||
* @param ngModuleRef An instance of the NgModuleRef that represent an NgModule.
|
||||
* This information is used to retrieve corresponding NgModule injector.
|
||||
* @param directives Directives that should be applied to the component.
|
||||
* @param bindings Bindings that should be applied to the component.
|
||||
*
|
||||
* @returns The new `ComponentRef` which contains the component instance and the host view.
|
||||
*
|
||||
* @deprecated Angular no longer requires component factories to dynamically create components.
|
||||
* Use different signature of the `createComponent` method, which allows passing
|
||||
* Component class directly.
|
||||
*/
|
||||
abstract createComponent<C>(
|
||||
componentFactory: ComponentFactory<C>,
|
||||
index?: number,
|
||||
injector?: Injector,
|
||||
projectableNodes?: any[][],
|
||||
environmentInjector?: EnvironmentInjector | NgModuleRef<any>,
|
||||
directives?: (Type<unknown> | DirectiveWithBindings<unknown>)[],
|
||||
bindings?: Binding[],
|
||||
): ComponentRef<C>;
|
||||
|
||||
/**
|
||||
* Inserts a view into this container.
|
||||
* @param viewRef The view to insert.
|
||||
|
|
@ -405,63 +435,102 @@ class R3ViewContainerRef extends ViewContainerRef {
|
|||
bindings?: Binding[];
|
||||
},
|
||||
): ComponentRef<C>;
|
||||
/**
|
||||
* @deprecated Angular no longer requires component factories to dynamically create components.
|
||||
* Use different signature of the `createComponent` method, which allows passing
|
||||
* Component class directly.
|
||||
*/
|
||||
override createComponent<C>(
|
||||
componentType: Type<C>,
|
||||
opts?: {
|
||||
index?: number;
|
||||
injector?: Injector;
|
||||
ngModuleRef?: NgModuleRef<unknown>;
|
||||
environmentInjector?: EnvironmentInjector | NgModuleRef<unknown>;
|
||||
projectableNodes?: Node[][];
|
||||
directives?: (Type<unknown> | DirectiveWithBindings<unknown>)[];
|
||||
bindings?: Binding[];
|
||||
},
|
||||
componentFactory: ComponentFactory<C>,
|
||||
index?: number | undefined,
|
||||
injector?: Injector | undefined,
|
||||
projectableNodes?: any[][] | undefined,
|
||||
environmentInjector?: EnvironmentInjector | NgModuleRef<any> | undefined,
|
||||
directives?: (Type<unknown> | DirectiveWithBindings<unknown>)[],
|
||||
bindings?: Binding[],
|
||||
): ComponentRef<C>;
|
||||
override createComponent<C>(
|
||||
componentFactoryOrType: ComponentFactory<C> | Type<C>,
|
||||
indexOrOptions?:
|
||||
| number
|
||||
| undefined
|
||||
| {
|
||||
index?: number;
|
||||
injector?: Injector;
|
||||
ngModuleRef?: NgModuleRef<unknown>;
|
||||
environmentInjector?: EnvironmentInjector | NgModuleRef<unknown>;
|
||||
projectableNodes?: Node[][];
|
||||
directives?: (Type<unknown> | DirectiveWithBindings<unknown>)[];
|
||||
bindings?: Binding[];
|
||||
},
|
||||
injector?: Injector | undefined,
|
||||
projectableNodes?: any[][] | undefined,
|
||||
environmentInjector?: EnvironmentInjector | NgModuleRef<any> | undefined,
|
||||
directives?: (Type<unknown> | DirectiveWithBindings<unknown>)[],
|
||||
bindings?: Binding[],
|
||||
): ComponentRef<C> {
|
||||
const isComponentFactory = componentFactoryOrType && !isType(componentFactoryOrType);
|
||||
let index: number | undefined;
|
||||
|
||||
if (ngDevMode) {
|
||||
assertDefined(
|
||||
getComponentDef(componentType),
|
||||
`Provided Component class doesn't contain Component definition. ` +
|
||||
`Please check whether provided class has @Component decorator.`,
|
||||
);
|
||||
assertEqual(
|
||||
typeof opts !== 'number',
|
||||
true,
|
||||
'It looks like Component type was provided as the first argument ' +
|
||||
"and a number (representing an index at which to insert the new component's " +
|
||||
'host view into this container as the second argument. This combination of arguments ' +
|
||||
'is incompatible. Please use an object as the second argument instead.',
|
||||
);
|
||||
// This function supports 2 signatures and we need to handle options correctly for both:
|
||||
// 1. When first argument is a Component type. This signature also requires extra
|
||||
// options to be provided as object (more ergonomic option).
|
||||
// 2. First argument is a Component factory. In this case extra options are represented as
|
||||
// positional arguments. This signature is less ergonomic and will be deprecated.
|
||||
if (isComponentFactory) {
|
||||
if (ngDevMode) {
|
||||
assertEqual(
|
||||
typeof indexOrOptions !== 'object',
|
||||
true,
|
||||
'It looks like Component factory was provided as the first argument ' +
|
||||
'and an options object as the second argument. This combination of arguments ' +
|
||||
'is incompatible. You can either change the first argument to provide Component ' +
|
||||
'type or change the second argument to be a number (representing an index at ' +
|
||||
"which to insert the new component's host view into this container)",
|
||||
);
|
||||
}
|
||||
index = indexOrOptions as number | undefined;
|
||||
} else {
|
||||
if (ngDevMode) {
|
||||
assertDefined(
|
||||
getComponentDef(componentFactoryOrType),
|
||||
`Provided Component class doesn't contain Component definition. ` +
|
||||
`Please check whether provided class has @Component decorator.`,
|
||||
);
|
||||
assertEqual(
|
||||
typeof indexOrOptions !== 'number',
|
||||
true,
|
||||
'It looks like Component type was provided as the first argument ' +
|
||||
"and a number (representing an index at which to insert the new component's " +
|
||||
'host view into this container as the second argument. This combination of arguments ' +
|
||||
'is incompatible. Please use an object as the second argument instead.',
|
||||
);
|
||||
}
|
||||
const options = (indexOrOptions || {}) as {
|
||||
index?: number;
|
||||
injector?: Injector;
|
||||
ngModuleRef?: NgModuleRef<unknown>;
|
||||
environmentInjector?: EnvironmentInjector | NgModuleRef<unknown>;
|
||||
projectableNodes?: Node[][];
|
||||
directives?: (Type<unknown> | DirectiveWithBindings<unknown>)[];
|
||||
bindings?: Binding[];
|
||||
};
|
||||
if (ngDevMode && options.environmentInjector && options.ngModuleRef) {
|
||||
throwError(
|
||||
`Cannot pass both environmentInjector and ngModuleRef options to createComponent().`,
|
||||
);
|
||||
}
|
||||
index = options.index;
|
||||
injector = options.injector;
|
||||
projectableNodes = options.projectableNodes;
|
||||
environmentInjector = options.environmentInjector || options.ngModuleRef;
|
||||
directives = options.directives;
|
||||
bindings = options.bindings;
|
||||
}
|
||||
const options = (opts || {}) as {
|
||||
index?: number;
|
||||
injector?: Injector;
|
||||
ngModuleRef?: NgModuleRef<unknown>;
|
||||
environmentInjector?: EnvironmentInjector | NgModuleRef<unknown>;
|
||||
projectableNodes?: Node[][];
|
||||
directives?: (Type<unknown> | DirectiveWithBindings<unknown>)[];
|
||||
bindings?: Binding[];
|
||||
};
|
||||
|
||||
if (ngDevMode && options.environmentInjector && options.ngModuleRef) {
|
||||
throwError(
|
||||
`Cannot pass both environmentInjector and ngModuleRef options to createComponent().`,
|
||||
);
|
||||
}
|
||||
index = options.index;
|
||||
injector = options.injector;
|
||||
projectableNodes = options.projectableNodes;
|
||||
environmentInjector = options.environmentInjector || options.ngModuleRef;
|
||||
directives = options.directives;
|
||||
bindings = options.bindings;
|
||||
|
||||
const componentFactory = new R3ComponentFactory(getComponentDef(componentType)!);
|
||||
const componentFactory: ComponentFactory<C> = isComponentFactory
|
||||
? (componentFactoryOrType as ComponentFactory<C>)
|
||||
: new R3ComponentFactory(getComponentDef(componentFactoryOrType)!);
|
||||
const contextInjector = injector || this.parentInjector;
|
||||
|
||||
// If an `NgModuleRef` is not provided explicitly, try retrieving it from the DI tree.
|
||||
|
|
@ -482,7 +551,7 @@ class R3ViewContainerRef extends ViewContainerRef {
|
|||
// NgModule outside of a module tree). Instead, we always use `ViewContainerRef`'s parent
|
||||
// injector, which is normally connected to the DI tree, which includes module injector
|
||||
// subtree.
|
||||
const _injector = this.parentInjector;
|
||||
const _injector = isComponentFactory ? contextInjector : this.parentInjector;
|
||||
|
||||
// DO NOT REFACTOR. The code here used to have a `injector.get(NgModuleRef, null) ||
|
||||
// undefined` expression which seems to cause internal google apps to fail. This is documented
|
||||
|
|
@ -509,7 +578,7 @@ class R3ViewContainerRef extends ViewContainerRef {
|
|||
index,
|
||||
shouldAddViewToDom(this._hostTNode, dehydratedView),
|
||||
);
|
||||
return componentRef as ComponentRef<C>;
|
||||
return componentRef;
|
||||
}
|
||||
|
||||
override insert(viewRef: ViewRef, index?: number): ViewRef {
|
||||
|
|
|
|||
|
|
@ -13,7 +13,6 @@ import {By, DomSanitizer} from '@angular/platform-browser';
|
|||
import {expect} from '@angular/private/testing/matchers';
|
||||
import {ANIMATION_QUEUE} from '../../src/animation/queue';
|
||||
import {
|
||||
ChangeDetectionStrategy,
|
||||
ChangeDetectorRef,
|
||||
Compiler,
|
||||
Component,
|
||||
|
|
@ -48,6 +47,7 @@ import {
|
|||
ViewChildren,
|
||||
ViewContainerRef,
|
||||
ɵsetDocument,
|
||||
ChangeDetectionStrategy,
|
||||
} from '../../src/core';
|
||||
import {ComponentFixture, TestBed, TestComponentRenderer} from '../../testing';
|
||||
|
||||
|
|
@ -1671,6 +1671,43 @@ describe('ViewContainerRef', () => {
|
|||
componentRef.destroy();
|
||||
});
|
||||
|
||||
it('should be compatible with componentRef generated via TestBed.createComponent in component factory', () => {
|
||||
@Component({
|
||||
selector: 'child',
|
||||
template: `Child Component`,
|
||||
standalone: false,
|
||||
|
||||
changeDetection: ChangeDetectionStrategy.Eager,
|
||||
})
|
||||
class Child {}
|
||||
|
||||
@Component({
|
||||
selector: 'comp',
|
||||
template: '<ng-template #ref></ng-template>',
|
||||
standalone: false,
|
||||
|
||||
changeDetection: ChangeDetectionStrategy.Eager,
|
||||
})
|
||||
class Comp {
|
||||
@ViewChild('ref', {read: ViewContainerRef, static: true})
|
||||
viewContainerRef!: ViewContainerRef;
|
||||
|
||||
ngOnInit() {
|
||||
const makeComponentFactory = (componentType: any) => ({
|
||||
create: () => TestBed.createComponent(componentType).componentRef,
|
||||
});
|
||||
this.viewContainerRef.createComponent(makeComponentFactory(Child) as any);
|
||||
}
|
||||
}
|
||||
|
||||
TestBed.configureTestingModule({declarations: [Comp, Child]});
|
||||
|
||||
const fixture = TestBed.createComponent(Comp);
|
||||
fixture.detectChanges();
|
||||
|
||||
expect(fixture.debugElement.nativeElement.innerHTML).toContain('Child Component');
|
||||
});
|
||||
|
||||
it('should return ComponentRef with ChangeDetectorRef attached to root view', () => {
|
||||
@Component({
|
||||
selector: 'dynamic-cmp',
|
||||
|
|
|
|||
|
|
@ -19,9 +19,11 @@ import {
|
|||
APP_BOOTSTRAP_LISTENER,
|
||||
APP_INITIALIZER,
|
||||
ChangeDetectionStrategy,
|
||||
Compiler,
|
||||
Component,
|
||||
DestroyRef,
|
||||
EnvironmentInjector,
|
||||
InjectionToken,
|
||||
Injector,
|
||||
LOCALE_ID,
|
||||
NgModule,
|
||||
|
|
@ -114,6 +116,62 @@ describe('bootstrap', () => {
|
|||
return MyModule;
|
||||
}
|
||||
|
||||
it('should bootstrap a component from a child module', waitForAsync(
|
||||
inject([ApplicationRef, Compiler], (app: ApplicationRef, compiler: Compiler) => {
|
||||
@Component({
|
||||
selector: 'bootstrap-app',
|
||||
template: '',
|
||||
standalone: false,
|
||||
})
|
||||
class SomeComponent {}
|
||||
|
||||
const helloToken = new InjectionToken<string>('hello');
|
||||
|
||||
@NgModule({
|
||||
providers: [{provide: helloToken, useValue: 'component'}],
|
||||
declarations: [SomeComponent],
|
||||
})
|
||||
class SomeModule {}
|
||||
|
||||
createRootEl();
|
||||
const modFactory = compiler.compileModuleSync(SomeModule);
|
||||
const module = modFactory.create(TestBed.inject(Injector));
|
||||
const cmpFactory = module.componentFactoryResolver.resolveComponentFactory(SomeComponent);
|
||||
const component = app.bootstrap(cmpFactory);
|
||||
|
||||
// The component should see the child module providers
|
||||
expect(component.injector.get(helloToken)).toEqual('component');
|
||||
}),
|
||||
));
|
||||
|
||||
it('should bootstrap a component with a custom selector', waitForAsync(
|
||||
inject([ApplicationRef, Compiler], (app: ApplicationRef, compiler: Compiler) => {
|
||||
@Component({
|
||||
selector: 'bootstrap-app',
|
||||
template: '',
|
||||
standalone: false,
|
||||
})
|
||||
class SomeComponent {}
|
||||
|
||||
const helloToken = new InjectionToken<string>('hello');
|
||||
|
||||
@NgModule({
|
||||
providers: [{provide: helloToken, useValue: 'component'}],
|
||||
declarations: [SomeComponent],
|
||||
})
|
||||
class SomeModule {}
|
||||
|
||||
createRootEl('custom-selector');
|
||||
const modFactory = compiler.compileModuleSync(SomeModule);
|
||||
const module = modFactory.create(TestBed.inject(Injector));
|
||||
const cmpFactory = module.componentFactoryResolver.resolveComponentFactory(SomeComponent);
|
||||
const component = app.bootstrap(cmpFactory, 'custom-selector');
|
||||
|
||||
// The component should see the child module providers
|
||||
expect(component.injector.get(helloToken)).toEqual('component');
|
||||
}),
|
||||
));
|
||||
|
||||
describe('ApplicationRef', () => {
|
||||
beforeEach(async () => {
|
||||
TestBed.configureTestingModule({
|
||||
|
|
|
|||
|
|
@ -543,6 +543,7 @@
|
|||
"isSubscriber",
|
||||
"isSubscription",
|
||||
"isTemplateNode",
|
||||
"isType",
|
||||
"isTypeProvider",
|
||||
"isValidLink",
|
||||
"isValueProvider",
|
||||
|
|
|
|||
|
|
@ -783,6 +783,7 @@
|
|||
"isSubscriber",
|
||||
"isSubscription",
|
||||
"isTemplateNode",
|
||||
"isType",
|
||||
"isTypeProvider",
|
||||
"isValidLink",
|
||||
"isValidatorFn",
|
||||
|
|
|
|||
|
|
@ -777,6 +777,7 @@
|
|||
"isSubscriber",
|
||||
"isSubscription",
|
||||
"isTemplateNode",
|
||||
"isType",
|
||||
"isTypeProvider",
|
||||
"isValidLink",
|
||||
"isValidatorFn",
|
||||
|
|
|
|||
|
|
@ -864,6 +864,7 @@
|
|||
"isSubscriber",
|
||||
"isSubscription",
|
||||
"isTemplateNode",
|
||||
"isType",
|
||||
"isTypeProvider",
|
||||
"isUrlTree",
|
||||
"isValidLink",
|
||||
|
|
|
|||
|
|
@ -18,6 +18,7 @@ import {
|
|||
Attribute,
|
||||
Compiler,
|
||||
Component,
|
||||
ComponentFactory,
|
||||
ComponentRef,
|
||||
ContentChildren,
|
||||
createComponent,
|
||||
|
|
@ -1126,13 +1127,14 @@ describe('integration tests', function () {
|
|||
imports: [RootModule],
|
||||
}).createComponent(RootComp);
|
||||
const compiler = TestBed.inject(Compiler);
|
||||
const myCompFactory =
|
||||
compiler.compileModuleAndAllComponentsSync(MyModule).componentFactories[0];
|
||||
const myCompFactory = <ComponentFactory<MyComp>>(
|
||||
compiler.compileModuleAndAllComponentsSync(MyModule).componentFactories[0]
|
||||
);
|
||||
|
||||
// Note: the ComponentFactory was created directly via the compiler, i.e. it
|
||||
// does not have an association to an NgModuleRef.
|
||||
// -> expect the providers of the module that the view container belongs to.
|
||||
const compRef = myCompFactory.create(compFixture.componentInstance.vc.injector);
|
||||
const compRef = compFixture.componentInstance.vc.createComponent(myCompFactory);
|
||||
expect(compRef.instance.someToken).toBe('someRootValue');
|
||||
});
|
||||
|
||||
|
|
@ -1220,7 +1222,7 @@ describe('integration tests', function () {
|
|||
// Note: MyComp was declared as entryComponent in MyModule,
|
||||
// and we don't pass an explicit ModuleRef to the createComponent call.
|
||||
// -> expect the providers of MyModule!
|
||||
const compRef = myCompFactory.create(compFixture.componentInstance.vc.injector);
|
||||
const compRef = compFixture.componentInstance.vc.createComponent(myCompFactory);
|
||||
expect(compRef.instance.someToken).toBe('someValue');
|
||||
});
|
||||
});
|
||||
|
|
|
|||
|
|
@ -6,9 +6,9 @@
|
|||
* found in the LICENSE file at https://angular.dev/license
|
||||
*/
|
||||
|
||||
import {getActiveConsumer} from '../../primitives/signals';
|
||||
import {
|
||||
Component,
|
||||
ComponentFactoryResolver,
|
||||
createComponent,
|
||||
createEnvironmentInjector,
|
||||
effect,
|
||||
|
|
@ -24,6 +24,7 @@ import {
|
|||
ViewChild,
|
||||
ViewContainerRef,
|
||||
} from '../../src/core';
|
||||
import {getActiveConsumer} from '../../primitives/signals';
|
||||
import {createInjector} from '../../src/di/create_injector';
|
||||
import {TestBed} from '../../testing';
|
||||
|
||||
|
|
@ -87,10 +88,10 @@ describe('reactive safety', () => {
|
|||
}
|
||||
}
|
||||
|
||||
const environmentInjector = TestBed.inject(EnvironmentInjector);
|
||||
expectNotToThrowInReactiveContext(() => {
|
||||
createComponent(TestCmp, {environmentInjector});
|
||||
});
|
||||
const injector = TestBed.inject(EnvironmentInjector);
|
||||
const resolver = TestBed.inject(ComponentFactoryResolver);
|
||||
const factory = resolver.resolveComponentFactory(TestCmp);
|
||||
expectNotToThrowInReactiveContext(() => factory.create(injector));
|
||||
});
|
||||
|
||||
it('should be safe to flip @if to true', () => {
|
||||
|
|
|
|||
|
|
@ -11,19 +11,19 @@ import type {} from 'zone.js';
|
|||
|
||||
import {
|
||||
ApplicationRef,
|
||||
ɵChangeDetectionScheduler as ChangeDetectionScheduler,
|
||||
ɵComponentFactory as ComponentFactory,
|
||||
ɵComponentFactoryResolver as ComponentFactoryResolver,
|
||||
ComponentFactory,
|
||||
ComponentFactoryResolver,
|
||||
ComponentRef,
|
||||
EventEmitter,
|
||||
Injector,
|
||||
NgZone,
|
||||
Type,
|
||||
ɵChangeDetectionScheduler as ChangeDetectionScheduler,
|
||||
ɵNotificationSource as NotificationSource,
|
||||
ɵViewRef as ViewRef,
|
||||
ɵisViewDirty as isViewDirty,
|
||||
ɵmarkForRefresh as markForRefresh,
|
||||
NgZone,
|
||||
ɵNotificationSource as NotificationSource,
|
||||
OutputRef,
|
||||
Type,
|
||||
ɵViewRef as ViewRef,
|
||||
} from '@angular/core';
|
||||
import {merge, Observable, ReplaySubject} from 'rxjs';
|
||||
import {switchMap} from 'rxjs/operators';
|
||||
|
|
|
|||
|
|
@ -5,7 +5,7 @@
|
|||
* Use of this source code is governed by an MIT-style license that can be
|
||||
* found in the LICENSE file at https://angular.dev/license
|
||||
*/
|
||||
import {ɵComponentFactoryResolver as ComponentFactoryResolver, Injector, Type} from '@angular/core';
|
||||
import {ComponentFactoryResolver, Injector, Type} from '@angular/core';
|
||||
|
||||
/**
|
||||
* Provide methods for scheduling the execution of a callback.
|
||||
|
|
|
|||
|
|
@ -8,7 +8,7 @@
|
|||
|
||||
import {
|
||||
Component,
|
||||
ɵComponentFactoryResolver as ComponentFactoryResolver,
|
||||
ComponentFactoryResolver,
|
||||
destroyPlatform,
|
||||
EventEmitter,
|
||||
Input,
|
||||
|
|
|
|||
|
|
@ -11,6 +11,7 @@ import {
|
|||
ChangeDetectionStrategy,
|
||||
Compiler,
|
||||
Component,
|
||||
ComponentFactoryResolver,
|
||||
CUSTOM_ELEMENTS_SCHEMA,
|
||||
Directive,
|
||||
Inject,
|
||||
|
|
@ -523,6 +524,16 @@ describe('public testing API', () => {
|
|||
});
|
||||
|
||||
describe('overriding providers', () => {
|
||||
describe('in core', () => {
|
||||
it('ComponentFactoryResolver', () => {
|
||||
const componentFactoryMock = jasmine.createSpyObj('componentFactory', [
|
||||
'resolveComponentFactory',
|
||||
]);
|
||||
TestBed.overrideProvider(ComponentFactoryResolver, {useValue: componentFactoryMock});
|
||||
expect(TestBed.inject(ComponentFactoryResolver)).toEqual(componentFactoryMock);
|
||||
});
|
||||
});
|
||||
|
||||
describe('in NgModules', () => {
|
||||
it('should support useValue', () => {
|
||||
TestBed.configureTestingModule({
|
||||
|
|
|
|||
|
|
@ -6,13 +6,7 @@
|
|||
* found in the LICENSE file at https://angular.dev/license
|
||||
*/
|
||||
|
||||
import {
|
||||
ɵComponentFactory as ComponentFactory,
|
||||
ɵComponentFactoryResolver as ComponentFactoryResolver,
|
||||
Injector,
|
||||
NgZone,
|
||||
Type,
|
||||
} from '@angular/core';
|
||||
import {ComponentFactory, ComponentFactoryResolver, Injector, NgZone, Type} from '@angular/core';
|
||||
|
||||
import {
|
||||
IAnnotatedFunction,
|
||||
|
|
|
|||
|
|
@ -9,19 +9,19 @@
|
|||
import {
|
||||
ApplicationRef,
|
||||
ChangeDetectorRef,
|
||||
ɵComponentFactory as ComponentFactory,
|
||||
ComponentFactory,
|
||||
ComponentRef,
|
||||
type EventEmitter,
|
||||
Injector,
|
||||
type ɵInputSignalNode as InputSignalNode,
|
||||
OnChanges,
|
||||
type OutputEmitterRef,
|
||||
ɵSIGNAL as SIGNAL,
|
||||
SimpleChange,
|
||||
SimpleChanges,
|
||||
StaticProvider,
|
||||
Testability,
|
||||
TestabilityRegistry,
|
||||
type OutputEmitterRef,
|
||||
type ɵInputSignalNode as InputSignalNode,
|
||||
ɵSIGNAL as SIGNAL,
|
||||
} from '@angular/core';
|
||||
|
||||
import {
|
||||
|
|
|
|||
|
|
@ -8,7 +8,7 @@
|
|||
import {
|
||||
Compiler,
|
||||
Component,
|
||||
ɵComponentFactory as ComponentFactory,
|
||||
ComponentFactory,
|
||||
Injector,
|
||||
NgModule,
|
||||
TestabilityRegistry,
|
||||
|
|
|
|||
Loading…
Reference in a new issue