The Angular 4 directive appears to be showing 'undefined' when accessing @input values

I am a beginner when it comes to using Angular directives, so I created a directive like this:

import { Directive, ElementRef, Input, Output } from '@angular/core';

@Directive({
  selector: "[bonusCard]"
})
export class BonusCard {
  @Input() bonus: string;
  @Input() amount: string;
  @Input() isSelected: string;
  constructor(private el: ElementRef){
    el.nativeElement.innerHTML = this.getTemplate()
  }

  getTemplate(){
    return ''+
      '<div>'+
        '<div class="bonus">'+this.bonus+'</div>'+
        '<div class="amount">'+this.amount+'</div>'+
        '<div class="radio '+(this.isSelected?'is_selected':'')+'"></div>'+
      '</div>' +
      '';
  }
}

I am using this directive in my template like this:

  <div class="column col-3" *ngFor="let b of bonusJson; let i = index" bonusCard [bonus]="b.bonus" [amount]="b.amount" [isSelected]="i==activeBonus"></div>

Where the variables are defined as follows:

bonusJson = [{
    bonus:1500,
    amount:2000
  },{
    bonus:1000,
    amount:1000
  },{
    bonus:500,
    amount:500
  },{
    bonus:0,
    amount:100
  }];
  activeBonus = 0;

However, the view is not rendering correctly and it is showing undefined in the view as depicted in the image below:

$https://i.sstatic.net/hlFir.png

Answer №1

Summary

Avoid using the value of @Input() properties in the constructor. Instead, utilize ngOnInit().


Detailed Explanation

Executing code in the constructor may lead to issues as the Inputs are not yet bound.

During application bootstrapping, Angular creates components using the new keyword before the change detection cycle is active. This means that the constructor runs before the component is attached to the DOM and the input values are available.

As explained in this guide by an Angular team member, the constructor should focus on basic initialization tasks and dependency injection, not complex logic.

On the other hand, the ngOnInit() hook is a more suitable place for initialization. At this point, Angular has bound inputs and attached the component to the DOM, making the input values accessible.


Specific Answer

In your scenario, calling getTemplate() in the constructor leads to undefined values for this.bonus, this.amount, and this.isSelected due to input values not being bound yet.

To resolve this issue, transfer the logic to the ngOnInit method.

export class BonusCard implements OnInit {
  @Input() bonus: string;
  @Input() amount: string;
  @Input() isSelected: string;

  constructor(private el: ElementRef) {}

  ngOnInit() {
    this.el.nativeElement.innerHTML = this.getTemplate()
  }

Additional Recommendations

Consider the following tips to enhance your code:

