What is the best approach to apply type casting in an *ngFor loop for an array containing a variety of types in Angular?

I am facing a scenario where I have two objects named DeviceA and DeviceB, both belonging to the same parent class called Device.

interface Device {
  shared: string
}

interface DeviceA extends Device {
  attributeA: string[]
}

interface DeviceB extends Device {
  attributeB: number
}

Within my component, there is an array containing instances of DeviceA and DeviceB.

@Component({
  selector: 'my-app',
  templateUrl: './app.component.html',
  styleUrls: [ './app.component.css' ]
})
export class AppComponent  {
  devices: (DeviceA | DeviceB)[] = [];
}

I am looking for guidance on how to iterate through this array in my template. The desired outcome is as follows:

<div *ngFor="let device of devices">
  <div *ngIf="'attributeA' in device">
    <span *ngFor="let str of device.attributeA">{{ str }}</span>
  </div>
  <div *ngIf="'attributeB' in device">
    <span>{{ device.attributeB }}</span>
  </div>
</div>

The issue arises during compilation with the error message: Property 'attributeA' does not exist on type 'DeviceA | DeviceB'

Any suggestions on how to properly handle typecasting in this situation?

Answer №1

When TypeScript is compiled, interfaces are lost and become objects, making it impossible to infer class types from interfaces using instanceOf. You can either continue using interfaces and infer types from parameters, or use classes with instanceOf. To implement these operations in HTML with Angular, pipes are used. Check out the examples below and the code here.

The code on StackBlitz demonstrates how to cast types in HTML.

First method: utilizing interfaces

Create a pipe in Angular

device-type.pipe.ts

@Pipe({
    name: 'isDeviceType'
})
export class IsDeviceTypePipe implements PipeTransform {
    transform(value: DeviceA|DeviceB, expect: 'a' | 'b'): boolean {
        if (expect==='a') return !!(value as DeviceA).attributeA;
        return !!(value as DeviceB).attributeB;
    }
}

Second method: using classes

Create a pipe in Angular

device-type.pipe.ts

@Pipe({
    name: 'isDeviceType'
})
export class IsDeviceTypePipe implements PipeTransform {
    transform(value: DeviceA|DeviceB, expect: 'a' | 'b'): boolean {
        if (expect==='a') return value instanceOf DeviceA;
        return value instanceOf DeviceB;
    }
}

and in your HTML

<div *ngFor="let device of devices">
  <div *ngIf="device | isDeviceType:'a'">
    <span *ngFor="let str of device.attributeA">{{ str }}</span>
  </div>
  <div *ngIf="device | isDeviceType:'b'">
    <span>{{ device.attributeB }}</span>
  </div>
</div>

Review the code:

https://stackblitz.com/edit/angular-xtr54e?file=src/is-device-type.pipe.ts

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

Attention: issue TS18002 has been detected - The 'files' configuration file is currently blank

I'm currently working with TypeScript version 2.1.5.0. My setup includes the grunt-typescript-using-tsconfig plugin, but I'm encountering an error when running the task. The issue seems to be related to the property "files":[] in my tsconfig.jso ...

Why is it that my service in the Angular project doesn't fetch data after I make changes and reload the page?

When a user selects a customer, I have a method that fetches the customer's ID from the database and saves it to local storage. However, if I make changes to my code and refresh the page after selection, it doesn't fetch the customer ID. How can ...

Sending data from the LoginComponent to the RootComponent

I am facing a challenge with implementing *ngIf to hide the login/logout option in the navbar based on the user's authentication status. When logged in, I want to hide the logout link. Here is my current setup. app.component.ts import { Component, O ...

Definitions for images in the following format

I am currently utilizing typescript in conjunction with NextJs and next-images. Here is the code snippet: import css from "./style.sass"; import img from './logo.svg'; import Link from 'next/link'; export default () => <Link hre ...

What is the process for generating an injected and a const object instance?

