fix(core): make subclass inherit developer-defined data (#35105)
PR Close #35105
This commit is contained in:
parent
8e12707f88
commit
a756161dc2
|
@ -7,11 +7,10 @@
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import {Type, Writable} from '../../interface/type';
|
import {Type, Writable} from '../../interface/type';
|
||||||
import {assertEqual} from '../../util/assert';
|
|
||||||
import {fillProperties} from '../../util/property';
|
import {fillProperties} from '../../util/property';
|
||||||
import {EMPTY_ARRAY, EMPTY_OBJ} from '../empty';
|
import {EMPTY_ARRAY, EMPTY_OBJ} from '../empty';
|
||||||
import {ComponentDef, ContentQueriesFunction, DirectiveDef, DirectiveDefFeature, HostBindingsFunction, RenderFlags, ViewQueriesFunction} from '../interfaces/definition';
|
import {ComponentDef, ContentQueriesFunction, DirectiveDef, DirectiveDefFeature, HostBindingsFunction, RenderFlags, ViewQueriesFunction} from '../interfaces/definition';
|
||||||
import {AttributeMarker, TAttributes} from '../interfaces/node';
|
import {TAttributes} from '../interfaces/node';
|
||||||
import {isComponentDef} from '../interfaces/type_checks';
|
import {isComponentDef} from '../interfaces/type_checks';
|
||||||
import {mergeHostAttrs} from '../util/attrs_utils';
|
import {mergeHostAttrs} from '../util/attrs_utils';
|
||||||
|
|
||||||
|
@ -71,6 +70,15 @@ export function ɵɵInheritDefinitionFeature(definition: DirectiveDef<any>| Comp
|
||||||
fillProperties(definition.declaredInputs, superDef.declaredInputs);
|
fillProperties(definition.declaredInputs, superDef.declaredInputs);
|
||||||
fillProperties(definition.outputs, superDef.outputs);
|
fillProperties(definition.outputs, superDef.outputs);
|
||||||
|
|
||||||
|
// Merge animations metadata.
|
||||||
|
// If `superDef` is a Component, the `data` field is present (defaults to an empty object).
|
||||||
|
if (isComponentDef(superDef) && superDef.data.animation) {
|
||||||
|
// If super def is a Component, the `definition` is also a Component, since Directives can
|
||||||
|
// not inherit Components (we throw an error above and cannot reach this code).
|
||||||
|
const defData = (definition as ComponentDef<any>).data;
|
||||||
|
defData.animation = (defData.animation || []).concat(superDef.data.animation);
|
||||||
|
}
|
||||||
|
|
||||||
// Inherit hooks
|
// Inherit hooks
|
||||||
// Assume super class inheritance feature has already run.
|
// Assume super class inheritance feature has already run.
|
||||||
writeableDef.afterContentChecked =
|
writeableDef.afterContentChecked =
|
||||||
|
@ -179,4 +187,4 @@ function inheritHostBindings(
|
||||||
} else {
|
} else {
|
||||||
definition.hostBindings = superHostBindings;
|
definition.hostBindings = superHostBindings;
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -6,11 +6,13 @@
|
||||||
* found in the LICENSE file at https://angular.io/license
|
* found in the LICENSE file at https://angular.io/license
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
import {state, style, trigger} from '@angular/animations';
|
||||||
import {Component, ContentChildren, Directive, EventEmitter, HostBinding, HostListener, Input, OnChanges, Output, QueryList, ViewChildren} from '@angular/core';
|
import {Component, ContentChildren, Directive, EventEmitter, HostBinding, HostListener, Input, OnChanges, Output, QueryList, ViewChildren} from '@angular/core';
|
||||||
import {ivyEnabled} from '@angular/core/src/ivy_switch';
|
import {ivyEnabled} from '@angular/core/src/ivy_switch';
|
||||||
import {getDirectiveDef} from '@angular/core/src/render3/definition';
|
import {getDirectiveDef} from '@angular/core/src/render3/definition';
|
||||||
import {TestBed} from '@angular/core/testing';
|
import {TestBed} from '@angular/core/testing';
|
||||||
import {By} from '@angular/platform-browser';
|
import {By} from '@angular/platform-browser';
|
||||||
|
import {NoopAnimationsModule} from '@angular/platform-browser/animations';
|
||||||
import {onlyInIvy} from '@angular/private/testing';
|
import {onlyInIvy} from '@angular/private/testing';
|
||||||
|
|
||||||
describe('inheritance', () => {
|
describe('inheritance', () => {
|
||||||
|
@ -4132,6 +4134,99 @@ describe('inheritance', () => {
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
describe('animations', () => {
|
||||||
|
onlyInIvy('View Engine does not inherit `host` metadata from superclass')
|
||||||
|
.it('should work with inherited host bindings and animations', () => {
|
||||||
|
@Component({
|
||||||
|
selector: 'super-comp',
|
||||||
|
template: '<div>super-comp</div>',
|
||||||
|
host: {
|
||||||
|
'[@animation]': 'colorExp',
|
||||||
|
},
|
||||||
|
animations: [
|
||||||
|
trigger('animation', [state('color', style({color: 'red'}))]),
|
||||||
|
],
|
||||||
|
})
|
||||||
|
class SuperComponent {
|
||||||
|
colorExp = 'color';
|
||||||
|
}
|
||||||
|
|
||||||
|
@Component({
|
||||||
|
selector: 'my-comp',
|
||||||
|
template: `<div>my-comp</div>`,
|
||||||
|
})
|
||||||
|
class MyComponent extends SuperComponent {
|
||||||
|
}
|
||||||
|
|
||||||
|
@Component({
|
||||||
|
template: '<my-comp>app</my-comp>',
|
||||||
|
})
|
||||||
|
class App {
|
||||||
|
}
|
||||||
|
|
||||||
|
TestBed.configureTestingModule({
|
||||||
|
declarations: [App, MyComponent, SuperComponent],
|
||||||
|
imports: [NoopAnimationsModule],
|
||||||
|
});
|
||||||
|
const fixture = TestBed.createComponent(App);
|
||||||
|
fixture.detectChanges();
|
||||||
|
const queryResult = fixture.debugElement.query(By.css('my-comp'));
|
||||||
|
|
||||||
|
expect(queryResult.nativeElement.style.color).toBe('red');
|
||||||
|
});
|
||||||
|
|
||||||
|
onlyInIvy('View Engine does not inherit `host` metadata from superclass')
|
||||||
|
.it('should compose animations (from super class)', () => {
|
||||||
|
@Component({
|
||||||
|
selector: 'super-comp',
|
||||||
|
template: '...',
|
||||||
|
animations: [
|
||||||
|
trigger('animation1', [state('color', style({color: 'red'}))]),
|
||||||
|
trigger('animation2', [state('opacity', style({opacity: '0.5'}))]),
|
||||||
|
],
|
||||||
|
})
|
||||||
|
class SuperComponent {
|
||||||
|
}
|
||||||
|
|
||||||
|
@Component({
|
||||||
|
selector: 'my-comp',
|
||||||
|
template: '<div>my-comp</div>',
|
||||||
|
host: {
|
||||||
|
'[@animation1]': 'colorExp',
|
||||||
|
'[@animation2]': 'opacityExp',
|
||||||
|
'[@animation3]': 'bgExp',
|
||||||
|
},
|
||||||
|
animations: [
|
||||||
|
trigger('animation1', [state('color', style({color: 'blue'}))]),
|
||||||
|
trigger('animation3', [state('bg', style({backgroundColor: 'green'}))]),
|
||||||
|
],
|
||||||
|
})
|
||||||
|
class MyComponent extends SuperComponent {
|
||||||
|
colorExp = 'color';
|
||||||
|
opacityExp = 'opacity';
|
||||||
|
bgExp = 'bg';
|
||||||
|
}
|
||||||
|
|
||||||
|
@Component({
|
||||||
|
template: '<my-comp>app</my-comp>',
|
||||||
|
})
|
||||||
|
class App {
|
||||||
|
}
|
||||||
|
|
||||||
|
TestBed.configureTestingModule({
|
||||||
|
declarations: [App, MyComponent, SuperComponent],
|
||||||
|
imports: [NoopAnimationsModule],
|
||||||
|
});
|
||||||
|
const fixture = TestBed.createComponent(App);
|
||||||
|
fixture.detectChanges();
|
||||||
|
const queryResult = fixture.debugElement.query(By.css('my-comp'));
|
||||||
|
|
||||||
|
expect(queryResult.nativeElement.style.color).toBe('blue');
|
||||||
|
expect(queryResult.nativeElement.style.opacity).toBe('0.5');
|
||||||
|
expect(queryResult.nativeElement.style.backgroundColor).toBe('green');
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
describe('host bindings (style related)', () => {
|
describe('host bindings (style related)', () => {
|
||||||
// TODO: sub and super HostBinding same property but different bindings
|
// TODO: sub and super HostBinding same property but different bindings
|
||||||
// TODO: sub and super HostBinding same binding on two different properties
|
// TODO: sub and super HostBinding same binding on two different properties
|
||||||
|
@ -4819,6 +4914,70 @@ describe('inheritance', () => {
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
describe('animations', () => {
|
||||||
|
onlyInIvy('View Engine does not inherit `host` metadata from superclass')
|
||||||
|
.it('should compose animations across multiple inheritance levels', () => {
|
||||||
|
@Component({
|
||||||
|
selector: 'super-comp',
|
||||||
|
template: '...',
|
||||||
|
host: {
|
||||||
|
'[@animation1]': 'colorExp',
|
||||||
|
'[@animation2]': 'opacityExp',
|
||||||
|
},
|
||||||
|
animations: [
|
||||||
|
trigger('animation1', [state('color', style({color: 'red'}))]),
|
||||||
|
trigger('animation2', [state('opacity', style({opacity: '0.5'}))]),
|
||||||
|
],
|
||||||
|
})
|
||||||
|
class SuperComponent {
|
||||||
|
colorExp = 'color';
|
||||||
|
opacityExp = 'opacity';
|
||||||
|
}
|
||||||
|
|
||||||
|
@Component({
|
||||||
|
selector: 'intermediate-comp',
|
||||||
|
template: '...',
|
||||||
|
})
|
||||||
|
class IntermediateComponent extends SuperComponent {
|
||||||
|
}
|
||||||
|
|
||||||
|
@Component({
|
||||||
|
selector: 'my-comp',
|
||||||
|
template: '<div>my-comp</div>',
|
||||||
|
host: {
|
||||||
|
'[@animation1]': 'colorExp',
|
||||||
|
'[@animation3]': 'bgExp',
|
||||||
|
},
|
||||||
|
animations: [
|
||||||
|
trigger('animation1', [state('color', style({color: 'blue'}))]),
|
||||||
|
trigger('animation3', [state('bg', style({backgroundColor: 'green'}))]),
|
||||||
|
],
|
||||||
|
})
|
||||||
|
class MyComponent extends IntermediateComponent {
|
||||||
|
colorExp = 'color';
|
||||||
|
opacityExp = 'opacity';
|
||||||
|
bgExp = 'bg';
|
||||||
|
}
|
||||||
|
|
||||||
|
@Component({
|
||||||
|
template: '<my-comp>app</my-comp>',
|
||||||
|
})
|
||||||
|
class App {
|
||||||
|
}
|
||||||
|
|
||||||
|
TestBed.configureTestingModule({
|
||||||
|
declarations: [App, MyComponent, IntermediateComponent, SuperComponent],
|
||||||
|
imports: [NoopAnimationsModule],
|
||||||
|
});
|
||||||
|
const fixture = TestBed.createComponent(App);
|
||||||
|
fixture.detectChanges();
|
||||||
|
const queryResult = fixture.debugElement.query(By.css('my-comp'));
|
||||||
|
|
||||||
|
expect(queryResult.nativeElement.style.color).toBe('blue');
|
||||||
|
expect(queryResult.nativeElement.style.opacity).toBe('0.5');
|
||||||
|
expect(queryResult.nativeElement.style.backgroundColor).toBe('green');
|
||||||
|
});
|
||||||
|
});
|
||||||
describe('host bindings (style related)', () => {
|
describe('host bindings (style related)', () => {
|
||||||
// TODO: sub and super HostBinding same property but different bindings
|
// TODO: sub and super HostBinding same property but different bindings
|
||||||
// TODO: sub and super HostBinding same binding on two different properties
|
// TODO: sub and super HostBinding same binding on two different properties
|
||||||
|
|
Loading…
Reference in New Issue