Is ngForIn a valid directive in Angular 4?

While attempting to loop over the properties of an object using *ngFor with in, I encountered a challenge. Here is a sample code snippet:

@Controller({
  selector: 'sample-controller',
  template: `
    <ul>
      <li *ngFor="let i in obj">
        <b>{{i}}</b>: {{obj[i]}}
      </li>
    </ul>`
})
class SampleController {
  obj = {a: 1, b: 2}
}

This led to an error message:

Can't bind to 'ngForIn' since it isn't a known property of 'li'.

In this component's @NgModule section, FormsModule and BrowserModule are already included in the imports.

So, my question is whether it is viable to use ngForIn on li, and if not, what would be the recommended approach?

Answer №1

In response to AJT_82's suggestion, you have the option to develop a unique directive specifically designed for this purpose. This directive will be built upon the NgForOf<T> directive:

interface NgForInChanges extends SimpleChanges {
  ngForIn?: SimpleChange;
  ngForOf?: SimpleChange;
}

@Directive({
  selector: '[ngFor][ngForIn]'
})
export class NgForIn<T> extends NgForOf<T> implements OnChanges {

  @Input() ngForIn: any;

  ngOnChanges(changes: NgForInChanges): void {
    if (changes.ngForIn) {
      this.ngForOf = Object.keys(this.ngForIn) as Array<any>;

      const change = changes.ngForIn;
      const currentValue = Object.keys(change.currentValue);
      const previousValue = change.previousValue ? 
                            Object.keys(change.previousValue) : undefined;
      changes.ngForOf =  new SimpleChange(previousValue, currentValue, change.firstChange);

      super.ngOnChanges(changes);
    }
  }
}

Plunker Example

Answer №2

To easily convert your object into an array, you can utilize the Object.values() and Object.keys() methods. If you need both keys and values, consider adding an index in your ngFor loop. Take a look at this plunker for a practical example.

Template:

<ul>
  <li *ngFor="let item of array; let index = index;">
    <b>{{keys[index]}}</b> value: {{item}}, index: {{index}}
  </li>
</ul>

Component TypeScript:

export class App {
  obj = {a: 1, b: 2}
  array = [];
  keys = [];
  constructor() {
  }

  ngOnInit() {
  this.array = Object.values(this.obj);
  this.keys = Object.keys(this.obj);
  }
}

Answer №3

There have been some updates at Angular 9, rendering yurzi's previous answer ineffective. However, a similar approach of setting the 'forOf' value to 'super.ngForOf' can solve the issue.

import {   ContentChildren,   Directive,   Input,   IterableDiffers,   NgIterable,   OnChanges,   SimpleChanges,   TemplateRef,   ViewChildren,   ViewContainerRef } from "@angular/core";

import { NgForOf } from "@angular/common";

export interface Selectable<T> {   selected: boolean;   isActive: boolean; }

@Directive({   
 selector: "[optionsFor][[optionsForOf]]" }) 
 export class OptionsForDirective<T extends Selectable<any>,U extends NgIterable<T> = NgIterable<T>> 
     extends NgForOf<T, U> implements OnChanges {   

   _editMode = false;   
   _forOf: Array<Selectable<any>>;

  @Input()   
  set iefOptionsForOf(value: any) {
    this._forOf = value;
    console.log("set of: ", value);   
  }

  @Input()   
  set iefOptionsForEditMode(value: boolean) {
    this._editMode = value;
    console.log("set edit mode: ", value);   
  }

  constructor(
    public _viewContainer2: ViewContainerRef,
    public _templateRef2: TemplateRef<any>,
    public _differs2: IterableDiffers   ) {
    super(_viewContainer2, _templateRef2, _differs2);   
  }

  ngOnChanges(changes: SimpleChanges) {
    if (changes.optionsForOf) {
      const origNgForOf: Array<Selectable<any>> = this._forOf;
      let filtered = origNgForOf;
      if (origNgForOf) {
        filtered = origNgForOf.filter(s => {
          return this._editMode || s.isActive !== false;
        });
      }
      console.log("filtered", filtered);

      super.ngForOf = filtered as any;
    }   
  } 
}

Answer №4

Object.keys and Object.values can be set as public properties on the component, enabling them to be used as functions within a template. For example:

//Inside the component
public objectKeys = Object.keys;
public objectValues = Object.values;

//Within the template
...*ngFor="let key of objectKeys(someObject)"...

Answer №5

Another option could be utilizing the keyValue pipe.

<div *ngFor="let pair of myObject | entries">
    {{ pair.key }} - {{ pair.value }}
</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 to Override Global CSS in a Freshly Created Angular Component

My CSS skills are a bit rusty and I need some assistance with a project I'm working on. The project includes multiple global CSS files that have properties defined for different tags, such as .btn. However, these global CSS files are causing conflicts ...

Error: The NgTools_InternalApi_NG_2 member is not exported in compiler-cli/src/ngtools_api

