4

I want to iterate over key value pairs from an object and display two items per row. I looked at other examples like this one Example but I couldn't figure out how to add keyvalue to this.

Here is my code:

<div *ngFor="let item of book.bookData.privateData | keyvalue; index as i; let even = even">
    <div fxFlex="100" fxLayout="row" *ngIf="even">
        <div fxFlex="50" fxLayout="column">
            Key: <b>{{book.bookData.privateData[i].key}}</b> and Value: <b>{{book.bookData.privateData[i].value}}</b>
        </div>
        <div fxFlex="50" fxLayout="column">
            Key: <b>{{book.bookData.privateData[i+1].key}}</b> and Value: <b>{{book.bookData.privateData[i+1].value}}</b>
        </div>
    </div>
</div>

This does not work since there is no key and value attribute on the privateData object, the attributes are assigned to item.

Thanks in advance for any help!

EDIT:

The following is a working example of what I'm trying to achieve, but it's clearly not an efficient way:

<div *ngFor="let item of bookState.bookData.privateData | keyvalue; index as i; let even = even">
    <div fxFlex="100" fxLayout="row" *ngIf="even">
        <div fxFlex="50">
            <div *ngFor="let deeperItem1 of bookState.bookData.privateData | keyvalue; index as i2">
                <div *ngIf="i2 === i">
                    <b>INDEX {{i2}} and {{i}} </b>Key: <b>{{deeperItem1.key}}</b> and Value: <b>{{deeperItem1.value}}</b>
                </div>
            </div>
        </div>
        <div fxFlex="50">
            <div *ngFor="let deeperItem2 of bookState.bookData.privateData | keyvalue; index as i3">
                <div *ngIf="(i3-1) === i">
                    <b>INDEX {{i3}} and {{i}} </b>Key: <b>{{deeperItem2.key}}</b> and Value: <b>{{deeperItem2.value}}</b>
                </div>
            </div>
        </div>
    </div>
</div>

EDIT2:

To specify the question: The problem is not that the keyvalue pipe is not working, the problem is how to efficiently add indexes to these to display x-amount (in my case 2) of items per row. Sorry for any misunderstanding!

lennertr
  • 150
  • 1
  • 10
  • could you add a schema of what are you looping on? what is in item ? – SeleM Oct 29 '18 at 09:00
  • What is the `keyvalue` pipe exactly doing or trying to do? Can you share the interface for `pirvateData`? – Caius Oct 29 '18 at 09:01
  • privateData contains an arbitrary amount of key value pairs as a JSON response. key and value are both strings. – lennertr Oct 29 '18 at 09:09

2 Answers2

4

It depends on the structure you are iterating. Here you can find the following example:

@Component({
  selector: 'keyvalue-pipe',
  template: `<span>
    <p>Object</p>
    <div *ngFor="let item of object | keyvalue">
      {{item.key}}:{{item.value}}
    </div>
    <p>Map</p>
    <div *ngFor="let item of map | keyvalue">
      {{item.key}}:{{item.value}}
    </div>
  </span>`
})
export class KeyValuePipeComponent {
  object: {[key: number]: string} = {2: 'foo', 1: 'bar'};
  map = new Map([[2, 'foo'], [1, 'bar']]);
}

In your case, if you do not have a key value structure, use the map case like this:

<div *ngFor="let item of book.bookData.privateData | keyvalue; let i=index">
    <div fxFlex="100" fxLayout="row" *ngIf="i%2 === 0">
        <div fxFlex="50" fxLayout="column">
            Key: <b>{{item.key}}</b> and Value: <b>{{item.value}}</b>
        </div>
        <div fxFlex="50" fxLayout="column" *ngIf="(book.bookData.privateData|keyvalue)[i+1]">
            Key: <b>{{(book.bookData.privateData|keyvalue)[i+1].key}}</b> and Value: <b>{{(book.bookData.privateData|keyvalue)[i+1].value}}</b>
        </div>
    </div>
</div>
Tommy
  • 2,355
  • 1
  • 19
  • 48
  • your answer works but does not display two items per row – lennertr Oct 29 '18 at 09:11
  • @lennertr Post your `array` structure so he could restructure his code – Basheer Kharoti Oct 29 '18 at 09:35
  • 1
    That's a very tricky one but i finally found a solution. You can apply the pipe within the view by using `(array|pipe)` and access the next element with `(array|pipe)[i+1]`. To make sure that the next element exists using `*ngIf="(book.bookData.privateData|keyvalue)[i+1]"` hopefully helps. I updated my answer. – Tommy Oct 29 '18 at 09:35
  • 1
    Thanks a lot @Tommy!! That's what I was looking for :) – lennertr Oct 29 '18 at 09:46
  • follow up question: can you think of a way to use two way data binding within this? `` works only in one way, it doesn't store changes to the book.bookData.privateData object for the corresponding key. – lennertr Oct 29 '18 at 10:19
  • nevermind that's the solution to that: `[(ngModel)]="loanState.loanData.privateData[item.key]"` – lennertr Oct 29 '18 at 10:24
  • Sorry for all the follow ups, not sure if I should open a new question for this? When using two way data binding with ngModel on the second column it throws "Cannot have a pipe in an action expression" `[(ngModel)]="(book.bookData.privateData|keyvalue)[i+1].value"` is there a workaround? – lennertr Oct 29 '18 at 10:43
  • Have you tried to use `(ngModelChange)` and `[ngModel]` instead of `[(ngModel)]` syntax as purposes [here](https://stackoverflow.com/a/39643180/3623608)? Otherwise i would suggest to open up a new question. – Tommy Oct 29 '18 at 11:57
-1

@lennertr please update your angular version to 6.1.0 to use keyvalue pipe. because keyvalue is available from version 6. and please refer below demo.

https://stackblitz.com/edit/angular-keyvalue

  • That's not the problem. I'm using an angular version greater than that and the keyvalue pipe works fine. The problem is how to use indexes with the keyvalue pipe to display a specific amount of items per row. – lennertr Oct 29 '18 at 09:22