Angular'da nesne üzerinde yineleme


130

Angular 2 Alpha 28'de bazı şeyler yapmaya çalışıyorum ve sözlükler ve NgFor ile ilgili bir sorun yaşıyorum.

TypeScript'te şöyle görünen bir arayüzüm var:

interface Dictionary {
    [ index: string ]: string
}

JavaScript'te bu, verilerle aşağıdaki gibi görünebilecek bir nesneye çevrilir:

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

Bunu tekrarlamak istiyorum ve bunu denedim:

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

Ancak boşuna, aşağıdakilerden hiçbiri de işe yaramadı:

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

Her durumda "Beklenmeyen belirteç" veya "'iterableDiff' boru destekleyen nesne bulunamıyor" gibi hatalar alıyorum

Burada neyi özlüyorum? Bu artık mümkün değil mi? (İlk sözdizimi Angular 1.x'te çalışır) veya sözdizimi bir nesne üzerinde yineleme için farklı mıdır?


"Sözlük" nedir? Bu terimi bir JavaScript, Angular veya TypeScript bağlamında hiç görmedim veya duymadım. Y

Sözlük, sanırım bir harita anlamına geliyor, terim JS bağlamında hiç kullanılmıyor, Python veya Ruby'de kullanılıyor.
Cesar Jr Rodriguez

2
Sanırım @ bersling cevabı artık bu sorunun doğru cevabı.
Joshua Kissoon

1
Lütfen doğru cevabı daha iyi işaretleyiniz. bersling doğrudur
activedecay

Yanıtlar:


87

Görünüşe göre ng1'den sözdizimini desteklemek istemiyorlar.

Miško Hevery'ye göre ( referans ):

Haritalar anahtarlarda sipariş içermez ve bu nedenle yinelemeleri tahmin edilemez. Bu, ng1'de desteklendi, ancak bunun bir hata olduğunu ve NG2'de desteklenmeyeceğini düşünüyoruz.

Plan, bir mapToIterable boruya sahip olmaktır.

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

Dolayısıyla, nesnenizin üzerinde yineleme yapmak için bir "boru" kullanmanız gerekecektir. Şu anda bunu yapan uygulanmış bir boru yoktur .

Geçici bir çözüm olarak, anahtarlar üzerinde yinelenen küçük bir örnek aşağıda verilmiştir:

Bileşen:

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
ile i nesne üzerinde aynı çalışıyorum keyolaraknumber ve valueolarakstring ama açısal hata fırlatma expression has changed after it was checkedmı? neden bu kadar herhangi bir fikir?
Pardeep Jain

Evet bu benim için de oluyor. @ Obsur'un çözümünü de kullanırsam aynı.
user2294382

1
Son açısal 7 önemsiz bir çözüm yoktur çünkü en bersling cevaba bakınız
activedecay

156

Açısal 6.1.0+ Cevap

Kullanım dahili keyvalue-boru böyle:

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

veya bunun gibi:

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

dosyanızın neresi mySortingFunction, .tsörneğin:

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

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

Açısal borular herhangi bir şablonda kutudan çıktığı için bunu herhangi bir modüle kaydetmeniz gerekmez.

Javascript-Maps için de çalışıyor .


Sen eklemek gerekir implements PipeTransformsınıf tanımında (bkz angular.io/guide/pipes#custom-pipes )
toioski

1
@toioski teşekkürler, onu ekledim ve for döngüsünün yeni sözdizimine güncelledim.
2018

Harika cevap, bunu sözlüğüm için kullandı. KeyValuePair.val [0] yapmak zorunda kaldım, ancak değerlerim sona erdiğinde [{}] değil {}
jhhoff02

1
Bunun üzerinde bir avantajı var mı return Object.keys(dict).map(key => ({key, val: dict[key]}))?
Justin Morgan

Hiç görmüyorum, aslında senin yolunu kullanırdım!
2018

72

bu boruyu kullanmayı dene

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
Harika, ve anahtara referansı korumak istersem, bunun yerine hem anahtar hem de değer ile bir nesneyi eşlerim. Keşke birkaç cevabı kabul edilmiş cevap olarak işaretleyebilseydim, çünkü bu benim sorunumun çözümü iken, işaretlenen cevap sorumun cevabıdır.
Rickard Staaf

1
@obscur - Yukarıdakileri şimdi yaparsam, angular2.beta.0.0'ı kullanarak "ifade kontrol edildikten sonra değişti" hatası alıyorum. Düşüncesi olan var mı?
user2294382

Bunun nedeni saf: yanlış, bir değişiklik tespiti gerektirir
Stratejinin

1
Neden onu saf olmayacak?
tom10271

Bu benim için iyi çalıştı. NgFor'da # kullanamadığım tek şeydi. Bunun yerine let kullanılır.
MartinJH

19

@ Obscur'un cevabına ek olarak, burada hem keyve hem devalue @View dan.

Boru:

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

}

Görünüm:

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

Yani nesne şöyle görünecekse:

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

Oluşturulan sonuç şöyle olacaktır:

Adı: Daario
Soyad: Naharis

Adı: Victarion
Soyadı: Greyjoy

Adı: Quentyn
Soyadı: Ball


2
Görünümü değiştirmeniz gereken tek şey: as <li *ngFor="let u of myObject | keyValueFilter">First Name: {{u.key}} <br> Last Name: {{u.value}}</li>. Benden +1.
sib10

Harita işlevinizin içindeki kod şu şekilde basitleştirilebilir: return { 'key' : key, 'value' : value[key] };
Makotosan

17

Güncellendi: Angular artık json Nesnesinin içinden geçmek için boruyu şu yolla sağlıyor keyvalue:

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

ÇALIŞMA DEMOSU ve daha fazla ayrıntı için Oku


Daha önce (Eski Sürüm için): Şimdiye kadar bulduğum en iyi / en kısa cevap (Bileşen Tarafında Boru Filtresi veya Özel işlev olmadan)

Bileşen tarafı:

objectKeys = Object.keys;

Şablon tarafı:

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

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

</div>

ÇALIŞMA DEMOSU


1
let item of myDict | keyvaluebu benim sorunumu çözdü.
Silambarasan RD

13

SimonHawesome'ın mükemmel cevabına ek olarak . Yeni daktilo özelliklerinden bazılarını kullanan kısa ve öz bir sürüm hazırladım. SimonHawesome'nın versiyonunun, altta yatan detayları açıklamak için kasıtlı olarak ayrıntılı olduğunu anlıyorum. Ayrıca borunun yanlış değerler için çalışması için erken bir kontrol ekledim . Örneğin, haritanull .

Bir yineleyici dönüşümü kullanmanın (burada yapıldığı gibi) daha verimli olabileceğini unutmayın, çünkü geçici bir dizi için bellek ayırmaya ihtiyacımız yoktur (diğer yanıtların bazılarında olduğu gibi).

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
Bir yorum diğerinin üzerine inşa ederek bu konuyu sevin! Kodunuzu gördüğümde aynı şeyi yazmak üzereydim
David

3
bu çözümdeki tek şey: uygulaması gerekirPipeTransform
iRaS

@iRaS İyi nokta. Cevabımı güncelledim. Null yerine undefined döndürüyorum.
Frederik Aalund

9

Birden çok dönüşümü (anahtar değer, anahtar, değer) destekleyen yukarıdaki yanıtlardan bazılarının bir varyasyonu:

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

kullanım

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: falseanlık yansımalar için gerçekten önemlidir.
Fırat KÜÇÜK

4

Nesneler ve Haritalar için benzer bir sorunla karşılaştım.

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
Bu iyi çalışıyor, ancak TypeScript'te implements PipeTransformsınıf tanımına eklemeniz gerekiyor
GeorgDangl

3

Angular 2.x && Angular 4.x bunu kutunun dışında desteklemiyor

Anahtar veya değere göre yinelemek için bu iki kanalı kullanabilirsiniz. .

Anahtarlar borusu:

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

Değerler borusu:

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])
  }
}

