What is the reason for the lack of template reference output from ContentChilden?

I am striving to create a modular reusable table by using directives to generate columns for custom cell content. However, after multiple days of effort, I am unable to access my TemplateRefs and they always turn out empty.

The Angular version being used is v10, due to the project being stagnant in time as per business requirements. The complexity of the issues at hand overwhelms me, making it difficult to pinpoint what might be wrong. There seem to be persistent problems with this approach, pushing me to the brink of frustration.

What could be causing this issue and how can I achieve my goal?

= the component containing the table =

[HTML]

<app-table2 #ordTable [rows]="rows" [columns]="columns">
    <ng-template column columnId="order_id" let-row>
            {{row.order_id}}
    </ng-template>
</app-table2>

[TS]

columns: Table2Column[] = [
    {identifier: "order_id", title: "ORDER #"},
    ...
];

= table component =

[HTML]

<div>table 2</div>

<div class="table-row-{{i}}" *ngFor="let row of rows; let i = index">
  <div class="table-cell checkbox-cell">
  </div>
  <ng-container *ngFor="let column of columns">
    <div class="table-cell {{ column.extraClass }}">
      <ng-container *ngTemplateOutlet="dict[column.identifier]; context:{$implicit: row}"></ng-container>
    </div>
  </ng-container>
</div>

[TS]

export interface Table2Column {
  identifier: string;
  title: string;
  sizing?: string;
  extraClass?: string;
}


@Directive({
  selector: "[column]"
})
export class ColumnTemplateDirective {
  @Input('columnId') column!: string;

  constructor(public templateRef: TemplateRef<any>) {}
}

@Component({
    selector: 'app-table2',
    templateUrl: './table2.component.html',
    styleUrls: ['./table2.component.scss']
})
export class Table2Component implements OnInit {
  @ContentChildren(ColumnTemplateDirective) columnTemplates!: QueryList<ColumnTemplateDirective>;

  dict: { [key: string]: TemplateRef<any> } = {};

  @Input() rows: Array<any>;
  @Input() columns: Array<Table2Column>;

  constructor() {}

  ngOnInit(): void {}

  ngAfterContentInit() {
    console.log('asdasdasdsad', this.columnTemplates);
    this.columnTemplates.forEach(x => {
      this.dict[x.column] = x.templateRef
    });
    console.log('dict', this.dict);
  }
}

No matter what I try, I cannot retrieve the template references. Interestingly, we have successfully implemented a similar technique in another part of the project running Angular 12.

Could this be a version-specific issue that I have encountered?

Answer №1

The ContentChildren directive in Angular has a useful second argument called read, allowing you to specify which directive/component to access. This enables direct access to the templateRef.

Key Metadata Properties (taken from the Documentation):

...
read - Enables reading a different token from the queried elements.

...
@ContentChildren(TemplateRef, { read: TemplateRef }) columnTemplates!: QueryList<TemplateRef<any>>;
...

Alternatively:

...
@ContentChildren('column', { read: TemplateRef }) columnTemplates!: QueryList<TemplateRef<any>>;
...

Incorporate this into your HTML as follows:

<app-table2 #ordTable [rows]="rows" [columns]="columns">
    <ng-template #column column columnId="order_id" let-row>
            {{row.order_id}}
    </ng-template>
</app-table2>

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

Is it possible to eliminate the arrows from an input type while restricting the change to a specific component?

Is there a way to remove the arrows from my input field while still applying it only to the text fields within this component? <v-text-field class="inputPrice" type="number" v-model="$data._value" @change="send ...

Error: The route in "src/app/api/orders/route.ts" does not conform to the necessary types for a Next.js Route. The "default" export field is not recognized as a valid Route

I need to retrieve the orders from the database using Prisma based on the user's email (authenticated via Google Provider). Here is the repository - https://github.com/Jayesh-kahnani/Snatch Here is the API. // src/app/api/order/route.ts import { Next ...

Customizing date colors in JavaScript: A step-by-step guide

