Get child directive using a structural directive

I am struggling to access a 'child' directive within a structural directive in order to pass some data into that directive. Unfortunately, I keep getting undefined or an injection error.

Here is the HTML snippet I have:

<app-table [dataSource]="data">
  <button *customColumn="let row" [highlightRowOnClick]="row">
    click here!
  </button>
</app-table>

I aim to inject all rows using the customColumn directive into the [highlightRowOnClick] directive so that clicking on one row will deselect all other rows.

The flow of passing rows to the directive should be: (app-table --> *customColumn --> [highlightRowOnClick]).

I have set up a Stackblitz to showcase this issue with row selection functioning correctly but not with row deselection.


My Thoughts

  • I am aware that accessing child components via @ContentChildren is not possible due to the [highlightRowOnClick] directive not being a direct child, rather a sibling in the view (read more here).

  • A structural directive can select its host element (e.g., an attribute directive like ngSwitch) through the @Host decorator, but it doesn't work the other way around, leading to an Injector error (

    no provider for 'customColumn' found
    ). (Details available here)

  • Using a service to inject the data could cause bugs when multiple tables exist in the current view since the service is a singleton and overlapping rows could create unexpected selection issues (like selections happening in both tables).

  • Directly injecting the rows into the [highlightRowOnClick] directive would require manual intervention by the programmer each time. It seems logical to only highlight the current row and not expose all rows at that level since they are already accessible via [dataSource]. Therefore, this might not be the optimal solution.

Example:

<app-table [dataSource]="rows">
  <button *customColumn="let row; let rows=rows" [highlightRowOnClick]="row" allRows="rows">
    click here!
  </button>
</app-table>

Is there a better approach to resolve this dilemma? Perhaps a completely different strategy?

Answer №1

If you encapsulate everything in a wrapper component, you can easily inject it into the HighlightRowOnClickDirective.

constructor(@Optional() @Host() private wrap:WrapperComponent ){}
  @Input('highlightRowOnClick')
  row: Row;

  @HostListener('click')
  onClick(): void {
    this.wrap.dataSource.forEach(x=>{
      x.isSelected=(x==this.row)
    })
  }
}

Check out this stackblitz link

It might be more practical to use a variable like "indexSelected" instead of using *ngFor="...;let i=index" and passing it as an argument.

Update: Implementing Index Select

You can either have a boolean variable "isSelected" in each row or a unique variable "indexSelected" in the wrapper, and assign the index value to it.

<div *ngFor="let row of dataSource; let i=index">
  <div [class.selected]="i==indexSelected"> Hello world! </div>
  <ng-container *ngFor="let col of colHeaders">
     <ng-template *ngTemplateOutlet="col.template; context: { $implicit: i }"></ng-template>
  </ng-container>
  <br><br>
</div>

The updated directive will look like this:

@Input('highlightRowOnClick') index:number

@HostListener('click')
onClick(): void {
  this.wrap.indexSelected = this.index
}

View another example on this stackblitz link

Similar questions

If you have not found the answer to your question or you are interested in this topic, then look at other similar questions below or use the search

I have noticed that my unit test case does not include coverage for the if statement