Currently, my ng serve command is not working and showing the following error message: ERROR in node_modules/@angular/compiler-cli/index.d.ts(20,10): error TS2305: Module '"./node_modules/@angular/compiler-cli/src/ngtools_api"' has no expor ...

Having trouble retrieving my .scss bundle file from a different directory

I am trying to access my .scss file that imports all the other .scss files (they are all imported in styles.scss). Even though I have added the path to my angular cli file, the file is still not accessible and no styling is being applied to my html elemen ...

Encountering an issue while trying to upgrade angular from version 8 to version 16. The error message states: "Unable to bind to 'something' as it is not recognized as a property of 'something'."

Currently in the process of upgrading an old Angular 8 project to Angular 16. The update has been completed, however, when compiling the project I am encountering multiple errors related to components not being able to bind to certain properties that are s ...

What is the rationale behind allowing any type in TypeScript, even though it can make it more challenging to detect errors during compile time?

Why is it that all types are allowed in TypeScript? This can lead to potential bugs at runtime, as the use of type "any" makes it harder to detect errors during compilation. Example: const someValue: string = "Some string"; someValue.toExponentia ...

Different return types of a function in Typescript when the parameter is either undefined or its type is explicitly asserted vary

Addressing Complexity This code may seem simple, but the use case it serves is quite complex: type CustomFunction = <B extends ['A']>( param?: B ) => B extends undefined ? 'TYPE_1' : 'TYPE_2' // Usage examples: co ...

Steps for inserting a dropdown menu into a kendo grid

Hey there, I'm currently working on adding a drop-down list to a kendo-grid column but I'm facing an issue where the data is not loading into the drop-down list. My goal is to populate the data from an array. Here's the code snippet from t ...

What is the reason behind TypeScript prohibiting the assignment of a className property to a context provider?

Embarking on my first journey with TypeScript, I am in the process of reconfiguring a React application (originally built with create-react-app) to use TS. Specifically, I am working with function components and have introduced a global context named Order ...

Tips for deploying an Angular Universal 9 application on a live server

Our Angular 9 app functions perfectly when deployed on an IIS server. We also have a version of the app that has been integrated with Universal by another company. Now, we need to figure out how to deploy our app with server-side rendering into productio ...

Disabling the scrollbar within angular elements

Trying to remove the two scrollbars from this code, but so far unsuccessful. Attempted using overflow:hidden without success filet.component.html <mat-drawer-container class="example-container" autosize> <button type="button&qu ...

Troubleshooting React child problems in TypeScript

I am facing a coding issue and have provided all the necessary code for reference. Despite trying numerous solutions, I am still unable to resolve it. export class JobBuilderOptimise extends React.Component<JobBuilderOptimiseProps & JobBuilderOptim ...

Encountering an issue while attempting to install Font Awesome free

I encountered the following error: npm ERR! code E401 npm ERR! Unable to authenticate, need: Basic realm="https://npm.fontawesome.com/",service="npm.fontawesome.com" npm ERR! A complete log of this run can be found in: npm ERR! C:& ...

modify pseudo element's style in Angular based on a particular condition

I am trying to change the style of :before based on a condition. I attempted to implement the solution provided at , but it did not work as expected. Here is my code: .sender:before { content: ""; width: 0px; ...

What are some strategies for distinguishing between callable and newable types?

I seem to be facing a challenge related to narrowing, specifically the differentiation between Fnc (callable) and Class (newable). The code snippet provided functions in Playground, but the autocomplete feature is lacking, as shown in the first image. My ...

Don't display div if database has certain value - Angular

Is there a way to determine if a specific value exists in a PostgreSQL database? If so, can I then hide an HTML element based on this information? Can CSS and JavaScript be used to hide elements, or is there another method that should be utilized for hidi ...

Testing inherit from a parent class in a unit test for Angular 2

Trying to create a unit test that checks if the method from the base class is being called This is the base class: export abstract class Animal{ protected eatFood() { console.log("EAT FOOD!") } } Here is the class under test: export ...

The error message "TypeError: Unable to access the 'getFullWidth' property of an undefined value when using TSLint and TypeScript" was

I have been using Dan Wahlin's tutorials and online examples to set up Gulp and Typescript, but I am facing an issue with the tslint() function. The problem occurs in my code as follows: node_modules\tslint\lib\language\walker&bso ...

What makes fastify-plugin better than simply calling a regular function?

I recently came across a detailed explanation of how fastify-plugin operates and its functionality. Despite understanding the concept, I am left with a lingering question; what sets it apart from a standard function call without using the .register() metho ...

I'm having trouble retrieving the value from the textbox in my Angular 7 project using TypeScript

I'm currently working with Angular 7 and trying to create a textbox to display its value in an alert. However, I'm facing difficulty in fetching the textbox value in typescript. I would appreciate any guidance on how to proceed with this issue. ...

Error arises when attempting to pass interface props to a component in a React Typescript application

I am currently delving into the world of React js and typescript. As part of my learning process, I have created a demo application that allows users to input their name and age. The app features an ErrorModal that should pop up on the screen whenever inco ...