How can an instance of class A obtain a dependency on an instance of class O, while remaining a singleton for others? @injectable() class O{} // A must be single instance! @injectable() class A{ constructor(o: O){ console.log( 'is A inst ...

Is there a way to switch (transpose) the rows and columns of a dynamically generated HTML table from Highchair in Angular 12?

Click here to view the code for creating a highchart table. In the provided example, I have successfully implemented a highchart table using the code below. Highcharts.chart('container', { title: { text: 'Solar Employment Growth by Sec ...

SvgIcon is not a recognized element within the JSX syntax

Encountering a frustrating TypeScript error in an Electron React App, using MUI and MUI Icons. Although it's not halting the build process, I'm determined to resolve it as it's causing issues with defining props for icons. In a previous pro ...

What is the proper way to include special symbols such as "++" and "#" in a request?

I am facing an issue while trying to make a request to an ASP .NET CORE API from an Angular application using Typescript. Upon sending the request, the API searches in an SQL database for any rows with the specified value. The problem arises when attempt ...

The ngOnChanges lifecycle hook is triggered only once upon initial rendering

While working with @Input() data coming from the parent component, I am utilizing ngOnChanges to detect any changes. However, it seems that the method only triggers once. Even though the current value is updated, the previous value remains undefined. Below ...

When working with the Google Sheets API, an error occurred: "this.http.put(...).map is not a valid

Having difficulty with a straightforward request to the Google Sheets API using the PUT method. I followed the syntax for http.put, but an error keeps popping up: this.http.put(...).map is not a function. Here's my code snippet: return this.http ...

Exploring the Power of Asynchronous Operations with Await and Async in

I have a basic understanding of how to use await/async in Angular 2 with the following example: async getValueWithAsync() { const value = <number>await this.resolveAfter2Seconds(20); console.log(`async result: ${value}`); } In my Angular ...

Enhancing IntelliSense to recognize exports specified in package.json

I have a package.json file where I define various scripts to be exported using the exports field. "exports": { ".": { "default": "./dist/main.es.js", "require": "./dist/main.cjs.js", ...

Every time I try to run my Angular app, it crashes. I've already tried reinstalling Node and rebooting

After attempting to relocate an angular project into a different folder yesterday, I encountered issues when trying to start the app using ng serve. Interestingly, creating a new project and running ng serve resulted in the same error. Both projects were ...

Why is it that my side effect action is unable to identify the return action type property?

I've been working on setting up SideEffect for my authentication store, but every time I trigger the action (TrySignIn), I keep encountering this error: "AuthEffects.authSignin" dispatched an invalid action ERROR TypeError: Actions must hav ...

Navigating in express

Here is the structure I am working with: server.ts routes/ index.ts homeRoute.ts In server.ts: let app = Express(); app.use(router); In routes/index.ts: const routes = Router(); export default function router() { routes.use('/home' ...

Cease monitoring for operations within NGRX

One challenge I am facing is related to common components that dispatch actions. Take "Status Selection" for example, a component used in various modules of my application. In each module, there are effects triggered by this action. However, the issue ari ...

Is there a method to run code in the parent class right after the child constructor is called in two ES6 Parent-Child classes?

For instance: class Parent { constructor() {} } class Child { constructor() { super(); someChildCode(); } } I need to run some additional code after the execution of someChildCode(). Although I could insert it directly there, the requirement is not to ...

Guide on streamlining interface initialization within a React project using Typescript

Working on my Typescript project, I consistently utilize an interface within the State of various components: interface Item { selectedValue: string originalSelectedValue: string loading: boolean disabled: boolean isValid?: boolean } ...

Execute the render function of the components that have been passed as

I've been grappling with a challenge lately - figuring out how to invoke a passed component's render function within another function. Let's say I have two functions named A and B: export const A = (value: any) => { return ( <div& ...

What is the best way to construct an interface in TypeScript with a variable number of properties?

Is it possible to create an interface in typescript with a variable number of string properties, ranging from 5 to potentially 50? ...