Here is the function I have in my TypeScript file: routeToIndividualPortal(sessionToken: string) { let redirectUrl = this.relayState; console.log("Pre-source-check Indivual URL : " + redirectUrl); let url = ""; if(redirectUrl.includes(this. ...

Error: Property 'nativeElement' is undefined in Ionic/Chart.js

I am currently working on a project that involves displaying an Ionic segment containing a Chart.js barchart. While I have successfully displayed the chart itself, I encountered an error when attempting to place the chart HTML inside the Ionic segment: ER ...

Enhancement of Nebular Theme from version 4.4.0 to 9.0.1 in relation to Angular (from 8.2.3 to 13.2.1)

Upon upgrading from Nebular Theme version 4.4.0 to 9.0.1 and Angular version 8.2.3 to 13.2.1, I encountered the following issues: 'nb-card-header' is not recognized as a valid element: If 'nb-card-header' is an Angular component, ens ...

Error event encountered during development of React.js/Next.js application

When building on my local server everything works fine, but when I try to build on a production Linux server, I encounter the following error: > next build ...

Implementing the breadcrumb component within dynamically loaded modules loaded through the router-outlet component

I'm currently working on an angular 8 breadcrumb component for my application. The requirement is to display it in the content of the page, not just in the header, and it should never be located outside the router-outlet. This has posed a challenge fo ...

Initiating external libraries at the right time in Angular 4

I am currently experimenting with a UI kit (you can check it out here) that comes with multiple JavaScript files, jQuery, Bootstrap, and its own components. I have included them in my index.html file and everything works perfectly as long as the checkbox ...

Starting a checkbox group with values based on total number

I am facing a challenge with a group of checkboxes that have values assigned as 1,2,4,8,16,32,64. Each number corresponds to a day of the week (e.g. Sunday, Monday etc...) The total value of the checkbox group is calculated as the sum of the selected chec ...

develop a directive that allows for input fields to become focused

I am currently working on activating focus in multiple inputs within my application. In order to avoid code duplication, I had the idea of creating a custom directive but unfortunately, I am facing some challenges with it. If anyone could lend a hand... Be ...

Using TypeScript to create a list of object keys defined as [key: string] key/value pairs

I'm looking to define an interface in TypeScript, but I'm unsure of how to go about it. Here's what I have in mind: export interface SomeInterface { testProp:{ [key: string]: { prop1: string; prop2?: string; prop3?: string; ...

JavaScript 2 add or remove a class to the keyboard when opening or closing

As I work on developing an app in Ionic 2, I am facing a challenge where the background image needs to change when the keyboard is opened and closed in an input field. <ion-content padding class="bg_gogreen"> <ion-list> <ion-item class=" ...

If either the form is invalid or has been submitted, disable the button

Is there a way to either disable the button when the form is invalid or after the user clicks it, but not both at the same time? How can I incorporate two statements inside the quotes for this purpose? I attempted the following code, but it didn't w ...

Executing functions before the data is loaded in an Angular application

Hey there, I'm relatively new to Angular and I've been facing some difficulties when it comes to loading data. In the code snippet below, you'll notice that I have two services being called. Each service logs something to the console. After ...

NativeScript is having trouble locating Angular components

As I venture into the world of Angular and NativeScript, I find myself stuck on an issue with component references that just won't seem to work. I kick off by generating the initial file using: tns create Test --ng. Then I lay out the file structure ...

Error occurs when a value is passed to a component after it has already been checked for changes

Whenever I try to pass a value as input to a custom child component, I keep encountering the error message indicating that the expression has changed after being checked. The structure of the child component is as follows: <mat-card *ngIf="user$ | asyn ...

What is the best way to inject the Service into the Controller constructor using TypeScript?

I am developing a straightforward REST API using TypeScript that interacts with your classes to query a database in the following sequence: Controller > Service > Repository. While working on this, I experimented with the following code snippets: Co ...

Learn how to utilize interpolation within an *ngIf statement in Angular 2 in order to access local template

Consider the following scenario; <div *ngFor="item of items; let i = index;"> <div *ngIf="variable{{i}}">show if variable{{i}} is true</div> </div> Suppose I have variables named "variable0", "variable1",... Is there a way to ac ...

Angular2 - Access to fetch at 'https://example.com' from

Requesting some guidance on integrating the forecast API into my angular2 application. Currently facing a Cross-Origin Error while attempting to access the API. Any suggestions on resolving this issue? search(latitude: any, longitude: any){ consol ...

Tips for utilizing the @Input() property of a component to set the initial value of an input field

Is there a way to utilize the value of an @Input() property on Component B as the initial value for an input field in that component when it is contained within Component A? I attempted passing the value during form construction, but found that it only wo ...

Accessing information from RESTful Web Service with Angular 2's Http functionality

I am currently working on retrieving data from a RESTful web service using Angular 2 Http. Initially, I inject the service into the constructor of the client component class: constructor (private _myService: MyService, private route: Activat ...

Creating a user-friendly interface for an array of objects, complete with an included array containing those same objects

I have an array that I want to iterate through. It contains a single object and an array of objects. How can I create an interface for this structure? What is the appropriate declaration to replace any[]? Here is the code: export const initialPhotoProps: ...