Angular 12 is throwing an error due to the undefined @Input property

The issue presents a simple problem to comprehend yet proves challenging to resolve. There are 2 key components involved:

  1. CustomerComponent (Parent)
  2. InvoiceComponent (Child)

Currently, I'm passing customer details using

<admin-invoice-form [customer]="customer"></admin-invoice-form>
from the parent component to the child component. However, upon inspecting the input property of the child component in both the constructor() and ngOnInit(), the result returned is undefined.

Take a look at the code snippet below for better clarity,

1. admin.customer.components.ts file

import { Component} from '@angular/core';
import { Customer } from 'src/app/model/customer';
import { CustomerService } from 'src/app/services/customer.service';

@Component({
  selector: 'app-admin-customers',
  templateUrl: './admin-customers.component.html',
  styleUrls: ['./admin-customers.component.css']
})
export class AdminCustomersComponent {
  customerArray: Customer[] = [];
  customer: Customer;

  constructor(private customerService: CustomerService) {
    this.customerService.getAll()
       .subscribe((customer: Customer[]) => {
           this.customerArray = customer;
      });
  }

  setCustomer(customer: Customer) {
    this.customer = customer;
  }
}

2. admin-customers.component.html file

<div class="container">
     <table class="table table-striped table-hover border" datatable [dtOptions]="dtOptions" [dtTrigger]="dtTrigger">
        <thead>
            <tr>
                <th class="text-center">First Name</th>
                <th class="text-center">Last Name</th>
                <th class="text-center">Mobile No</th>
                <th class="text-center">City</th>
                <th></th>
            </tr>
        </thead>

        <tbody>
             <tr *ngFor="let customer of customerArray">
                    <td class="text-center">{{ customer.firstName}} </td>
                    <td class="text-center">{{ customer.lastName }}</td>
                    <td class="text-center">{{ customer.mobileNo }}</td>
                    <td class="text-center">{{ customer.city }}</td>
                   
                    <td class="text-center">
                        <button type="button" class="btn btn-success" (click)="setCustomer(customer)" data-bs-toggle="modal" data-bs-target="#custInvoiceModal">
                            Generate Invoice
                        </button>
                    </td>
                </tr>          
        </tbody>
    </table>
    

    <!-- Invoice Modal: Starts -->
    <div id="printThis"> 
        <div class="modal fade" id="custInvoiceModal" tabindex="-1" aria-labelledby="custInvoiceLabel" aria-hidden="true">
            <!-- Child Component: Starts -->
            <admin-invoice-form [customer]="customer"></admin-invoice-form>
        </div>
    </div>
</div>

3. admin-invoice-form.component.ts file

import { Component, Input, OnChanges, OnInit, SimpleChanges } from '@angular/core';
import { Customer } from 'src/app/model/customer';

@Component({
  selector: 'admin-invoice-form',
  templateUrl: './admin-invoice-form.component.html',
  styleUrls: ['./admin-invoice-form.component.css']
})
export class AdminInvoiceFormComponent implements OnInit, OnChanges{
  @Input('customer') customer: Customer; // provided from its consumer i.e. admin-customers.component.html
  
  
  constructor() { 
    console.log('inside constructor()');
    console.log(this.customer); // Output: undefined
  }
  
  ngOnInit(): void {
    console.log('inside ngOnInit()');
    console.log(this.customer); // Output: undefined   
  }
  
  ngOnChanges(changes: SimpleChanges): void {
    if(changes['customer']){
      console.log('inside ngOnChanges()');
      console.log(this.customer); // Output: undefined
    }
      
  }
  
}

An error arises after introducing *ngIf="customer" in the child component.

https://i.sstatic.net/FN1et.png

Answer №1

The solution below successfully resolved the issue I was facing:

1. Initializing a customer as an empty object within the parent class before invoking the setCustomer(customer) method.

