fix(ivy): merged host bindings functions should take superclass hostVars into account (#27013)

PR Close #27013
This commit is contained in:
Kara Erickson 2018-11-08 11:46:33 -08:00 committed by Andrew Kushnir
parent 2f36a9591d
commit 552836ebf0
3 changed files with 62 additions and 36 deletions

View File

@ -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;
} }

View File

@ -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', () => {

View File

@ -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);