Angularでオブジェクトを反復する


130

Angular 2 Alpha 28でいくつかのことをしようとしていますが、辞書とNgForに問題があります。

TypeScriptのインターフェイスは次のようになっています。

interface Dictionary {
    [ index: string ]: string
}

JavaScriptでは、これは、データを含む次のようなオブジェクトに変換されます。

myDict={'key1':'value1','key2':'value2'}

私はこれを繰り返して、これを試しました:

<div *ngFor="(#key, #value) of myDict">{{key}}:{{value}}</div>

しかし、無駄に、以下のどれもうまくいきませんでした:

<div *ngFor="#value of myDict">{{value}}</div>
<div *ngFor="#value of myDict #key=index">{{key}}:{{value}}</div>

すべてのケースで、「予期しないトークン」または「「iterableDiff」パイプサポートオブジェクトが見つかりません」などのエラーが発生します。

ここで何が欠けていますか?これはもう不可能ですか?(最初の構文はAngular 1.xで機能します)またはオブジェクトを反復するための構文は異なりますか?


「辞書」とは何ですか?私はその用語をJavaScript、Angular、またはTypeScriptのコンテキストで見たり聞いたりしたことがありません。Y

辞書は私が思うマップを意味します。この用語はJSコンテキストではまったく使用されていませんが、PythonまたはRubyでは使用されています。
Cesar Jrロドリゲス

2
@berslingの答えが、この質問に対する正しい答えになったと思います。
ジョシュアキッソン

1
正解を上手にマークしてください。berslingは正解です
-activedecay

回答:


86

ng1の構文をサポートしたくないようです。

MiškoHevery(参考文献)によると:

マップはキーに順序がないため、反復は予測できません。これはng1でサポートされていましたが、これは誤りであり、NG2ではサポートされないと考えられます

計画はmapToIterableパイプを持つことです

<div *ngFor"var item of map | mapToIterable">

したがって、オブジェクトを反復処理するには、「パイプ」を使用する必要があります。現在パイプはありません、それを行う実装されていませ。

回避策として、キーを反復処理する小さな例を次に示します。

成分:

import {Component} from 'angular2/core';

@Component({
  selector: 'component',
  templateUrl: `
       <ul>
       <li *ngFor="#key of keys();">{{key}}:{{myDict[key]}}</li>
       </ul>
  `
})
export class Home {
  myDict : Dictionary;
  constructor() {
    this.myDict = {'key1':'value1','key2':'value2'};
  }

  keys() : Array<string> {
    return Object.keys(this.myDict);
  }
}

interface Dictionary {
    [ index: string ]: string
}

1
私はkeyas numberおよびvalueas を使用してオブジェクトで同じことを試していますstringが、angularがエラーをスローしていますexpression has changed after it was checkedか?なぜそうなのか?
Pardeep Jain

ええ、これも私のために起こっています。@obsurのソリューションを使用する場合も同様です。
user2294382 2016

1
最新の角度7に自明な解決策があるため、バーズリングの回答をご覧ください
アクティブディケイ

156

Angular 6.1.0以降の回答

次のように組み込みのkeyvalueパイプを使用します。

<div *ngFor="let item of myObject | keyvalue">
    Key: <b>{{item.key}}</b> and Value: <b>{{item.value}}</b>
</div>

またはこのように:

<div *ngFor="let item of myObject | keyvalue:mySortingFunction">
    Key: <b>{{item.key}}</b> and Value: <b>{{item.value}}</b>
</div>

ファイルのどこにmySortingFunctionあるか、.ts例:

mySortingFunction = (a, b) => {
  return a.key > b.key ? -1 : 1;
}

Stackblitz:https ://stackblitz.com/edit/angular-iterate-key-value

Angularパイプはどのテンプレートでもそのまま使用できるため、これをモジュールに登録する必要はありません。

また、Javascript-Mapsでも機能します。