import { Component} from '@angular/core';
import { Customer } from 'src/app/model/customer';
import { CustomerService } from 'src/app/services/customer.service';

@Component({
  selector: 'app-admin-customers',
  templateUrl: './admin-customers.component.html',
  styleUrls: ['./admin-customers.component.css']
})
export class AdminCustomersComponent {
  customerArray: Customer[] = [];
  customer: Customer = new Customer(); // initialize to an empty object initially

  constructor(private customerService: CustomerService) {
    this.customerService.getAll()
       .subscribe((customer: Customer[]) => {
           this.customerArray = customer;
      });
  }

  setCustomer(customer: Customer) {
    this.customer = customer;
  }
}

2. Implement ngOnChange() method and handle the remaining tasks with updated @Input('customer') customer;

import { Component, Input, OnChanges, SimpleChanges } from '@angular/core';
import { Customer } from 'src/app/model/customer';
import { Invoice } from 'src/app/model/invoice';
import { Item } from 'src/app/model/item';

@Component({
  selector: 'admin-invoice-form',
  templateUrl: './admin-invoice-form.component.html',
  styleUrls: ['./admin-invoice-form.component.css']
})
export class AdminInvoiceFormComponent implements OnChanges{
  @Input('customer') customer: Customer; // provided by its consumer, i.e., admin-customers.component.html
  invoice: Invoice; // for storing invoice details
  
  ngOnChanges(changes: SimpleChanges): void {
    if(changes['customer']){
      this.invoice = new Invoice(this.customer);
    }
  }
  
}

Answer №2

I am unsure about where you are invoking the setCustomer() function.

Regardless, I believe one way to resolve your issue is as follows:

<admin-invoice-form *ngIf="customer" [customer]="customer"></admin-invoice-form>

This will ensure that the component is only generated when the customer variable is not null or undefined.

Answer №3

To resolve the issue, simply update the detection strategy to "onpush" in your child component

@Component({
  selector: 'admin-invoice-form',
  templateUrl: './admin-invoice-form.component.html',
  styleUrls: ['./admin-invoice-form.component.css'],
  changeDetection: ChangeDetectionStrategy.OnPush
})

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

Issue with hydration in Next.js while trying to access the persisted "token" variable in Zustand and triggering a loading spinner

