fix(ivy): merged host bindings functions should take superclass hostVars into account (#27013)
PR Close #27013
This commit is contained in:
parent
2f36a9591d
commit
552836ebf0
|
@ -76,6 +76,7 @@ export function InheritDefinitionFeature(definition: DirectiveDef<any>| Componen
|
||||||
superHostBindings(directiveIndex, elementIndex);
|
superHostBindings(directiveIndex, elementIndex);
|
||||||
prevHostBindings(directiveIndex, elementIndex);
|
prevHostBindings(directiveIndex, elementIndex);
|
||||||
};
|
};
|
||||||
|
(definition as any).hostVars += superDef.hostVars;
|
||||||
} else {
|
} else {
|
||||||
definition.hostBindings = superHostBindings;
|
definition.hostBindings = superHostBindings;
|
||||||
}
|
}
|
||||||
|
|
|
@ -7,7 +7,8 @@
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import {Inject, InjectionToken} from '../../src/core';
|
import {Inject, InjectionToken} from '../../src/core';
|
||||||
import {ComponentDef, DirectiveDef, InheritDefinitionFeature, NgOnChangesFeature, ProvidersFeature, RenderFlags, defineBase, defineComponent, defineDirective, directiveInject, element} from '../../src/render3/index';
|
import {ComponentDef, DirectiveDef, InheritDefinitionFeature, NgOnChangesFeature, ProvidersFeature, RenderFlags, bind, defineBase, defineComponent, defineDirective, directiveInject, element, elementProperty, load} from '../../src/render3/index';
|
||||||
|
|
||||||
import {ComponentFixture, createComponent} from './render_util';
|
import {ComponentFixture, createComponent} from './render_util';
|
||||||
|
|
||||||
describe('InheritDefinitionFeature', () => {
|
describe('InheritDefinitionFeature', () => {
|
||||||
|
@ -299,36 +300,61 @@ describe('InheritDefinitionFeature', () => {
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should compose hostBindings', () => {
|
it('should compose hostBindings', () => {
|
||||||
const log: Array<[string, number, number]> = [];
|
let subDir !: SubDirective;
|
||||||
|
|
||||||
class SuperDirective {
|
class SuperDirective {
|
||||||
|
id = 'my-id';
|
||||||
|
|
||||||
static ngDirectiveDef = defineDirective({
|
static ngDirectiveDef = defineDirective({
|
||||||
type: SuperDirective,
|
type: SuperDirective,
|
||||||
selectors: [['', 'superDir', '']],
|
selectors: [['', 'superDir', '']],
|
||||||
hostBindings: (directiveIndex: number, elementIndex: number) => {
|
hostBindings: (directiveIndex: number, elementIndex: number) => {
|
||||||
log.push(['super', directiveIndex, elementIndex]);
|
const instance = load(directiveIndex) as SuperDirective;
|
||||||
|
elementProperty(elementIndex, 'id', bind(instance.id));
|
||||||
},
|
},
|
||||||
|
hostVars: 1,
|
||||||
factory: () => new SuperDirective(),
|
factory: () => new SuperDirective(),
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
class SubDirective extends SuperDirective {
|
class SubDirective extends SuperDirective {
|
||||||
|
title = 'my-title';
|
||||||
|
|
||||||
static ngDirectiveDef = defineDirective({
|
static ngDirectiveDef = defineDirective({
|
||||||
type: SubDirective,
|
type: SubDirective,
|
||||||
selectors: [['', 'subDir', '']],
|
selectors: [['', 'subDir', '']],
|
||||||
hostBindings: (directiveIndex: number, elementIndex: number) => {
|
hostBindings: (directiveIndex: number, elementIndex: number) => {
|
||||||
log.push(['sub', directiveIndex, elementIndex]);
|
const instance = load(directiveIndex) as SubDirective;
|
||||||
|
elementProperty(elementIndex, 'title', bind(instance.title));
|
||||||
},
|
},
|
||||||
factory: () => new SubDirective(),
|
hostVars: 1,
|
||||||
|
factory: () => subDir = new SubDirective(),
|
||||||
features: [InheritDefinitionFeature]
|
features: [InheritDefinitionFeature]
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
const subDef = SubDirective.ngDirectiveDef as DirectiveDef<any>;
|
|
||||||
|
|
||||||
subDef.hostBindings !(1, 2);
|
const App = createComponent('app', (rf: RenderFlags, ctx: any) => {
|
||||||
|
if (rf & RenderFlags.Create) {
|
||||||
|
element(0, 'div', ['subDir', '']);
|
||||||
|
}
|
||||||
|
}, 1, 0, [SubDirective]);
|
||||||
|
|
||||||
expect(log).toEqual([['super', 1, 2], ['sub', 1, 2]]);
|
const fixture = new ComponentFixture(App);
|
||||||
|
const divEl = fixture.hostElement.querySelector('div') as HTMLElement;
|
||||||
|
|
||||||
|
expect(divEl.id).toEqual('my-id');
|
||||||
|
expect(divEl.title).toEqual('my-title');
|
||||||
|
|
||||||
|
subDir.title = 'new-title';
|
||||||
|
fixture.update();
|
||||||
|
expect(divEl.id).toEqual('my-id');
|
||||||
|
expect(divEl.title).toEqual('new-title');
|
||||||
|
|
||||||
|
subDir.id = 'new-id';
|
||||||
|
fixture.update();
|
||||||
|
expect(divEl.id).toEqual('new-id');
|
||||||
|
expect(divEl.title).toEqual('new-title');
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should compose viewQuery', () => {
|
it('should compose viewQuery', () => {
|
||||||
|
|
|
@ -1231,42 +1231,41 @@ import {NgModelCustomComp, NgModelCustomWrapper} from './value_accessor_integrat
|
||||||
|
|
||||||
describe('validation directives', () => {
|
describe('validation directives', () => {
|
||||||
|
|
||||||
fixmeIvy('RequiredValidator provided instead of CheckboxRequiredValidator') &&
|
it('required validator should validate checkbox', fakeAsync(() => {
|
||||||
it('required validator should validate checkbox', fakeAsync(() => {
|
const fixture = initTest(NgModelCheckboxRequiredValidator);
|
||||||
const fixture = initTest(NgModelCheckboxRequiredValidator);
|
fixture.detectChanges();
|
||||||
fixture.detectChanges();
|
tick();
|
||||||
tick();
|
|
||||||
|
|
||||||
const control =
|
const control =
|
||||||
fixture.debugElement.children[0].injector.get(NgForm).control.get('checkbox') !;
|
fixture.debugElement.children[0].injector.get(NgForm).control.get('checkbox') !;
|
||||||
|
|
||||||
const input = fixture.debugElement.query(By.css('input'));
|
const input = fixture.debugElement.query(By.css('input'));
|
||||||
expect(input.nativeElement.checked).toBe(false);
|
expect(input.nativeElement.checked).toBe(false);
|
||||||
expect(control.hasError('required')).toBe(false);
|
expect(control.hasError('required')).toBe(false);
|
||||||
|
|
||||||
fixture.componentInstance.required = true;
|
fixture.componentInstance.required = true;
|
||||||
fixture.detectChanges();
|
fixture.detectChanges();
|
||||||
tick();
|
tick();
|
||||||
|
|
||||||
expect(input.nativeElement.checked).toBe(false);
|
expect(input.nativeElement.checked).toBe(false);
|
||||||
expect(control.hasError('required')).toBe(true);
|
expect(control.hasError('required')).toBe(true);
|
||||||
|
|
||||||
input.nativeElement.checked = true;
|
input.nativeElement.checked = true;
|
||||||
dispatchEvent(input.nativeElement, 'change');
|
dispatchEvent(input.nativeElement, 'change');
|
||||||
fixture.detectChanges();
|
fixture.detectChanges();
|
||||||
tick();
|
tick();
|
||||||
|
|
||||||
expect(input.nativeElement.checked).toBe(true);
|
expect(input.nativeElement.checked).toBe(true);
|
||||||
expect(control.hasError('required')).toBe(false);
|
expect(control.hasError('required')).toBe(false);
|
||||||
|
|
||||||
input.nativeElement.checked = false;
|
input.nativeElement.checked = false;
|
||||||
dispatchEvent(input.nativeElement, 'change');
|
dispatchEvent(input.nativeElement, 'change');
|
||||||
fixture.detectChanges();
|
fixture.detectChanges();
|
||||||
tick();
|
tick();
|
||||||
|
|
||||||
expect(input.nativeElement.checked).toBe(false);
|
expect(input.nativeElement.checked).toBe(false);
|
||||||
expect(control.hasError('required')).toBe(true);
|
expect(control.hasError('required')).toBe(true);
|
||||||
}));
|
}));
|
||||||
|
|
||||||
it('should validate email', fakeAsync(() => {
|
it('should validate email', fakeAsync(() => {
|
||||||
const fixture = initTest(NgModelEmailValidator);
|
const fixture = initTest(NgModelEmailValidator);
|
||||||
|
|
Loading…
Reference in New Issue