In my angular application, the app component is as follows:
<table>
<tr appA="Hello" *ngFor="let row of rows">
<ng-container *ngTemplateOutlet="tdTemplate">
</ng-container>
</tr>
<ng-template #tdTemplate>
<td appB>
</td>
</ng-template>
</table>
There are 2 directives, ADirective
and BDirective
. In BDirective
I need to get a referenece to the ADirective
that was placed on its parent <tr>
@Directive({
selector: '[appB]'
})
export class BDirective {
constructor(private a: ADirective) { }
}
Since the <td>
is below the <tr>
in the actual DOM, I expected the Dependency Injection to be able to provide it. But since the DI travels up the View hierarchy, not the actual DOM Hierarchy I get a NullInjectorError
instead.
Is there any way for the BDirective
to get a hold of the ADirective
, assuming that the structure of the app.component.html
cannot be changed?
Some background about the problem
I am using **angular material** table component. It requires to provide a template for each row and a template for each column and then it builds the table from the templates. I need each table cell to perform calculations that are affected by data that is set per row. The directive on the cell performs the calculations, the directive on the row captures the data. So by injecting the "row directive" into the "cell directive" I am able to pass that data to the cell directive. But it seems like the angular DI arranges the hierarchy by the views, not by the actual DOM.CodePudding user response:
I think it could be done like this
<table>
<tr appA="Hello" #parentAppa="appA">
<ng-container *ngTemplateOutlet="tdTemplate">
</ng-container>
</tr>
<ng-template #tdTemplate>
<td appB [appA]="parentAppa">
</td>
</ng-template>
</table>
where
@Directive({
selector: '[appB]'
})
export class BDirective {
@Input()
appA:ADirective;
constructor(private a: ADirective) { }
}
after your breaking edit, hopefully you will know now that you should include a meaningfull code and not what you might think is a perfect representaion of actual code/problem. That saves everyones time.
Pass it as template varable like this
<table>
<tr appA="Hello" *ngFor="let row of rows" #varAppa="appA">
<ng-container [ngTemplateOutlet]="tdTemplate; context:{$implicit:varAppa}">
</ng-container>
</tr>
<ng-template #tdTemplate let-passedAppa>
<td appB [appA]="passedAppa">
</td>
</ng-template>
</table>
Im writing it by hand so it might be somehow wrong regarding to using templates. If so, check https://angular.io/api/common/NgTemplateOutlet#example how to fix it
I hope it works, never did it nor tried, I just think it should work.
CodePudding user response:
You could reduce reliance on template hierarchies if there are too many complications by using a service
Inject the service into both the parent A
directive and child B
directives. Register the A
directives with the service using an id of some kind e.g. the ngFor
row index.
In the B
directive retrieve the relevant A
instance from the service using the id (the id could be passed using the template context)