Nasıl kullanılır:

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

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

2

Birisi çok boyutlu nesne ile nasıl çalışılacağını merak ediyorsa işte çözüm.

hizmette aşağıdaki nesneye sahip olduğumuzu varsayalım

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

bileşende aşağıdaki işlevi ekleyin

challenges;

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

sonunda görünümde aşağıdakileri yapın

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

2

JSON sorgusu / api çağrısından döndürülen verileri ayrıştırmaya ve kullanmaya çalışırken saçlarımı yırtıyorum. Nerede yanlış yaptığımdan tam olarak emin değilim, günlerdir cevabın etrafında dolaşıp, çeşitli hata kodlarını takip ettiğimi hissediyorum:

"'İterableDiff' kanal destekleyen nesne bulunamıyor"

"Genel TYpe Dizisi bir bağımsız değişken gerektirir"

JSON ayrıştırma Hataları ve eminim diğerleri

Yanlış düzeltme kombinasyonuna sahip olduğumu varsayıyorum.

İşte size sorunların ve aranacak şeylerin bir özeti.

Öncelikle api çağrılarınızın sonucunu kontrol edin, sonuçlarınız bir nesne, bir dizi veya bir dizi nesne biçiminde olabilir.

Çok fazla girmeyeceğim, OP'nin orijinal Hatasının yinelenememe hatası genellikle bir Diziyi değil, bir nesneyi yinelemeye çalışmanızdan kaynaklanır.

Hem dizilerin hem de nesnelerin değişkenlerini gösteren bazı hata ayıklama sonuçlarım burada

Bu nedenle, genellikle JSON sonucumuzu yinelemek istediğimizden, bunun bir Dizi biçiminde olmasını sağlamamız gerekir. Çok sayıda örnek denedim ve belki şimdi bildiğimi bildiğimden bazılarının aslında işe yarayacağını biliyordum, ancak benim kullandığım yaklaşım gerçekten bir pipo uygulamaktı ve kullandığım kod t.888 tarafından gönderildi.

   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;