Below is the code snippet from _app.tsx where components/pages are wrapped in a PageWrapper component that handles displaying a loading spinner. export default function App(props: MyAppProps) { const updateJWT = useJWTStore((state) => state.setJWT); ...

Oops! Looks like there was an issue finding a matching route for the URL segment in Angular 2

I am currently exploring angular2 and trying to figure out how to incorporate multiple <router-outlets> in a specific template. Despite searching through numerous Q&A, I have been unable to resolve the issue. router.module.ts const routes: Routes = ...

Imitate dependencies using Mocha and Typescript

In my typescript project, I am using mocha. Let's consider two modules: // http.ts export class Http { } // app.ts import Http from './http'; export class App { } I want to know how to mock the Http module while testing the App. The te ...

Unable to transform the singular JSON object received from the server into the necessary format in order to analyze the data

Forgive me if my questions seem simple, as I am new to working with Angular. I'm having trouble understanding how to handle a JSON object received from the server and convert it into a custom datatype to use for rendering HTML using ngFor. I've ...

A guide on incorporating typings for d3-tip in TypeScript 2.0

Seeking to implement tooltips in my charts using the d3-tip library. After installing typings for d3-tip in Typescript 2.0: npm install @types/d3-tip --save The typings appear in my package.json: "dependencies": { "@types/d3": "^4.7.0", "@types/d3- ...

The server failed to respond to the Angular HTTP request

To store data in my database, I have created a function in my component.ts file that invokes a service: ajoutText(newtext: String) { this.dataService.sendtext(newtext); } On the HTML side, it looks like this: <form class="form"> <mat-form-f ...

What are some ways to incorporate advanced/nested type variables when using arrow functions?

Is there a way to use "advanced/nested" type variables, similar to how T is utilized in this function declaration, when working with arrow functions? function wrapInObject<T>(key: string) { return (x: T) => ({ [key]: x }); } I attempted to ach ...

Unable to assign the value 'hello' to an undefined property in TypeScript

I'm attempting to define a class in TypeScript, but I keep encountering the error shown below. Here is the execution log where the error occurs: [LOG]: "adding" [LOG]: undefined [ERR]: Cannot set property 'hello' of undefined class Cust ...

Changing the Text of an Anchor Tag When Clicked in Angular 2

Is there a way to toggle the text between "View" and "Hide" without using JQuery, only Angular? I've tried several methods but none have worked. Can anyone offer guidance on how to achieve this? <a class="history-link view-history-class" id="show- ...

What causes Gun.js to generate duplicate messages within a ReactJs environment?

I need assistance with my React application where gun.js is implemented. The issue I am facing is that messages are being duplicated on every render and update. Can someone please review my code and help me figure out what's wrong? Here is the code s ...

Dealing with arrays in Typescript and flattening them using the RX

Struggling with a problem involving RXJS transformation in an Ionic 2 application. My goal is to flatten a JSON file into objects, here is the simplified JSON structure: [{ "language": "it", "labels": { "name": "Hi", }, "t ...

The function service.foo is not recognized in Angular

My service function is not being recognized by my component import { Injectable } from '@angular/core'; import { ToastController } from '@ionic/angular'; @Injectable({ providedIn: 'root' }) export class LocationService { ...

The specified type '{ children: Element; ref: MutableRefObject<HTMLDivElement>; }' cannot be matched with type 'IntrinsicAttributes & RefAttributes<HTMLDivElement>' in the assignment

Encountering an issue with fowardRef in ReactJS where everything seems fine, but an error pops up: Type '{ children: Element; ref: MutableRefObject<HTMLDivElement>; }' is not assignable to type 'IntrinsicAttributes & SectionContent ...

How can express.js be properly installed using typescript?

Currently, I am in the process of setting up a new project that involves using express.js with typescript integration. Would it suffice to just install @types/express by running the following command: npm install @types/express Alternatively, do I also ...

Tips for integrating Reactjs with Chessboard.js

Recently, I stumbled upon chessboardjs (https://chessboardjs.com/) as a way to hone my React coding skills. However, I hit a roadblock while trying to implement a simple example of displaying the chess board in my application. The documentation instructed ...

What causes a function loss when using the spread operator on window.localStorage?

I am attempting to enhance the window.localStorage object by adding custom methods and returning an object in the form of EnhancedStorageType, which includes extra members. Prior to using the spread operator, the storage.clear method is clearly defined... ...

Exploring alternative methods for accessing object values in TypeScript using a string object path without relying on the eval function

If we have an object in JS/typescript structured like this: var obj = { a: { b:{ c:1 } } } And a string "b.c" is given, how can we evaluate this using only the variables provided and retrieve the value 1 from the object without rel ...

The or operator in Typescript does not function properly when used as a generic argument

My current configuration is as follows: const foo = async <T>(a): Promise<T> => { return await a // call server here } type A = { bar: 'bar' } | { baz: 'baz' } foo<A>({ bar: 'bar' }) .then(response =& ...

What does the "start" script do in the package.json file for Angular 2 when running "concurrent "npm run tsc:w" "npm run lite"" command?

What is the purpose of concurrent in this code snippet? "scripts": { "tsc": "tsc", "tsc:w": "tsc -w", "lite": "lite-server", "start": "Concurrent npm run tsc:w npm run lite" } ...

"Overcoming obstacles in managing the global state of a TypeScript preact app with React/Next signals

Hello, I recently attempted to implement a global app state in Preact following the instructions provided in this documentation. However, I kept encountering errors as this is my first time using useContext and I suspect that my configuration might be inco ...