Can ngForIn be used in angular 4?
Asked Answered
C

5

12

I am trying to iterate over the properties of an object using *ngFor but using in. When I try to do this

@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}
}

I get the error message:

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

I have included FormsModule and BrowserModule in the imports section of the @NgModule for this component.

Is it possible to use ngForIn on li and if not is there an idiomatic alternative?

Confab answered 17/7, 2017 at 18:36 Comment(6)
Is it this problem?Flavius
@Flavius No, I want to iterate over object properties explicitly, not iterate over an iterable like an array.Confab
medium.com/@jsayol/…Oscillation
@AJT_82 Ah ok, I was hoping I could get this done without writing a directive, but I'll just do that then. Thank you!Confab
Yeah, it is not included in the maaaagical Angular box unfortunately, you need to tinker a bit. Good luck and have fun with that :) One option is to use a custom pipe of course, just to mention that (more common) option.Oscillation
you want let i of obj NOT let i in objVenerate
W
9

As AJT_82 mentioned in comment you can create special directive for such purposes. It will be based on 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

Wealth answered 17/7, 2017 at 19:8 Comment(1)
Those who stumble across this and have upgraded angular ngForOf no longer implements OnChanges. The trick is to just set the value of your forOf into the super.ngForOf. Full directive here https://mcmap.net/q/936419/-can-ngforin-be-used-in-angular-4Pitiless
G
3

The easiest approach would be to turn your object into an array using Object.values() and Object.keys(). Check out this plunker for an example.

If you want access to the keys as well as the value you can include an index in your *ngFor.

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);
  }
}
Glyceride answered 17/7, 2017 at 19:3 Comment(0)
P
1

Things have changed a bit (at Angular 9) so the answer by yurzi wont work but on the similar lines setting your 'forOf' value into 'super.ngForOf' will do the trick

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;
    }   
  } 
}
Pitiless answered 29/5, 2020 at 13:3 Comment(0)
C
0

Object.keys and Object.values can be assigned to public properties on the component and then they are available as functions in a template. eg

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

//somewhere in template
...*ngFor="let key of objectKeys(someObject)"...
Center answered 24/1, 2019 at 19:31 Comment(0)
D
0

An alternative would be to use the keyValue pipe.

<span *ngFor="let keyValue of myObject | keyValue">
    {{ keyValue.key }} - {{ keyValue.value }}
</span>
Dogoodism answered 13/3, 2023 at 16:24 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.