fix(ivy): align discovery methods for consistency (#27117)
PR Close #27117
This commit is contained in:
@ -10,8 +10,9 @@ import {TemplateRef, ViewContainerRef} from '@angular/core';
|
||||
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, templateRefExtractor} from '../../src/render3/index';
|
||||
import {whenRendered} from '../../src/render3/component';
|
||||
import {LifecycleHooksFeature, defineComponent, defineDirective, getRenderedText, templateRefExtractor} from '../../src/render3/index';
|
||||
|
||||
import {bind, container, containerRefreshEnd, containerRefreshStart, detectChanges, directiveInject, element, elementEnd, elementProperty, elementStart, embeddedViewEnd, embeddedViewStart, interpolation1, interpolation2, listener, markDirty, reference, text, template, textBinding, tick} from '../../src/render3/instructions';
|
||||
import {RenderFlags} from '../../src/render3/interfaces/definition';
|
||||
import {RElement, Renderer3, RendererFactory3} from '../../src/render3/interfaces/renderer';
|
||||
|
@ -7,9 +7,8 @@
|
||||
*/
|
||||
|
||||
import {ViewEncapsulation, createInjector, defineInjectable, defineInjector} from '../../src/core';
|
||||
import {getRenderedText} from '../../src/render3/component';
|
||||
|
||||
import {AttributeMarker, ComponentFactory, LifecycleHooksFeature, defineComponent, directiveInject, markDirty, template} from '../../src/render3/index';
|
||||
import {AttributeMarker, ComponentFactory, LifecycleHooksFeature, defineComponent, directiveInject, markDirty, template, getRenderedText} from '../../src/render3/index';
|
||||
import {bind, container, containerRefreshEnd, containerRefreshStart, element, elementEnd, elementProperty, elementStart, embeddedViewEnd, embeddedViewStart, nextContext, text, textBinding, tick} from '../../src/render3/instructions';
|
||||
import {ComponentDef, RenderFlags} from '../../src/render3/interfaces/definition';
|
||||
|
||||
|
@ -2019,7 +2019,8 @@ describe('di', () => {
|
||||
describe('getOrCreateNodeInjector', () => {
|
||||
it('should handle initial undefined state', () => {
|
||||
const contentView = createLViewData(
|
||||
null !, createTView(-1, null, 1, 0, null, null, null), null, LViewFlags.CheckAlways);
|
||||
null, null !, createTView(-1, null, 1, 0, null, null, null), null,
|
||||
LViewFlags.CheckAlways);
|
||||
const oldView = enterView(contentView, null);
|
||||
try {
|
||||
const parentTNode = createNodeAtIndex(0, TNodeType.Element, null, null, null);
|
||||
|
@ -5,59 +5,263 @@
|
||||
* 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 {ReferenceFilter} from '@angular/compiler';
|
||||
import {createInjector} from '@angular/core';
|
||||
|
||||
import {StaticInjector} from '../../src/di/injector';
|
||||
import {getComponent, getDirectives, getHostComponent, getInjector, getLocalRefs, getRootComponents} from '../../src/render3/discovery_utils';
|
||||
import {RenderFlags, defineComponent, defineDirective} from '../../src/render3/index';
|
||||
import {element, elementEnd, elementStart, elementStyling, elementStylingApply} from '../../src/render3/instructions';
|
||||
import {getComponent, getDirectives, getInjector, getLocalRefs, getRootComponents, getViewComponent} from '../../src/render3/discovery_utils';
|
||||
import {ProvidersFeature, RenderFlags, defineComponent, defineDirective, getHostElement} from '../../src/render3/index';
|
||||
|
||||
import {container, element, elementEnd, elementStart, elementStyling, elementStylingApply, template, bind, elementProperty, text, textBinding, markDirty} from '../../src/render3/instructions';
|
||||
|
||||
import {ComponentFixture} from './render_util';
|
||||
import {NgIf} from './common_with_def';
|
||||
import {getRootContext} from '@angular/core/src/render3/util';
|
||||
|
||||
describe('discovery utils', () => {
|
||||
describe('getComponent()', () => {
|
||||
it('should return the component instance for a DOM element', () => {
|
||||
class InnerComp {
|
||||
static ngComponentDef = defineComponent({
|
||||
type: InnerComp,
|
||||
selectors: [['inner-comp']],
|
||||
factory: () => new InnerComp(),
|
||||
consts: 1,
|
||||
vars: 0,
|
||||
template: (rf: RenderFlags, ctx: InnerComp) => {
|
||||
let fixture: ComponentFixture<MyApp>;
|
||||
let myApp: MyApp[];
|
||||
let dirA: DirectiveA[];
|
||||
let childComponent: DirectiveA[];
|
||||
let child: NodeListOf<Element>;
|
||||
let span: NodeListOf<Element>;
|
||||
let div: NodeListOf<Element>;
|
||||
let p: NodeListOf<Element>;
|
||||
|
||||
beforeEach(() => {
|
||||
myApp = [];
|
||||
dirA = [];
|
||||
childComponent = [];
|
||||
fixture = new ComponentFixture(
|
||||
MyApp, {injector: createInjector(null, null, [{provide: String, useValue: 'Module'}])});
|
||||
child = fixture.hostElement.querySelectorAll('child');
|
||||
span = fixture.hostElement.querySelectorAll('span');
|
||||
div = fixture.hostElement.querySelectorAll('div');
|
||||
p = fixture.hostElement.querySelectorAll('p');
|
||||
});
|
||||
|
||||
/**
|
||||
* For all tests assume this set up
|
||||
*
|
||||
* ```
|
||||
* <my-app>
|
||||
* <#VIEW>
|
||||
* <span>{{text}}</span>
|
||||
* <div dirA #div #foo="dirA"></div>
|
||||
* <child>
|
||||
* <#VIEW>
|
||||
* <p></p>
|
||||
* <VIEW>
|
||||
* </child>
|
||||
* <child dirA #child>
|
||||
* <#VIEW>
|
||||
* <p></p>
|
||||
* <VIEW>
|
||||
* </child>
|
||||
* <child dirA *ngIf="true">
|
||||
* <#VIEW>
|
||||
* <p></p>
|
||||
* <VIEW>
|
||||
* </child>
|
||||
* </#VIEW>
|
||||
* </my-app>
|
||||
* ```
|
||||
*/
|
||||
class Child {
|
||||
constructor() { childComponent.push(this); }
|
||||
|
||||
static ngComponentDef = defineComponent({
|
||||
type: Child,
|
||||
selectors: [['child']],
|
||||
factory: () => new Child(),
|
||||
consts: 1,
|
||||
vars: 0,
|
||||
template: (rf: RenderFlags, ctx: Child) => {
|
||||
if (rf & RenderFlags.Create) {
|
||||
element(0, 'p');
|
||||
}
|
||||
},
|
||||
features: [ProvidersFeature([{provide: String, useValue: 'Child'}])]
|
||||
});
|
||||
}
|
||||
|
||||
class DirectiveA {
|
||||
constructor() { dirA.push(this); }
|
||||
|
||||
static ngDirectiveDef = defineDirective({
|
||||
type: DirectiveA,
|
||||
selectors: [['', 'dirA', '']],
|
||||
exportAs: 'dirA',
|
||||
factory: () => new DirectiveA(),
|
||||
});
|
||||
}
|
||||
|
||||
class MyApp {
|
||||
text: string = 'INIT';
|
||||
constructor() { myApp.push(this); }
|
||||
|
||||
static ngComponentDef = defineComponent({
|
||||
type: MyApp,
|
||||
selectors: [['my-app']],
|
||||
factory: () => new MyApp(),
|
||||
consts: 9,
|
||||
vars: 1,
|
||||
directives: [Child, DirectiveA, NgIf],
|
||||
template: (rf: RenderFlags, ctx: MyApp) => {
|
||||
if (rf & RenderFlags.Create) {
|
||||
elementStart(0, 'span');
|
||||
text(1);
|
||||
elementEnd();
|
||||
element(2, 'div', ['dirA', ''], ['div', '', 'foo', 'dirA']);
|
||||
element(5, 'child');
|
||||
element(6, 'child', ['dirA', ''], ['child', '']);
|
||||
template(8, function(rf: RenderFlags, ctx: never) {
|
||||
if (rf & RenderFlags.Create) {
|
||||
element(0, 'div');
|
||||
element(0, 'child');
|
||||
}
|
||||
}
|
||||
});
|
||||
}, 1, 0, null, ['ngIf', '']);
|
||||
}
|
||||
if (rf & RenderFlags.Update) {
|
||||
textBinding(1, bind(ctx.text));
|
||||
elementProperty(8, 'ngIf', bind(true));
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
class Comp {
|
||||
static ngComponentDef = defineComponent({
|
||||
type: Comp,
|
||||
selectors: [['comp']],
|
||||
factory: () => new Comp(),
|
||||
consts: 1,
|
||||
vars: 0,
|
||||
template: (rf: RenderFlags, ctx: Comp) => {
|
||||
if (rf & RenderFlags.Create) {
|
||||
element(0, 'inner-comp');
|
||||
}
|
||||
},
|
||||
directives: [InnerComp]
|
||||
});
|
||||
}
|
||||
|
||||
const fixture = new ComponentFixture(Comp);
|
||||
fixture.update();
|
||||
|
||||
const hostElm = fixture.hostElement;
|
||||
const innerCompElm = hostElm.querySelector('inner-comp');
|
||||
const component = fixture.component;
|
||||
|
||||
expect(getComponent(innerCompElm !) !).toBe(component);
|
||||
expect(getComponent(hostElm) !).toBeFalsy();
|
||||
describe('getComponent', () => {
|
||||
it('should return null if no component', () => {
|
||||
expect(getComponent(span[0])).toEqual(null);
|
||||
expect(getComponent(div[0])).toEqual(null);
|
||||
expect(getComponent(p[0])).toEqual(null);
|
||||
});
|
||||
it('should throw when called on non-element', () => {
|
||||
expect(() => getComponent(dirA[0] as any)).toThrowError(/Expecting instance of DOM Node/);
|
||||
expect(() => getComponent(dirA[1] as any)).toThrowError(/Expecting instance of DOM Node/);
|
||||
});
|
||||
it('should return component from element', () => {
|
||||
expect(getComponent<MyApp>(fixture.hostElement)).toEqual(myApp[0]);
|
||||
expect(getComponent<Child>(child[0])).toEqual(childComponent[0]);
|
||||
expect(getComponent<Child>(child[1])).toEqual(childComponent[1]);
|
||||
});
|
||||
});
|
||||
|
||||
describe('getHostElement', () => {
|
||||
it('should return element on component', () => {
|
||||
expect(getHostElement(myApp[0])).toEqual(fixture.hostElement);
|
||||
expect(getHostElement(childComponent[0])).toEqual(child[0]);
|
||||
expect(getHostElement(childComponent[1])).toEqual(child[1]);
|
||||
});
|
||||
it('should return element on directive', () => {
|
||||
expect(getHostElement(dirA[0])).toEqual(div[0]);
|
||||
expect(getHostElement(dirA[1])).toEqual(child[1]);
|
||||
});
|
||||
it('should throw on unknown target', () => {
|
||||
expect(() => getHostElement({})).toThrowError(); //
|
||||
});
|
||||
});
|
||||
|
||||
describe('getInjector', () => {
|
||||
it('should return node-injector from element', () => {
|
||||
expect(getInjector(fixture.hostElement).get(String)).toEqual('Module');
|
||||
expect(getInjector(child[0]).get(String)).toEqual('Child');
|
||||
expect(getInjector(p[0]).get(String)).toEqual('Child');
|
||||
});
|
||||
it('should return node-injector from component with providers', () => {
|
||||
expect(getInjector(myApp[0]).get(String)).toEqual('Module');
|
||||
expect(getInjector(childComponent[0]).get(String)).toEqual('Child');
|
||||
expect(getInjector(childComponent[1]).get(String)).toEqual('Child');
|
||||
});
|
||||
it('should return node-injector from directive without providers', () => {
|
||||
expect(getInjector(dirA[0]).get(String)).toEqual('Module');
|
||||
expect(getInjector(dirA[1]).get(String)).toEqual('Child');
|
||||
});
|
||||
});
|
||||
|
||||
describe('getDirectives', () => {
|
||||
it('should return empty array if no directives', () => {
|
||||
expect(getDirectives(fixture.hostElement)).toEqual([]);
|
||||
expect(getDirectives(span[0])).toEqual([]);
|
||||
expect(getDirectives(child[0])).toEqual([]);
|
||||
});
|
||||
it('should return just directives', () => {
|
||||
expect(getDirectives(div[0])).toEqual([dirA[0]]);
|
||||
expect(getDirectives(child[1])).toEqual([dirA[1]]);
|
||||
});
|
||||
});
|
||||
|
||||
describe('getViewComponent', () => {
|
||||
it('should return null when called on root component', () => {
|
||||
expect(getViewComponent(fixture.hostElement)).toEqual(null);
|
||||
expect(getViewComponent(myApp[0])).toEqual(null);
|
||||
});
|
||||
it('should return containing component of child component', () => {
|
||||
expect(getViewComponent<MyApp>(child[0])).toEqual(myApp[0]);
|
||||
expect(getViewComponent<MyApp>(child[1])).toEqual(myApp[0]);
|
||||
expect(getViewComponent<MyApp>(child[2])).toEqual(myApp[0]);
|
||||
|
||||
expect(getViewComponent<MyApp>(childComponent[0])).toEqual(myApp[0]);
|
||||
expect(getViewComponent<MyApp>(childComponent[1])).toEqual(myApp[0]);
|
||||
expect(getViewComponent<MyApp>(childComponent[2])).toEqual(myApp[0]);
|
||||
});
|
||||
it('should return containing component of any view element', () => {
|
||||
expect(getViewComponent<MyApp>(span[0])).toEqual(myApp[0]);
|
||||
expect(getViewComponent<MyApp>(div[0])).toEqual(myApp[0]);
|
||||
expect(getViewComponent<Child>(p[0])).toEqual(childComponent[0]);
|
||||
expect(getViewComponent<Child>(p[1])).toEqual(childComponent[1]);
|
||||
expect(getViewComponent<Child>(p[2])).toEqual(childComponent[2]);
|
||||
});
|
||||
it('should return containing component of child directive', () => {
|
||||
expect(getViewComponent<MyApp>(dirA[0])).toEqual(myApp[0]);
|
||||
expect(getViewComponent<MyApp>(dirA[1])).toEqual(myApp[0]);
|
||||
});
|
||||
});
|
||||
|
||||
describe('localRefs', () => {
|
||||
it('should retrieve empty map', () => {
|
||||
expect(getLocalRefs(fixture.hostElement)).toEqual({});
|
||||
expect(getLocalRefs(myApp[0])).toEqual({});
|
||||
expect(getLocalRefs(span[0])).toEqual({});
|
||||
expect(getLocalRefs(child[0])).toEqual({});
|
||||
});
|
||||
|
||||
it('should retrieve the local map', () => {
|
||||
expect(getLocalRefs(div[0])).toEqual({div: div[0], foo: dirA[0]});
|
||||
expect(getLocalRefs(dirA[0])).toEqual({div: div[0], foo: dirA[0]});
|
||||
|
||||
expect(getLocalRefs(child[1])).toEqual({child: childComponent[1]});
|
||||
expect(getLocalRefs(dirA[1])).toEqual({child: childComponent[1]});
|
||||
});
|
||||
});
|
||||
|
||||
describe('getRootComponents', () => {
|
||||
it('should return root components from component', () => {
|
||||
const rootComponents = [myApp[0]];
|
||||
expect(getRootComponents(myApp[0])).toEqual(rootComponents);
|
||||
expect(getRootComponents(childComponent[0])).toEqual(rootComponents);
|
||||
expect(getRootComponents(childComponent[1])).toEqual(rootComponents);
|
||||
expect(getRootComponents(dirA[0])).toEqual(rootComponents);
|
||||
expect(getRootComponents(dirA[1])).toEqual(rootComponents);
|
||||
expect(getRootComponents(child[0])).toEqual(rootComponents);
|
||||
expect(getRootComponents(child[1])).toEqual(rootComponents);
|
||||
expect(getRootComponents(div[0])).toEqual(rootComponents);
|
||||
expect(getRootComponents(p[0])).toEqual(rootComponents);
|
||||
});
|
||||
});
|
||||
|
||||
describe('markDirty', () => {
|
||||
it('should re-render component', () => {
|
||||
expect(span[0].textContent).toEqual('INIT');
|
||||
myApp[0].text = 'WORKS';
|
||||
markDirty(myApp[0]);
|
||||
fixture.requestAnimationFrame.flush();
|
||||
expect(span[0].textContent).toEqual('WORKS');
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('discovery utils deprecated', () => {
|
||||
|
||||
describe('getRootComponents()', () => {
|
||||
it('should return a list of the root components of the application from an element', () => {
|
||||
let innerComp: InnerComp;
|
||||
@ -171,55 +375,6 @@ describe('discovery utils', () => {
|
||||
});
|
||||
});
|
||||
|
||||
describe('getHostComponent()', () => {
|
||||
it('should return the component instance for a DOM element', () => {
|
||||
let innerComp: InnerComp;
|
||||
|
||||
class InnerComp {
|
||||
static ngComponentDef = defineComponent({
|
||||
type: InnerComp,
|
||||
selectors: [['inner-comp']],
|
||||
factory: () => innerComp = new InnerComp(),
|
||||
consts: 1,
|
||||
vars: 0,
|
||||
template: (rf: RenderFlags, ctx: InnerComp) => {
|
||||
if (rf & RenderFlags.Create) {
|
||||
element(0, 'div');
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
class Comp {
|
||||
static ngComponentDef = defineComponent({
|
||||
type: Comp,
|
||||
selectors: [['comp']],
|
||||
factory: () => new Comp(),
|
||||
consts: 1,
|
||||
vars: 0,
|
||||
template: (rf: RenderFlags, ctx: Comp) => {
|
||||
if (rf & RenderFlags.Create) {
|
||||
element(0, 'inner-comp');
|
||||
}
|
||||
},
|
||||
directives: [InnerComp]
|
||||
});
|
||||
}
|
||||
|
||||
const fixture = new ComponentFixture(Comp);
|
||||
fixture.update();
|
||||
|
||||
const hostElm = fixture.hostElement;
|
||||
const innerElm = hostElm.querySelector('inner-comp') !;
|
||||
const divElm = hostElm.querySelector('div') !;
|
||||
const component = fixture.component;
|
||||
|
||||
expect(getHostComponent(hostElm) !).toBe(component);
|
||||
expect(getHostComponent(innerElm) !).toBe(innerComp !);
|
||||
expect(getHostComponent(divElm) !).toBeFalsy();
|
||||
});
|
||||
});
|
||||
|
||||
describe('getInjector', () => {
|
||||
|
||||
it('should return an injector that can return directive instances', () => {
|
||||
|
@ -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 {getComponent, getDirectives, getHostComponent, getInjector, getRootComponents} from '../../src/render3/discovery_utils';
|
||||
import {ɵmarkDirty as markDirty} from '@angular/core';
|
||||
|
||||
import {getComponent, getDirectives, getHostElement, getInjector, getRootComponents, getViewComponent} from '../../src/render3/discovery_utils';
|
||||
import {GLOBAL_PUBLISH_EXPANDO_KEY, GlobalDevModeContainer, publishDefaultGlobalUtils, publishGlobalUtil} from '../../src/render3/global_utils';
|
||||
import {getPlayers} from '../../src/render3/players';
|
||||
import {global} from '../../src/util';
|
||||
@ -26,16 +28,21 @@ describe('global utils', () => {
|
||||
|
||||
it('should publish getComponent', () => { assertPublished('getComponent', getComponent); });
|
||||
|
||||
it('should publish getViewComponent',
|
||||
() => { assertPublished('getViewComponent', getViewComponent); });
|
||||
|
||||
it('should publish getRootComponents',
|
||||
() => { assertPublished('getRootComponents', getRootComponents); });
|
||||
|
||||
it('should publish getDirectives', () => { assertPublished('getDirectives', getDirectives); });
|
||||
|
||||
it('should publish getHostComponent',
|
||||
() => { assertPublished('getHostComponent', getHostComponent); });
|
||||
() => { assertPublished('getHostElement', getHostElement); });
|
||||
|
||||
it('should publish getInjector', () => { assertPublished('getInjector', getInjector); });
|
||||
|
||||
it('should publish markDirty', () => { assertPublished('markDirty', markDirty); });
|
||||
|
||||
it('should publish getPlayers', () => { assertPublished('getPlayers', getPlayers); });
|
||||
});
|
||||
});
|
||||
|
@ -20,7 +20,7 @@ import {SWITCH_TEMPLATE_REF_FACTORY__POST_R3__ as R3_TEMPLATE_REF_FACTORY} from
|
||||
import {SWITCH_VIEW_CONTAINER_REF_FACTORY__POST_R3__ as R3_VIEW_CONTAINER_REF_FACTORY} from '../../src/linker/view_container_ref';
|
||||
import {SWITCH_RENDERER2_FACTORY__POST_R3__ as R3_RENDERER2_FACTORY} from '../../src/render/api';
|
||||
import {CreateComponentOptions} from '../../src/render3/component';
|
||||
import {discoverDirectives, getContext, isComponentInstance} from '../../src/render3/context_discovery';
|
||||
import {getContext, getDirectivesAtNodeIndex, isComponentInstance} from '../../src/render3/context_discovery';
|
||||
import {extractDirectiveDef, extractPipeDef} from '../../src/render3/definition';
|
||||
import {NG_ELEMENT_ID} from '../../src/render3/fields';
|
||||
import {ComponentTemplate, ComponentType, DirectiveDef, DirectiveType, RenderFlags, defineComponent, defineDirective, renderComponent as _renderComponent, tick} from '../../src/render3/index';
|
||||
@ -319,7 +319,7 @@ export function createDirective(
|
||||
|
||||
/** Gets the directive on the given node at the given index */
|
||||
export function getDirectiveOnNode(nodeIndex: number, dirIndex: number = 0) {
|
||||
const directives = discoverDirectives(nodeIndex + HEADER_OFFSET, getViewData(), true);
|
||||
const directives = getDirectivesAtNodeIndex(nodeIndex + HEADER_OFFSET, getViewData(), true);
|
||||
if (directives == null) {
|
||||
throw new Error(`No directives exist on node in slot ${nodeIndex}`);
|
||||
}
|
||||
|
@ -33,7 +33,7 @@ describe('style and class based bindings', () => {
|
||||
const rootContext =
|
||||
createRootContext(requestAnimationFrame.bind(window), playerHandler || null);
|
||||
const lViewData = createLViewData(
|
||||
domRendererFactory3.createRenderer(element, null),
|
||||
null, domRendererFactory3.createRenderer(element, null),
|
||||
createTView(-1, null, 1, 0, null, null, null), rootContext, LViewFlags.IsRoot);
|
||||
return lViewData;
|
||||
}
|
||||
|
Reference in New Issue
Block a user