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 {assertEqual} from '../../util/assert';
|
||||
import {fillProperties} from '../../util/property';
|
||||
import {EMPTY_ARRAY, EMPTY_OBJ} from '../empty';
|
||||
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 {mergeHostAttrs} from '../util/attrs_utils';
|
||||
|
||||
|
@ -71,6 +70,15 @@ export function ɵɵInheritDefinitionFeature(definition: DirectiveDef<any>| Comp
|
|||
fillProperties(definition.declaredInputs, superDef.declaredInputs);
|
||||
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
|
||||
// Assume super class inheritance feature has already run.
|
||||
writeableDef.afterContentChecked =
|
||||
|
@ -179,4 +187,4 @@ function inheritHostBindings(
|
|||
} else {
|
||||
definition.hostBindings = superHostBindings;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -6,11 +6,13 @@
|
|||
* 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 {ivyEnabled} from '@angular/core/src/ivy_switch';
|
||||
import {getDirectiveDef} from '@angular/core/src/render3/definition';
|
||||
import {TestBed} from '@angular/core/testing';
|
||||
import {By} from '@angular/platform-browser';
|
||||
import {NoopAnimationsModule} from '@angular/platform-browser/animations';
|
||||
import {onlyInIvy} from '@angular/private/testing';
|
||||
|
||||
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)', () => {
|
||||
// TODO: sub and super HostBinding same property but different bindings
|
||||
// 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)', () => {
|
||||
// TODO: sub and super HostBinding same property but different bindings
|
||||
// TODO: sub and super HostBinding same binding on two different properties
|
||||
|
|
Loading…
Reference in New Issue