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);
prevHostBindings(directiveIndex, elementIndex);
};
(definition as any).hostVars += superDef.hostVars;
} else {
definition.hostBindings = superHostBindings;
}

View File

@ -7,7 +7,8 @@
*/
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';
describe('InheritDefinitionFeature', () => {
@ -299,36 +300,61 @@ describe('InheritDefinitionFeature', () => {
});
it('should compose hostBindings', () => {
const log: Array<[string, number, number]> = [];
let subDir !: SubDirective;
class SuperDirective {
id = 'my-id';
static ngDirectiveDef = defineDirective({
type: SuperDirective,
selectors: [['', 'superDir', '']],
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(),
});
}
class SubDirective extends SuperDirective {
title = 'my-title';
static ngDirectiveDef = defineDirective({
type: SubDirective,
selectors: [['', 'subDir', '']],
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]
});
}
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', () => {

View File

@ -1231,42 +1231,41 @@ import {NgModelCustomComp, NgModelCustomWrapper} from './value_accessor_integrat
describe('validation directives', () => {
fixmeIvy('RequiredValidator provided instead of CheckboxRequiredValidator') &&
it('required validator should validate checkbox', fakeAsync(() => {
const fixture = initTest(NgModelCheckboxRequiredValidator);
fixture.detectChanges();
tick();
it('required validator should validate checkbox', fakeAsync(() => {
const fixture = initTest(NgModelCheckboxRequiredValidator);
fixture.detectChanges();
tick();
const control =
fixture.debugElement.children[0].injector.get(NgForm).control.get('checkbox') !;
const control =
fixture.debugElement.children[0].injector.get(NgForm).control.get('checkbox') !;
const input = fixture.debugElement.query(By.css('input'));
expect(input.nativeElement.checked).toBe(false);
expect(control.hasError('required')).toBe(false);
const input = fixture.debugElement.query(By.css('input'));
expect(input.nativeElement.checked).toBe(false);
expect(control.hasError('required')).toBe(false);
fixture.componentInstance.required = true;
fixture.detectChanges();
tick();
fixture.componentInstance.required = true;
fixture.detectChanges();
tick();
expect(input.nativeElement.checked).toBe(false);
expect(control.hasError('required')).toBe(true);
expect(input.nativeElement.checked).toBe(false);
expect(control.hasError('required')).toBe(true);
input.nativeElement.checked = true;
dispatchEvent(input.nativeElement, 'change');
fixture.detectChanges();
tick();
input.nativeElement.checked = true;
dispatchEvent(input.nativeElement, 'change');
fixture.detectChanges();
tick();
expect(input.nativeElement.checked).toBe(true);
expect(control.hasError('required')).toBe(false);
expect(input.nativeElement.checked).toBe(true);
expect(control.hasError('required')).toBe(false);
input.nativeElement.checked = false;
dispatchEvent(input.nativeElement, 'change');
fixture.detectChanges();
tick();
input.nativeElement.checked = false;
dispatchEvent(input.nativeElement, 'change');
fixture.detectChanges();
tick();
expect(input.nativeElement.checked).toBe(false);
expect(control.hasError('required')).toBe(true);
}));
expect(input.nativeElement.checked).toBe(false);
expect(control.hasError('required')).toBe(true);
}));
it('should validate email', fakeAsync(() => {
const fixture = initTest(NgModelEmailValidator);