  • Use DomSanitizer to bind HTML content in components for security. More details here.
  • Utilize template strings for multi-line templates by replacing quotes with backticks (`).
  • Prefer === over == for type-safe comparisons.

For example:

[isSelected]="i===activeBonus"

and

return `<div>
          <div class="bonus"> ${this.bonus} </div>
          <div class="amount"> ${this.amount} </div>
          <div class="radio ${this.isSelected?'is_selected':''}"></div>
        </div>`;

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

How can you utilize an injected service from inside a Class decorator in Typescript/NestJS?

Can an injected service be accessed from within a Class decorator? I aim to retrieve the service in the personalized constructor: function CustDec(constructor: Function) { var original = constructor; var f: any = function (...args) { // How can I ...

Even when it appears to be chaotic, the TypeScript array of numbers always manages to find its way back to being sorted

I recently developed a function that aims to verify if an array of numbers is sorted: const checkIfSorted = (numbers: number[]) => { return numbers === numbers.sort((a, b) => a - b); }; checkIfSorted([4, 2, 8, 7, 3, 10, 1, 5, 9, 6]); // This cur ...

Innovative approaches to enhancing Feathers services through the use of relational data in design patterns

I'm in the process of developing a straightforward application that involves a one-to-many relationship between data entities. Specifically, I am working with feathers js and sequelize (utilizing sqlite) to create a system where each site can have mul ...

What could be causing the profile detail to be missing in the home component?

To optimize performance, I am trying to only call the profile API if there is no detail available. This way, the API will only be called when necessary, such as when the user first visits the home component. I am using route resolve to ensure that the data ...

What is the correct way to convert a non-observable into an observable?

Can I convert a non-observable into an observable to receive direct image updates without having to refresh the page, but encountering this error: Type 'EntityImage[]' is missing the following properties from type 'Observable<EntityImage ...

Using Subject to send information between components that are not directly related

For some reason, I am struggling to collect the data that is being transmitted by the Subject. The console log indicates that the data is being passed around to various places except for the final destination. Even when I subscribe to the subject, nothing ...

Setting up ESLint and Prettier with TypeScript on Node 20: A Guide

I attempted to set up Prettier with ESLint and crafted a configuration in settings.json to rectify errors upon saving, but the errors only manifest in .js files and not .ts files. How can I adjust this? Dependencies: "@eslint/js": "^9.4.0& ...

Implementing a dynamic star rating system in Angular

I am working with an array of ratings that looks like this: "rating": [ { "sno": 1, "question": 13, }, { "sno": 2, ...

Angular 8: Implementing Form Validation with a Boolean Flag

Within my HTML code, I have a function (change)="limitUser($event)". In Typescript, I utilize a for loop to iterate through each element and determine if the value is less than 10. If it exceeds 10, the inValid = true condition is set. All form fields in m ...

Tips for validating dates in Angular 4

Recently, I have started working with Angular and in my application I am facing a challenge regarding date validation. I need to validate a date input and display an error message based on the validation criteria. To achieve this, I am utilizing ngx-boots ...

Delete the right-hand p-timeline-event-opposite margin from the Angular 17 PrimeNG Timeline

I am currently utilizing the PrimeNG Timeline module to showcase a few straightforward horizontal timelines with content placed on the top side in a table format. How can I eliminate the space allocated by the .p-timeline-event-opposite section? Upon inspe ...

Integrating Laravel 5.6 with Angular 6 for seamless functionality

For my project, I am considering using Laravel as the backend and Angular as the frontend. There are two methods that I can use to integrate these frameworks: 1) I could integrate both frameworks by utilizing an API service, or 2) I could opt for a monol ...

Setting up the Font Awesome Pro version in Angular using the Font-Awesome package

The process of installing the pro version of Angular Font-awesome began with setting up my registry using these commands: npm config set "@fortawesome:registry" https://npm.fontawesome.com/ && \ npm config set "//npm.fontawesome.com/:_authTo ...

Vue HeadlessUI Error: The function vue.defineComponent is not recognized

Trying to incorporate @headlessui/vue into my nuxt project has been a challenge. My attempt at using it looks like this: <template> <Menu> <MenuItems> <MenuItem>Item</MenuItem> </MenuItems> </Menu&g ...

Tips for creating a personalized asynchronous Express handler that seamlessly receives specific typed parameters

In my quest to create a unique Express endpoint wrapper, I aim to wrap async functions and handle errors effectively. The current implementation is basic but functional: import type {Request, RequestHandler, Response} from 'express'; type Handle ...

What could be the reason behind the absence of this.props.onLayout in my React Native component?

In my React Native app, I have the below class implemented with Typescript: class Component1 extends React.Component<IntroProps, {}> { constructor(props){ super(props) console.log(props.onLayout) } ... } The documentation for the View ...

How can a property be made mandatory in Typescript when another property is set as optional?

Currently, I am unsure about what to search for in order to fulfill the following requirement. Specifically, I am utilizing the given type for react props: interface ComponentProps { message : string, minValue? : number, minValueValidationMessage? ...

Angular observable goes unnoticed

I'm venturing into creating my own observable for the first time, and I'm running into issues. The data is visible in the console, but it doesn't show up when the component loads. Can anyone help me troubleshoot this problem? Here's a ...

Utilizing Angular's custom builder for asset revisioning

After coming across this specific inquiry on Stack Overflow, my goal is to implement a @angular-builders/custom-webpack:browser with a personalized Webpack setup that incorporates html-loader and file-loader to alter the names of asset files with their has ...

What is the rationale behind ngOnInit not being a private method in Angular?

After extensively checking both code samples and even the official documentation, I am still unable to find the information I need. Turning to Google has also yielded no results. The question that baffles me is: ngOnInit() { ... } Why do we use this syn ...