Dürüst olmak gerekirse, beni elde eden şeylerden biri hata işleme eksikliğiydi, 'tanımsız dönüş' çağrısını ekleyerek artık beklenmeyen verilerin boruya gönderilmesine izin verdiğimize inanıyorum, ki bu benim durumumda açıkça görülüyordu .

Eğer boruya yönelik bir argümanla uğraşmak istemiyorsanız (ve bakın, çoğu durumda bunun gerekli olduğunu düşünmüyorum), sadece aşağıdakini geri verebilirsiniz

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

Borunuzu ve bu boruyu kullanan sayfanızı veya bileşeninizi oluşturmayla ilgili bazı notlar

'name_of_my_pipe' bulunamadığıyla ilgili hatalar mı alıyorum

Boru modüllerinin doğru şekilde oluşturulduğundan ve referans verildiğinden emin olmak için CLI'den 'iyonik boru oluştur' komutunu kullanın. mypage.module.ts sayfasına aşağıdakileri eklediğinizden emin olun.

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

(kendi custom_module'ünüz varsa bunun değişip değişmediğinden emin değilsiniz, bunu custommodule.module.ts'e de eklemeniz gerekebilir)

Sayfanızı yapmak için 'iyonik sayfa oluştur' komutunu kullandıysanız, ancak bu sayfayı ana sayfanız olarak kullanmaya karar verdiyseniz, sayfa referansını app.module.ts'den kaldırmayı unutmayın (işte bu https: / /forum.ionicframework.com/t/solved-pipe-not-found-in-custom-component/95179/13?u=dreaser

Verileri html dosyasında görüntülemenin birkaç yolunun olduğu yanıtları ararken ve farklılıkları açıklayacak kadar anlamıyorum. Belirli senaryolarda birbirini kullanmak daha iyi olabilir.

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

Ancak, hem değeri hem de anahtarı görüntülememe izin veren şey şuydu:

    <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 çağrısını yapmak için HttpModule'u app.module.ts'ye aktarmanız gerekiyor gibi görünüyor

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

ve aramayı yaptığınız sayfada Http'ye ihtiyacınız var

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

API çağrısı yaparken, alt verilere (dizideki nesneler veya diziler) 2 farklı yoldan erişebiliyor gibi görünüyorsunuz;

ya görüşme sırasında

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

veya verileri yerel değişkeninize atadığınızda

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

(bu değişkenin bir Dizi Dizesi olması gerekip gerekmediğinden emin değilim, ancak şu anda ona sahip olduğum şey bu. Daha genel bir değişken olarak çalışabilir)

Ve son not, dahili JSON kitaplığını kullanmak gerekli değildi, ancak bu 2 çağrıyı bir nesneden bir dizeye veya tam tersine dönüştürmek için kullanışlı bulabilirsiniz.

        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);

Umarım bu bilgi derlemesi birine yardımcı olur.


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'te bu, verilerle şunun gibi görünebilecek bir nesneye çevrilir

TypeScript'teki arayüzler bir geliştirme zamanı yapısıdır (yalnızca takım oluşturma için ... 0 çalışma zamanı etkisi). JavaScript'inizle aynı TypeScript'i yazmalısınız.


bu doğru değil, sry. type script sizi daha temiz kod yazmaya zorlar. sınıfları soyutlamak çok daha kolay. sahip olmadığınız. C ++ bazı
asm'lere derler

1

Sözlük bir nesnedir, dizi değil. Ng-tekrarının Angular 2'de bir dizi gerektirdiğine inanıyorum.

En basit çözüm, nesneyi anında diziye dönüştüren bir boru / filtre oluşturmaktır. Bununla birlikte, @ basarat'ın dediği gibi muhtemelen bir dizi kullanmak istiyorsunuz.


1

Eğer varsa es6-shimveya tsconfig.jsonhedefiniz varsa es6, bunu yapmak için ES6 Haritasını kullanabilirsiniz .

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'u tanımlayın MapValuesPipeve uygulayın :

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

Borunuzu boru modülünüze ekleyin. Aynı boruyu birden fazla bileşende kullanmanız gerekiyorsa bu önemlidir :

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

Ardından html'nizdeki boruyu şu şekilde kullanın *ngFor:

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

Unutmayın, PipesModule'unuzu boruyu kullanmak istediğiniz bileşende belirtmeniz gerekecek:

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

0

Bu yüzden, sadece Object (obj) .keys.length döndüren kendi yardımcı fonksiyonum olan objLength (obj) 'yi uygulayacaktım. Ama sonra onu şablonuma * ngIf eklerken, IDE'm objectKeys () önerdi. Ben denedim ve işe yaradı. Açıklamasının ardından, lib.es5.d.ts tarafından sunuluyor gibi görünüyor, işte buyrun!

Bunu şu şekilde uyguladım (Yüklediğim dosyalar için dizin olarak sunucu tarafında oluşturulan anahtarları kullanan özel bir nesnem var):

        <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>
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.