var active_dates1 = ["2017-04-02 00:00:00","2014-04-03 00:00:00","2014-04-01 00:00:00"]; $('.datePick', this.$el).datepicker( beforeShowDay: function (date) { for(let date1 of active_dates1){ if (date.getTime( ...

Can someone point me to the typescript build option in Visual Studio 2019 Community edition?

When I created a blank node.js web app on VS 2015, there was an option under project properties called "TYPESCRIPT BUILD" that allowed me to configure settings like combining JavaScript output into a single file. After upgrading to VS 2019 Community, I ca ...

Ways to invoke a component function from another component

Hello all, I am currently learning Angular and I have encountered an issue where I need to call a function from one component in another component. Most of the solutions I found online involve scenarios where the components are either child or sibling comp ...

"Exploring the process of connecting a `mat-select` element with dynamically changing data

Having trouble binding data from an API to a mat-select control due to the asynchronous nature of the call. The issue arises because the mat-select control is set before the results are fetched. Any suggestions on how to resolve this? app.component.html ...

Dealing with a missing item in local storage in an Angular application

When working with local storage, I have a function that saves certain values and another method that reloads these values. However, what is the best approach to handle missing items in the local storage? This could happen if a user deletes an item or if it ...

A guide to effectively unit testing StripeJs within the Karma testing framework for Angular 8

I'm currently facing a challenge in unit testing a payment component that relies on StripeJS. In my 'ng-app.js' file, I import it as follows: stripe: /*@ngInject*/ function ($ocLazyLoad) { return $ocLazyLoad.load({ ...

get a collection of chosen files in angular 7 and save them to

Greetings, I am currently working on a project and encountering an issue. I need to download multiple selected files from a list, but despite my efforts in searching for a solution, I have been unable to find one. While I can successfully download single f ...

Ways to capture targeted requests

Utilizing NestJS and Angular 2, I have noticed that both frameworks have a similar approach when it comes to working with Interceptors. I am currently looking for the best practice to identify specific requests in order to perform additional tasks. When d ...

Encountering an issue with Next.js, Typescript, and mongoose when attempting to use `let cached = global.mongoose

I attempted to create a cached mongoose connection for my Next.js + Typescript application, but the code I used was: let cached = global.mongoose; if (!cached) { cached = global.mongoose = { conn: null, promise: null }; } The use of global.mongoose res ...

Exploring the world of logging in Nestjs to capture response data objects

I'm eager to implement logging for incoming requests and outgoing responses in NestJs. I gathered insights from various sources including a post on StackOverflow and a document on NestJs Aspect Interception. I'd love to achieve this without rely ...

Ways to import a library in JavaScript/TypeScript on a web browser?

I'm currently working on a project that involves a TypeScript file and an HTML page. Right now, I am loading the necessary libraries for the TypeScript file in the HTML Page using script tags like <script src="https://unpkg.com/<a href="/cd ...

How do I insert items into an ES6 Map using a specific object key/value type in Typescript?

I'm looking to utilize Maps instead of object maps to define keys and values. However, it appears that Typescript doesn't fully support index types for ES6 Maps. Are there any alternatives or workarounds available? Furthermore, I want to enforce ...

Using Angular with Google Maps: Learn how to retrieve a list of markers from a map and implement onClick events for each one

I am currently using the AGM angular module for Angular 2+ to integrate the Google Map API. In my project, I have a directive that displays waypoints as markers on the map using the Google Map Directions Service. Now, I am looking for a way to handle the ...

Unable to render an array created in the Angular constructor

Caution: The IP address collection mentioned here is solely for a school project to simplify the process. Rest assured, it will not be published online. Actual inquiry: I'm currently at a loss and quite desperate. Unable to identify the root cause of ...

Adding a new line after a comma in a table cell on Angular 2

I am currently using *ngFor to dynamically populate a table sourced from a mongoDB. Within one of the cells, I have an array of strings listed as string1, string2, string3, string4. How can I display these strings in the format below: string1, string ...

TypeScript - patiently anticipating the completion of nested for loops

Currently, I am working on a task that involves implementing two nested for loops in my code. The primary objective of the first loop is to make an API call, which is dependent on the IDs selected by the user. Unfortunately, the limitation of passing only ...

Authenticate the user using IdentityServer

I'm looking to set up a system where the user is automatically redirected to the login page after an hour, but I'm having trouble configuring this on my server. I've added both AccessTokenLifetime and AbsoluteRefreshTokenLifetime on my ID se ...

Retrieving the value of a selected option in Angular

I have the following dropdown select in my HTML and I am currently retrieving the text content of the selected option. How can I access the value attribute instead? Here is the dropdown select: <form [formGroup]="angForm" class="form-inline my-5 my-l ...