Reputation: 7640
closely related to ContentChild by extended abstract class
I am using a chart package that handle very poorly the resize.
I made a wrapper component that would set a static height/width, and draw the chart inside using ng-content:
<app-chart-sizer>
<chart>
</chart>
</app-chart-sizer>
app-chart-sizer is basically
<div #sizer>
<ng-content></ng-content>
</div>
with
@Component({
selector: 'app-chart-sizer',
templateUrl: './chart-sizer.component.html',
styleUrls: ['./chart-sizer.component.scss']
})
export class ChartSizerComponent implements AfterContentInit {
@Input() percentageH: number
@Input() percentageW: number
@ViewChild('sizer') sizer: ElementRef
@HostListener('window:resize', ['$event'])
onResize() {
this.setHeight()
}
constructor(
private renderer: Renderer2
) {
}
ngAfterContentInit(): void {
this.setHeight()
}
setHeight() {
//
}
}
The problem I face is, at first load, after draw in the content, the chart are not using a correct size, so I need to trigger their resize event.
I can just dispatch a window resize, or more cleanly, I would like to call the resize on the chart component inside the ngContent
The library as multiple charts, and they all have a parent BaseChartComponent
but their component are different.
I tried to do
@ContentChild(BaseChartComponent) chart: BaseChartComponent
but this return undefined, If I name a specific chart name like (BarChartComponent extends BaseChart)
@ContentChild(BarChartComponent) chart: BarChartComponent
this work, but I miss all other chart type.
how can I query correctly for an instance of BaseChartComponent? I can't edit the childs, so I can't use the solution provided in many SO exemples.
Upvotes: 0
Views: 998
Reputation: 9377
Yeah, it's not possible, because these decorators work like the querySelector
would work when search in the DOM for the components: and your AbstractClass is not in the DOM at all, so it won't be found. But maybe you can have a workaround using a directive to be applied to your charts.
To try to minimize the code here, I'm gonna make some assumptions that can be easily overridden by your real case. The main part is: you cannot query for a base abstract class not for a component without previous knowledge about its type, but you can query for a directive and you can apply a directive to the components you wanna query.
It has just one function: get and expose a reference of the component it's applied to. As we're gonna use an @Input
for grabbing the reference, we can use any type we want for the input, including the base abstract class BaseChartComponent
that's not a decorated with @Component
, despite its name (because you have some important properties/methods there that you'd like to access, like a resize()
method).
@Directive({selector: "[baseChart]"})
export class BaseChartDirective {
@Input() baseChart: BaseChartComponent;
}
Something like this:
export abstract class BaseChartComponent { size = 20 }
I'm also supposing that your sizer component is like below. Notice we're querying for the directive, so the component passed to ng-content
should have the directive applied to it. Maybe you could put some warnings in the sizer when it doesn't find anything.
@Component({
selector: "app-chart-sizer",
template: '<ng-content></ng-content>'
})
export class ChartSizerComponent implements AfterContentInit {
@ContentChild(BaseChartDirective) _chart: BaseChartDirective;
ngAfterContentInit() {
// You can get the new size via @Inputs in this component
this._chart.size = 45;
}
}
@Component({
selector: "app-bar-chart",
template: '<p>My size is {{size}}</p>'
})
export class BarChartComponent extends BaseChartComponent {}
So, when using the sizer. It's recommended that, in the directive, you throw an error if someone forgets to set the @Input() baseChart
. This is necessary because, in the directive, we cannot inject the parent, like it's common in many cases (we cannot do that because, as stated above, we don't know beforehand the type of the component where the directive is applied to).
<app-chart-sizer>
<app-bar-chart #chart [baseChart]="chart"></app-bar-chart>
</app-chart-sizer>
Upvotes: 2