implements PipeTransformクラス定義に追加する必要があります(angular.io/guide/pipes#custom-pipesを参照)
toioski

1
@toioskiありがとう、それを追加し、forループの新しい構文に更新しました。
bersling

すばらしい答えです。これをngFor my Dictionaryに使用しました。私はkeyValuePair.val [0]を実行する必要がありましたが、値が{}ではなく[{}]になったためです
jhhoff02

1
これに比べて利点はありreturn Object.keys(dict).map(key => ({key, val: dict[key]}))ますか?
ジャスティンモーガン

何も見えません、実際に私はあなたのやり方を使用します!
ベルリング、

72

このパイプを使用してみてください

import { Pipe, PipeTransform } from '@angular/core';

@Pipe({ name: 'values',  pure: false })
export class ValuesPipe implements PipeTransform {
  transform(value: any, args: any[] = null): any {
    return Object.keys(value).map(key => value[key]);
  }
}

<div *ngFor="#value of object | values"> </div>

5
素晴らしいです。キーへの参照を保持したい場合は、代わりにキーと値の両方でオブジェクトをマッピングします。これが私の問題の解決策であり、マークされた回答が私の質問への回答であるため、いくつかの回答を承認済みの回答としてマークできるといいのですが。
Rickard Staaf

1
@obscur-上記を今実行すると、angular2.beta.0.0を使用して「チェック後に式が変更されました」というエラーが表示されます。何かご意見は?
user2294382 2016

それは、pure:falseの場合、変更の検出
戦略を

1
なぜそれを不純に設定するのですか?
tom10271 2016

これは私にはうまくいきました。唯一のことは、ngForで#を使用できないことです。代わりにletを使用しました。
MartinJH 2018

19

@obscurの回答に加えて、@ View keyとの両方にアクセスする方法の例を次に示しますvalue

パイプ:

@Pipe({
   name: 'keyValueFilter'
})

export class keyValueFilterPipe {
    transform(value: any, args: any[] = null): any {

        return Object.keys(value).map(function(key) {
            let pair = {};
            let k = 'key';
            let v = 'value'


            pair[k] = key;
            pair[v] = value[key];

            return pair;
        });
    }

}

見る:

<li *ngFor="let u of myObject | 
keyValueFilter">First Name: {{u.key}} <br> Last Name: {{u.value}}</li>

したがって、オブジェクトが次のようになる場合:

myObject = {
    Daario: Naharis,
    Victarion: Greyjoy,
    Quentyn: Ball
}

生成される結果は次のとおりです。

名:Daario
姓:Naharis

名:ビクタリオン
姓:Greyjoy

名:Quentyn
姓:ボール


2
ビューを変更する必要があることを1つだけ述べます:as <li *ngFor="let u of myObject | keyValueFilter">First Name: {{u.key}} <br> Last Name: {{u.value}}</li>。私からの+1。
sib10

:あなたのmap関数内のコードを簡略化することができ return { 'key' : key, 'value' : value[key] };
Makotosan

17

更新:Angularは、jsonオブジェクトを介してロッピングするためのパイプを提供していkeyvalueます。

<div *ngFor="let item of myDict | keyvalue">
  {{item.key}}:{{item.value}}
</div>

WORKING DEMO、および詳細について読みます


以前(古いバージョンの場合):今までのところ、私が見つけた最良/最短の答えは(コンポーネント側からのパイプフィルターまたはカスタム関数なし)

コンポーネント側:

objectKeys = Object.keys;

テンプレート側:

<div *ngFor='let key of objectKeys(jsonObj)'>
   Key: {{key}}

    <div *ngFor='let obj of jsonObj[key]'>
        {{ obj.title }}
        {{ obj.desc }}
    </div>

</div>

ワーキングデモ


1
let item of myDict | keyvalueこれは私の問題を解決しました。
Silambarasan RD

13

SimonHawesomeの優れた答えに追加します。新しいtypescript機能のいくつかを利用する簡潔なバージョンを作成しました。SimonHawesomeのバージョンは、根本的な詳細を説明するために意図的に冗長であることを理解しています。また、パイプが偽の値に対して機能するように、早期チェックを追加しました。たとえば、マップがnull

(ここで行われるように)イテレータ変換を使用すると、(他の回答の一部で行われるように)一時配列にメモリを割り当てる必要がないため、より効率的になることに注意してください。

import {Pipe, PipeTransform} from '@angular/core';

@Pipe({
    name: 'mapToIterable'
})
export class MapToIterable implements PipeTransform {
    transform(map: { [key: string]: any }, ...parameters: any[]) {
        if (!map)
            return undefined;
        return Object.keys(map)
            .map((key) => ({ 'key': key, 'value': map[key] }));
    }
}

3
このスレッドが大好きで、1つのコメントが別のコメントの上に構築されています!私はあなたのコードを見たときに同じことを書こうとしていました
David

3
このソリューションで唯一のもの:実装する必要がありますPipeTransform
iRaS

@iRaS良い点。回答を更新しました。nullの代わりにundefinedも返します。
Frederik Aalund 2017年

9

複数の変換(keyval、key、value)をサポートする上記の回答のバリエーションを次に示します。

import { Pipe, PipeTransform } from '@angular/core';

type Args = 'keyval'|'key'|'value';

@Pipe({
  name: 'mapToIterable',
  pure: false
})
export class MapToIterablePipe implements PipeTransform {
  transform(obj: {}, arg: Args = 'keyval') {
    return arg === 'keyval' ?
        Object.keys(obj).map(key => ({key: key, value: obj[key]})) :
      arg === 'key' ?
        Object.keys(obj) :
      arg === 'value' ?
        Object.keys(obj).map(key => obj[key]) :
      null;
  }
}

使用法

map = {
    'a': 'aee',
    'b': 'bee',
    'c': 'see'
}

<div *ngFor="let o of map | mapToIterable">{{o.key}}: {{o.value}}</div>
  <div>a: aee</div>
  <div>b: bee</div>
  <div>c: see</div>

<div *ngFor="let o of map | mapToIterable:'keyval'">{{o.key}}: {{o.value}}</div>
  <div>a: aee</div>
  <div>b: bee</div>
  <div>c: see</div>

<div *ngFor="let k of map | mapToIterable:'key'">{{k}}</div>
  <div>a</div>
  <div>b</div>
  <div>c</div>

<div *ngFor="let v of map | mapToIterable:'value'">{{v}}</div>
  <div>aee</div>
  <div>bee</div>
  <div>see</div>

1
pure: falseインスタント反射には本当に重要です。
FıratKÜÇÜK

4

同様の問題があり、オブジェクトとマップ用に何かを構築しました。

import { Pipe } from 'angular2/core.js';

/**
 * Map to Iteratble Pipe
 * 
 * It accepts Objects and [Maps](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Map)
 * 
 * Example:
 * 
 *  <div *ngFor="#keyValuePair of someObject | mapToIterable">
 *    key {{keyValuePair.key}} and value {{keyValuePair.value}}
 *  </div>
 * 
 */
@Pipe({ name: 'mapToIterable' })
export class MapToIterable {
  transform(value) {
    let result = [];
    
    if(value.entries) {
      for (var [key, value] of value.entries()) {
        result.push({ key, value });
      }
    } else {
      for(let key in value) {
        result.push({ key, value: value[key] });
      }
    }

    return result;
  }
}


1
これはimplements PipeTransform
正常に

3

Angular 2.x && Angular 4.xはそのままではこれをサポートしていません

この2つのパイプを使用して、キーまたは値のいずれかで反復できます

キーパイプ:

import {Pipe, PipeTransform} from '@angular/core'

@Pipe({
  name: 'keys',
  pure: false
})
export class KeysPipe implements PipeTransform {
  transform(value: any, args: any[] = null): any {
    return Object.keys(value)
  }
}

値パイプ:

import {Pipe, PipeTransform} from '@angular/core'

@Pipe({
  name: 'values',
  pure: false
})
export class ValuesPipe implements PipeTransform {
  transform(value: any, args: any[] = null): any {
    return Object.keys(value).map(key => value[key])
  }
}

使い方:

let data = {key1: 'value1', key2: 'value2'}

<div *ngFor="let key of data | keys"></div>
<div *ngFor="let value of data | values"></div>

2

多次元オブジェクトを操作する方法を誰かが疑問に思っている場合は、ここに解決策があります。

次のオブジェクトが稼働していると仮定します

getChallenges() {
    var objects = {};
    objects['0'] = { 
        title: 'Angular2', 
        description : "Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur."
    };

    objects['1'] = { 
        title: 'AngularJS', 
        description : "Lorem Ipsum is simply dummy text of the printing and typesetting industry."
    };

    objects['2'] = { 
        title: 'Bootstrap',
        description : "Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat.",
    };
    return objects;
}

コンポーネントに次の関数を追加します

challenges;

constructor(testService : TestService){
    this.challenges = testService.getChallenges();
}
keys() : Array<string> {
    return Object.keys(this.challenges);
}

最後にビューで以下を行います

<div *ngFor="#key of keys();">
    <h4 class="heading">{{challenges[key].title}}</h4>
    <p class="description">{{challenges[key].description}}</p>
</div>

2

私は、JSONクエリ/ API呼び出しから返されたデータを解析して使用しようとすることで頭を悩ませてきました。私はどこが間違っていたのか正確にはわかりません、私は何日も答えを丸めていて、次のようなさまざまなエラーコードを追跡しているように感じます:

「 'iterableDiff'パイプサポートオブジェクトが見つかりません」

「ジェネリックTYpe配列には1つの引数が必要です」

JSON解析エラー、およびその他

私は修正の組み合わせが間違っていたと思います。

だからここに落とし穴と探すべきことの要約があります。

まず、API呼び出しの結果を確認します。結果は、オブジェクト、配列、またはオブジェクトの配列の形式になります。

あまり深くは触れませんが、OPの元の反復可能ではないというエラーは、通常、配列ではなくオブジェクトを反復しようとしたことが原因であると言えます。

配列とオブジェクトの両方の変数を示すデバッグ結果の一部を次に示します

そのため、通常はJSON結果を反復処理したいので、それが配列の形式であることを確認する必要があります。私は多数の例を試しましたが、おそらく私が知っていることの一部は実際に機能することを知っていますが、私が行ったアプローチは実際にパイプを実装することであり、使用したコードはt.888によって投稿されたものでした

   transform(obj: {[key: string]: any}, arg: string) {
if (!obj)
        return undefined;

return arg === 'keyval' ?
    Object.keys(obj).map((key) => ({ 'key': key, 'value': obj[key] })) :
  arg === 'key' ?
    Object.keys(obj) :
  arg === 'value' ?
    Object.keys(obj).map(key => obj[key]) :
  null;

正直に言って、私が得た原因の1つはエラー処理の欠如だったと思います。'returnundefined '呼び出しを追加することで、予期しないデータをパイプに送信できるようになりました。これは明らかに私の場合に発生していました。 。

パイプへの引数を処理したくない場合(そして、ほとんどの場合それが必要だとは思わない場合)、次のコードを返すだけです。

       if (!obj)
          return undefined;
       return Object.keys(obj);

パイプとそのパイプを使用するページまたはコンポーネントの作成に関する注意事項

「name_of_my_pipe」が見つからないというエラーを受け取っていましたか

CLIから 'ionic generate pipe'コマンドを使用して、pipe modules.tsが正しく作成および参照されるようにします。以下をmypage.module.tsページに追加してください。

import { PipesModule } from ‘…/…/pipes/pipes.module’;

(独自のcustom_moduleもある場合、これが変更されるかどうかは不明です。custommodule.module.tsに追加する必要がある場合もあります)

'ionic generate page'コマンドを使用してページを作成したが、そのページをメインページとして使用することにした場合は、app.module.tsからページ参照を削除することを忘れないでください(https:/ /forum.ionicframework.com/t/solved-pipe-not-found-in-custom-component/95179/13?u=dreaser

htmlファイルにデータを表示するいくつかの方法がある答えを検索するときに、違いを説明するのに十分な理解がありません。特定のシナリオでは、別のものを使用する方がよい場合があります。

            <ion-item *ngFor="let myPost of posts">
                  <img src="https://somwhereOnTheInternet/{{myPost.ImageUrl}}"/>
                  <img src="https://somwhereOnTheInternet/{{posts[myPost].ImageUrl}}"/>
                  <img [src]="'https://somwhereOnTheInternet/' + myPost.ImageUrl" />
            </ion-item>

ただし、値とキーの両方を表示できるように機能したのは次のとおりです。

    <ion-list>  
      <ion-item *ngFor="let myPost of posts  | name_of_pip:'optional_Str_Varible'">

        <h2>Key Value = {{posts[myPost]}} 

        <h2>Key Name = {{myPost}} </h2>

      </ion-item>
   </ion-list>  

API呼び出しを行うには、HttpModuleをapp.module.tsにインポートする必要があるように見えます

import { HttpModule } from '@angular/http';
 .
 .  
 imports: [
BrowserModule,
HttpModule,

呼び出し元のページにHttpが必要です

import {Http} from '@angular/http';

API呼び出しを行うと、子データ(配列内のオブジェクトまたは配列)に到達できるようです。

通話中のどちらか

this.http.get('https://SomeWebsiteWithAPI').map(res => res.json().anyChildren.OrSubChildren).subscribe(
        myData => {

または、ローカル変数にデータを割り当てるとき

posts: Array<String>;    
this.posts = myData['anyChildren'];

(その変数が配列文字列である必要があるかどうかはわかりませんが、それが現在私が持っているものです。より一般的な変数として機能する可能性があります)

そして最後の注意として、組み込みのJSONライブラリを使用する必要はありませんでしたが、これらの2つの呼び出しはオブジェクトから文字列への変換やその逆に便利です。

        var stringifiedData = JSON.stringify(this.movies);                  
        console.log("**mResults in Stringify");
        console.log(stringifiedData);

        var mResults = JSON.parse(<string>stringifiedData);
        console.log("**mResults in a JSON");
        console.log(mResults);

この情報の集まりが誰かを助けてくれることを願っています。


2
//Get solution for ng-repeat    
//Add variable and assign with Object.key

    export class TestComponent implements OnInit{
      objectKeys = Object.keys;
      obj: object = {
        "test": "value"
        "test1": "value1"
        }
    }
    //HTML
      <div *ngFor="let key of objectKeys(obj)">
        <div>
          <div class="content">{{key}}</div>
          <div class="content">{{obj[key]}}</div>
        </div>

1

JavaScriptでは、これは、データを含む次のようなオブジェクトに変換されます

TypeScriptのインターフェースは、開発時の構成要素です(完全にツール用... 0ランタイムインパクト)。JavaScriptと同じTypeScriptを記述する必要があります。


それは真実ではありません、申し訳ありません。タイプスクリプトは、よりクリーンなコードを書くことを強制します。クラスを抽象化する方がはるかに簡単です。あなたは単に持っていない。C ++は一部のasmにコンパイルされます
-asmに

1

辞書は配列ではなくオブジェクトです。Angular 2では、ng-repeatには配列が必要だと思います。

最も簡単な解決策は、オブジェクトをその場で配列に変換するパイプ/フィルターを作成することです。とはいえ、@ basaratが言うように、おそらく配列を使用する必要があります。


1

あなたが持っている場合es6-shimや、あなたtsconfig.jsonの目標es6は、ES6使用できる地図を、それを作るために。

var myDict = new Map();
myDict.set('key1','value1');
myDict.set('key2','value2');

<div *ngFor="let keyVal of myDict.entries()">
    key:{{keyVal[0]}}, val:{{keyVal[1]}}
</div>

0

PipeTransformを定義しMapValuesPipeて実装します

import {Pipe, PipeTransform} from '@angular/core';

@Pipe({name: 'mapValuesPipe'})
export class MapValuesPipe implements PipeTransform {
    transform(value: any, args?: any[]): Object[] {
        let mArray: 
        value.forEach((key, val) => {
            mArray.push({
                mKey: key,
                mValue: val
            });
        });

        return mArray;
    }
}

pipesモジュールにパイプを追加します。これは、複数のコンポーネントで同じパイプを使用する必要がある場合に重要です。

@NgModule({
  imports: [
    CommonModule
  ],
  exports: [
    ...
    MapValuesPipe
  ],
  declarations: [..., MapValuesPipe, ...]
})
export class PipesAggrModule {}

次に、単にあなたのhtmlでパイプを使用します*ngFor

<tr *ngFor="let attribute of mMap | mapValuesPipe">

パイプを使用するコンポーネントでPipesModuleを宣言する必要があることに注意してください。

@NgModule({
  imports: [
    CommonModule,
    PipesAggrModule
  ],
...
}
export class MyModule {}

0

そのため、Object(obj).keys.lengthのみを返す独自のヘルパー関数objLength(obj)を実装しました。しかし、それをテンプレートの* ngIf関数に追加すると、IDEはobjectKeys()を提案しました。試してみましたが、うまくいきました。宣言に従って、lib.es5.d.tsによって提供されているようです。これで完了です。

これが私が実装した方法です(私がアップロードしたファイルのインデックスとしてサーバー側で生成されたキーを使用するカスタムオブジェクトがあります)。

        <div *ngIf="fileList !== undefined && objectKeys(fileList).length > 0">
          <h6>Attached Files</h6>
          <table cellpadding="0" cellspacing="0">
            <tr *ngFor="let file of fileList | keyvalue">
              <td><a href="#">{{file.value['fileName']}}</a></td>
              <td class="actions">
                <a title="Delete File" (click)="deleteAFile(file.key);">
                </a>
              </td>
            </tr>
          </table>
        </div>
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.