Подтвердить что ты не робот

Итерация над объектом в Angular

Я пытаюсь сделать что-то в 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) или является синтаксисом, отличающимся для итерации над объектом?

4b9b3361

Ответ 1

Похоже, что они не хотят поддерживать синтаксис от ng1.

Согласно Miško Hevery (ссылка):

Карты не имеют заказов в ключах и, следовательно, итерация непредсказуема. Это было поддержано в ng1, но мы считаем, что это была ошибка и не будет поддерживаться в NG2

План состоит в том, чтобы иметь mapToIterable pipe

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

Итак, чтобы перебрать ваш объект, вам нужно будет использовать "трубу". В настоящее время не реализовано pipe, которое делает это.

Как обходной путь, вот небольшой пример, который выполняет итерации по клавишам:

компонент:

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
}

Ответ 2

Угловой 6.1. 0+ Ответ

Используйте встроенное значение keyvalue -pipe следующим образом:

<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

Вам не нужно регистрировать это в любом модуле, так как угловые трубы работают из коробки в любом шаблоне.

Это также работает для Javascript-Maps.


Преангулярный 6 Ответ

Как уже упоминалось в других ответах, он не поддерживается в ngx, поэтому здесь есть обходной путь с парами ключ-значение:

Труба:

import { Pipe, PipeTransform } from '@angular/core';
@Pipe({
  name: 'mapToIterable'
})
export class MapToIterable implements PipeTransform {
  transform(dict: Object) {
    var a = [];
    for (var key in dict) {
      if (dict.hasOwnProperty(key)) {
        a.push({key: key, val: dict[key]});
      }
    }
    return a;
  }
}

Использование:

<div *ngFor="let keyValuePair of someObject | mapToIterable">
  This is the key {{keyValuePair.key}} and this is the value {{keyValuePair.val}}.
</div>

Пример Stackblitz: https://stackblitz.com/edit/map-to-iterable-pipe

Ответ 3

попробуйте использовать эту трубу

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>

Ответ 4

В дополнение к ответу @obscur, вот пример того, как вы можете получить доступ к key и value из @View.

Труба:

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

Сгенерированный результат будет:

Имя: Даарио
Фамилия: Нахарис

Имя: Виктарион
Фамилия: Грейджой

Имя: Квентин
Фамилия: Бал

Ответ 5

Добавление в SimonHawesome отличный ответ. Я сделал краткую версию, которая использует некоторые из новых функций typescript. Я понимаю, что версия SimonHawesome намеренно многословна, чтобы объяснить основные детали. Я также добавил раннюю проверку, чтобы труба работала для значений falsy. Например, если карта 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] }));
    }
}

Ответ 6

Здесь приведена вариация на некоторые из вышеперечисленных ответов, которые поддерживают несколько преобразований (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>

Ответ 7

Обновлено: Angular теперь предоставляет канал для прохождения через объект json через значение keyvalue :

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

РАБОЧАЯ ДЕМО, и для более подробной информации читайте


Ранее (для более старой версии): до сих пор лучший/самый короткий ответ, который я нашел, - это (без какого-либо фильтра труб или пользовательской функции со стороны компонентов)

Компонентная сторона:

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>

РАБОЧАЯ ДЕМО

Ответ 8

У меня была аналогичная проблема, построенная что-то для объектов и Maps.

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

Ответ 9

Angular 2.x && Angular 4.x не поддерживает это из коробки

Вы можете использовать эти два канала для итерации клавишей или значением.

Ключ:

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>

Ответ 10

Если кому-то интересно, как работать с многомерным объектом, вот решение.

позволяет предположить, что у нас есть следующий объект в службе

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>

Ответ 11

Я рвал на себе волосы, пытаясь разобрать и использовать данные, возвращаемые из запроса JSON/API-интерфейса. Я не уверен точно, где я ошибался, я чувствую, что обводил ответ в течение нескольких дней, преследуя различные коды ошибок, такие как:

"Не удалось найти объект поддержки трубы" iterableDiff ""

"Универсальный массив типов требует один аргумент (ы)"

Ошибки синтаксического анализа 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;

Честно говоря, я думаю, что одной из вещей, которые меня привлекли, было отсутствие обработки ошибок, добавив вызов return undefined, который, я полагаю, теперь мы разрешаем отправлять непредвиденные данные в канал, что, очевидно, происходило в моем случае.,

если вы не хотите иметь дело с аргументом в трубу (и посмотрите, я не думаю, что это необходимо в большинстве случаев), вы можете просто вернуть следующее

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

Некоторые заметки о создании канала и страницы или компонента, использующего этот канал

я получаю ошибки о том, что name_of_my_pipe не найден

Используйте команду 'ionic generate pipe из CLI, чтобы убедиться, что 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, вы, кажется, можете получить доступ к дочерним данным (объектам или массивам в массиве) 2 разными способами, либо, похоже, работает

либо во время разговора

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

или когда вы назначаете данные для вашей локальной переменной

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

(не уверен, что эта переменная должна быть строкой массива, но это то, что у меня есть сейчас. Может работать как более общая переменная)

И последнее замечание: не было необходимости использовать встроенную библиотеку JSON, однако вы можете найти эти два вызова удобными для преобразования объекта в строку и наоборот.

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

Я надеюсь, что эта подборка информации поможет кому-то.

Ответ 12

В JavaScript это переведет объект, который с данными может выглядеть следующим образом:

Интерфейсы в TypeScript представляют собой конструкцию времени dev (чисто для инструментальной обработки... 0). Вы должны написать тот же TypeScript, что и ваш JavaScript.

Ответ 13

Словарь - это объект, а не массив. Я считаю, что ng-repeat требует массив в Angular 2.

Простейшим решением было бы создать канал/фильтр, который преобразует объект в массив "на лету". Тем не менее, вы, вероятно, захотите использовать массив, как говорит @basarat.

Ответ 14

Если у вас есть es6-shim или ваш tsconfig.json target es6, вы можете использовать ES6 Map, чтобы сделать это.

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>

Ответ 15

Определите MapValuesPipe и реализуйте PipeTransform:

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

Добавьте свою трубу в свой модуль труб. Это важно, если вам нужно использовать одну и ту же трубу в нескольких компонентах:

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

Ответ 16

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

Ответ 17

Поэтому я собирался реализовать свою собственную вспомогательную функцию, objLength (obj), которая возвращает просто Object (obj).keys.length. Но затем, когда я добавлял его в функцию шаблона * 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>