Parsing JSON with *ngfor directive in HTML - javascript

I would like to be able to get and list the keys (not values) from the params array in this JSON Object, using the *ngfor directive in an ionic2 page.
{
"id":"x",
"name":"xxx",
"code":"xxx",
"description":"xxx",
"params":
[
{"param1": "params1"},
{"param2": "params2"},
{"param3": "params3"}
]
}
I have searched around this forum and found an implementation using pipes; but I have been unsuccessful so far. The most I've been able to obtain using the pipes are the array indexes, but not the keys.

import { PipeTransform , Pipe } from '#angular/core';
#Pipe( { name : 'keys' } )
export class KeysPipe implements PipeTransform {
transform ( value , args : string[] ) : any {
let keys = [];
for ( let key in value ) {
if ( value.hasOwnProperty( key ) ) {
keys.push( key );
}
}
return keys;
}
}
And use it like this :
<ul>
<li *ngFor="let item of obj.params ">
<span *ngFor="let key of item | keys ">{{key }}</span>
</li>
</ul>
Here is the plunker
Note that plunker is using # , you should use let instead , like above.

Related

Javascript if string matches array property reorder array with it at 0 position

Im using an Angular 5 pipe to process an array before output to ngFor.
My array contains all the active users and I want it to always have the user thats currently logged-in to be at position 0 of the array so that it displays first in the ngFor output.
I can sort alphabetically desc/asc but I cant seem to figure out a solid way to do this..
A pipe might be the wrong way to go about it maybe using filter in some way on the observable before it outputs to the main page is the right way to go but again Im not sure about how to implement that.
My observable is output here by:
getActiveUsers(): Observable<iUser[]> {
return this.db.collection<iUser>('users').valueChanges().catch(this.catchError);
}
subscribe happens here in my component
this.auth.getActiveUsers().subscribe( async usersAvailable => {
this.usersFetched = usersAvailable
}, error => console.log(error));
displays in the component html here (with pipe being fed current users id for comparisons within it)
<ion-row *ngFor="let i of rows">
<div class="o-userGrid__item" (click)="openItem(u)" *ngFor="let u of usersFetched | filterActive: { activeUser: activeAccount }" width-33>
<img [src]="u?.photoURL" />
<div class="o-userGrid__title">
{{ u?.displayName }}
</div>
</div>
</ion-row>
and my filterActive pipe is fed the current users id here:
filterActive: { activeUser: activeAccount }
What's left of my pipe is the following. As mentioned I've tried some sort methods like alphabetically organising etc but of course thats not what I need, so to reiterate: I need the pipe to have the user, whose id matches the active user id, fed into the pipe to be at the first output on the page or position [0] of the array
import { Pipe, PipeTransform } from '#angular/core';
#Pipe({
name: 'filterActive',
})
export class FilterActivePipe implements PipeTransform {
transform(array: Array<string>, args?: any): Array<string> {
// console.log("indexOf ", array.indexOf(args.activeUser) );
return array.sort( (a:any, b:any) => {
console.log("arrayoutput ", b);
if( b.uid == args.activeUser ) {
console.log("active user found ", b.uid + " " + args.activeUser);
}
return b;
});
}
}
Got it in the end with the following pipe:
import { Pipe, PipeTransform } from '#angular/core';
#Pipe({
name: 'filterActive',
})
export class FilterActivePipe implements PipeTransform {
transform(array: Array<string>, args?: any): Array<string> {
array.unshift(
array.splice(
array.findIndex( (item:any) => item.uid === args.activeUser), // move the active user to the first position in the array
1)[0]
)
return array;
}
}

Filter on multiple columns in a table using one pipe in Angular

Hi everyone.
I want to make a custom filter for my table which intakes more than one argument
to search multiple columns .. in my case right now only one argument can be passed .
thanks in advance
component.html
<tr *ngFor = "let builder of builderDetailsArray[0] | filter :'groupName': searchString; let i = index" >
<td style="text-align: center;"><mat-checkbox></mat-checkbox></td>
<td>{{builder.builderId}}</td>
<td>{{builder.viewDateAdded}}</td>
<td>{{builder.viewLastEdit}}</td>
<td>{{builder.groupName}}</td>
<td>{{builder.companyPersonName}}</td>
<td style="text-align: center;"><button mat-button color="primary">UPDATE</button></td>
</tr>
pipe.ts
#Pipe({
name: "filter",
pure:false
})
export class FilterPipe implements PipeTransform {
transform(items: any[], field: string, value: string): any[] {
if (!items) {
return [];
}
if (!field || !value) {
return items;
}
return items.filter(singleItem =>
singleItem[field].toLowerCase().includes(value.toLowerCase()) );
}
Created multiple arguments pipe in angular 4
The code lets you search through multiple columns in your table.
Passed 2 arguments in the transform function
value: Which involves all the data inside the table, all columns
searchString: What you want to search inside the columns (inside the table).
Hence, you can define which columns to be searched inside the transform function.
In this case, the columns to be searched are builderId, groupName and companyPersonName
Pipe file
import { Pipe, PipeTransform } from '#angular/core';
#Pipe({
name: "arrayFilter"
})
export class BuilderFilterPipe implements PipeTransform {
transform(value:any[],searchString:string ){
if(!searchString){
console.log('no search')
return value
}
return value.filter(it=>{
const builderId = it.builderId.toString().includes(searchString)
const groupName = it.groupName.toLowerCase().includes(searchString.toLowerCase())
const companyPersonName = it.companyPersonName.toLowerCase().includes(searchString.toLowerCase())
console.log( builderId + groupName + companyPersonName);
return (builderId + groupName + companyPersonName );
})
}
}
What does the transform function do?
builderId, groupName and companyPersonName are the three fields I searched
builderId converted to string because our searchString is in string format.
toLowerCase() is used to make search accurate irrespective of user search in lowercase or uppercase
Html:
<tr *ngFor = "let builder of newBuilderDetailsArray | arrayFilter:search" >
<td>{{builder.builderId}}</td>
<td>{{builder.groupName}}</td>
<td>{{builder.companyPersonName}}</td>
</tr>
Make sure your filter.ts file added to module.ts file
Below is the sample code where I have passed 10 and 2 as arguments to pipes similarly you can pass multiple arguments and get it through parameters in pipe component. increase the arguments in pipe component with respect to number of inputs. working demo
Template
<p>Super power boost: {{2 | exponentialStrength:10:2}}</p>
Pipe
import { Pipe, PipeTransform } from '#angular/core';
/*
* Raise the value exponentially
* Takes an exponent argument that defaults to 1.
* Usage:
* value | exponentialStrength:exponent
* Example:
* {{ 2 | exponentialStrength:10 }}
* formats to: 1024
*/
#Pipe({name: 'exponentialStrength'})
export class ExponentialStrengthPipe implements PipeTransform {
transform(value: number, exponent1: any,exponent2: any): number {
let exp = parseFloat(exponent2);
return Math.pow(value, isNaN(exp) ? 1 : exp);
}
}

Angular 2 ngFor Object array object not working

I have this data:
component.ts:
demo = [{
'ob1': [{'ob1_first':'value11'}, {'ob1_second':'value12'}],
'ob2': [{'ob2_first':'value21'}, {'ob2_second':'value22'}],
}]
I want show it in view ngFor, but I still get [Object object]
My html:
<div *ngFor="let key of demo">
{{demo[key]}}
</div>
My question is:
How to refer to Object array object in ngFor ?
Your json format is not correct. Still solution is as below,
<div *ngFor="let key of demo">
{{getValue(key) |json}} <br/>
</div>
getValue(key){
console.log(key)
return Object.values(key);
}
LIVE DEMO
demo is an array with 1 object.
*ngFor="let key of demo" -> iterates through the object which is only one in your case.
{
'ob1': [{'ob1_first':'value11'}, {'ob1_second':'value12'}],
'ob2': [{'ob2_first':'value21'}, {'ob2_second':'value22'}],
}
this gets you the first key ->
demo[key].ob1
which has
[{'ob1_first':'value11'}, {'ob1_second':'value12'}]
as value.
To access this.
You have to do
*ngFor="let innerkey of key.ob1"
then you can access value1 with innerkey.ob1_first
Objects are not iterable directly with *ngFor. To iterate objects, you can use a pipe like this one:
import { Pipe, PipeTransform } from '#angular/core';
#Pipe({
name: 'keys'
})
export class KeysPipe implements PipeTransform {
transform(value: any, args?: any): any {
let keys = [];
for (let key in value) {
keys.push(key);
}
return keys;
}
}
Then, you use it like this to iterate the object:
<div *ngFor="let item of demo | keys">
{{demo[item]}}
</div>
With the format given by you . you can print your demo object using following code
<div *ngFor="#itemArr of demo">
<span >{{itemArr |json}}</span>
</div>
But if you want to traverse your json data then you may have to correct it as
this.demo =
[
[{'ob1_first':'value11'}, {'ob1_second':'value12'}],
[{'ob2_first':'value21'}, {'ob2_second':'value22'}]
]
and then render it as
<div *ngFor="#itemArr of demo">
<div *ngFor="#item of itemArr">
<span >{{item |json}}</span>
</div>
<hr>
</div>
If still the answer dont solve your problem, Could you state your actual JSON object that you want to render.
<div *ngFor="let key of demo">
{{key}}
</div>
Everything in the demo is represented as key that's the reference to each individual object.
But it seems your object is a bit complicated for *ngFor to handle. So consider using Class for mapping.
Demo.ts
export class Demo {
ob1_first: string;
ob1_second: string;
ob2_first: string;
ob2_second: string;
}
component.ts
#Component({
selector: 'my-component',
......
})
export class Component {
demoArray: Demo[];
ngOnInit() {
// If this throw error you may need to replace with `demo: Demo = new Demo;
demo: Demo;
demo.ob1_first = "value11";
demo.ob1_second = "value12";
demo.ob2_first = "value21";
demo.obj2_secon = "value22";
demo2: Demo;
demo2.ob1_first = "value11";
demo2.ob1_second = "value12";
demo2.ob2_first = "value21";
demo2.obj2_secon = "value22";
this.demoArray.push(demo);
this.demoArray.push(demo2);
}
}
HTML
<div *ngFor="let key of demoArray">
<div>START</div>
<div>{{key.ob1_first}}</div>
<div>{{key.ob1_second}}</div>
<div>{{key.ob2_first}}</div>
<div>{{key.ob2_second}}</div>
<div>END</div>
</div>

Angular2 - Pass parameters to a dynamically generated component?

I have a simple demo app which I'm simulating manually insert / fetch data from DB and injecting new components - according to the num entered.
Plunker
So if I click the "manual " button twice :
And if I set "3" in the text and click "fetch from db" - I get the expected delay(simulate db) and then :
This all works as expected.
The "parent" component is :
//src/MainPage.ts
#Component({
selector: 'my-app',
template: `
<button (click)="putInMyHtml()">Insert component manually</button>
<p> # Items to fetch : <input type="text" style='width:40px' [(ngModel)]="dbNumItems" name="dbNumItems"/> <input type='button' value='fetch from db' (click)='fetchItems($event)'/></p>
<div #myDiv>
<template #target></template>
</div>
`
})
export class MainPage {
#ViewChild('target', { read: ViewContainerRef }) target: ViewContainerRef;
dbNumItems: string;
constructor(private cfr: ComponentFactoryResolver) {}
fetchItems(){
var p= new Promise((resolve, reject) => { //simulate db
setTimeout(()=>resolve(this.dbNumItems),2000)
});
p.then(v=>{
for (let i =0;i<v;i++)
{
this.putInMyHtml() ;// inject "v" times
}
})
}
putInMyHtml() {
// this.target.clear();
let compFactory = this.cfr.resolveComponentFactory(TestPage);
this.target.createComponent(compFactory);
}
}
This is the Injected component :
//src/TestPage.ts
#Component({
selector: 'test-component',
template: '<b>Content : Name={{user.Name}} Age={{user.Age}}</b><br/>',
})
export class TestPage {
#Input
User:Person;
}
So where is the problem ?
As you can see , in the injected component I have :
#Input
User:Person;
which means that I want the parent component to pass a Person object to each injection.
In other words :
Question
Looking at the "after db stage" , How can I pass a customized person to each injection ?
p.then(v=>{
for (let i =0;i<v;i++)
{
let p = new Person();
p.Name = "name"+i;
p.Age = i;
this.putInMyHtml() ; //how to I pass `p` ???
}
})
}
Expected output :
NB
I don't want to use ngFor because I don't need to hold an Array at the back end. this is an app which injects new articles periodically.and I will be glad to know if there's a better way of doing it.
You can do it with the instance property of component ref like this:
putInMyHtml(p) {
// this.target.clear();
let compFactory = this.cfr.resolveComponentFactory(TestPage);
let ref = this.target.createComponent(compFactory);
ref.instance.user = p;
}
-Fixed the #Input() binding, syntax was wrong.
-Added a safe-navigation operator (?) for the template to do the null checks for the async input.
Fixed plunker: https://plnkr.co/edit/WgWFZQLxt9RFoZLR46HH?p=preview
use *ngFor and iterate through an array of Person, that way you can use the #Input. You probably want something like
<ul>
<li *ngFor="let person of people">
<test-component [User]=person></test-component>
</li>
</ul>
add people: Person[] to your main component and when you fetch items
p.then(v=>{
for (let i =0;i<v;i++)
{
let p = new Person();
p.Name = "name"+i;
p.Age = i;
people.push(p)
}
})

How to do *ng-for on an object with keys in Angular 2.0? [duplicate]

This question already has answers here:
access key and value of object using *ngFor
(21 answers)
Closed 1 year ago.
I am trying to do some things in Angular 2 Alpha 28, and am having an issue with dictionaries and ngFor.
I have an interface in TypeScript looking like this:
interface Dictionary {
[index: string]: string
}
In JavaScript this will translate to an object that with data might look like this:
myDict={'key1':'value1','key2':'value2'}
I want to iterate over this and tried this:
<div *ngFor="(#key, #value) of myDict">{{key}}:{{value}}</div>
But to no avail, none of the below worked either:
<div *ngFor="#value of myDict">{{value}}</div>
<div *ngFor="#value of myDict #key=index">{{key}}:{{value}}</div>
In all cases I get errors like Unexpected token or Cannot find 'iterableDiff' pipe supporting object
What am I missing here? Is this not possible anymore? (The first syntax works in Angular 1.x) or is the syntax different for iterating over an object?
Angular 6.1.0+ Answer
Use the built-in keyvalue-pipe like this:
<div *ngFor="let item of myObject | keyvalue">
Key: <b>{{item.key}}</b> and Value: <b>{{item.value}}</b>
</div>
or like this:
<div *ngFor="let item of myObject | keyvalue:mySortingFunction">
Key: <b>{{item.key}}</b> and Value: <b>{{item.value}}</b>
</div>
where mySortingFunction is in your .ts file, for example:
mySortingFunction = (a, b) => {
return a.key > b.key ? -1 : 1;
}
Stackblitz: https://stackblitz.com/edit/angular-iterate-key-value
You won't need to register this in any module, since Angular pipes work out of the box in any template.
It also works for Javascript-Maps.
It appears they do not want to support the syntax from ng1.
According to Miško Hevery (reference):
Maps have no orders in keys and hence they iteration is unpredictable.
This was supported in ng1, but we think it was a mistake and will not
be supported in NG2
The plan is to have a mapToIterable pipe
<div *ngFor"var item of map | mapToIterable">
So in order to iterate over your object you will need to use a "pipe".
Currently there is no pipe implemented that does that.
As a workaround, here is a small example that iterates over the keys:
Component:
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
}
try to use this pipe
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>
Updated : Angular is now providing the pipe for looping through a json Object via keyvalue :
<div *ngFor="let item of myDict | keyvalue">
{{item.key}}:{{item.value}}
</div>
WORKING DEMO , and for more detail Read
Previously (For Older Version) : Till now the best / shortest answer I found is ( Without any Pipe Filter or Custom function from Component Side )
Component side :
objectKeys = Object.keys;
Template side :
<div *ngFor='let key of objectKeys(jsonObj)'>
Key: {{key}}
<div *ngFor='let obj of jsonObj[key]'>
{{ obj.title }}
{{ obj.desc }}
</div>
</div>
WORKING DEMO
In addition to #obscur's answer, here is an example of how you can access both the key and value from the #View.
Pipe:
#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;
});
}
}
View:
<li *ngFor="let u of myObject |
keyValueFilter">First Name: {{u.key}} <br> Last Name: {{u.value}}</li>
So if the object were to look like:
myObject = {
Daario: Naharis,
Victarion: Greyjoy,
Quentyn: Ball
}
The generated outcome would be:
First name: Daario
Last Name: Naharis
First name: Victarion
Last Name: Greyjoy
First name: Quentyn
Last Name: Ball
Adding to SimonHawesome's excellent answer. I've made an succinct version which utilizes some of the new typescript features. I realize that SimonHawesome's version is intentionally verbose as to explain the underlying details. I've also added an early-out check so that the pipe works for falsy values. E.g., if the map is null.
Note that using a iterator transform (as done here) can be more efficient since we do not need to allocate memory for a temporary array (as done in some of the other answers).
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] }));
}
}
Here's a variation on some of the above answers that supports multiple transforms (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;
}
}
Usage
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>
I had a similar issue, built something for objects and 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;
}
}
Angular 2.x && Angular 4.x do not support this out of the box
You can use this two pipes to iterate either by key or by value.
Keys pipe:
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)
}
}
Values pipe:
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])
}
}
How to use:
let data = {key1: 'value1', key2: 'value2'}
<div *ngFor="let key of data | keys"></div>
<div *ngFor="let value of data | values"></div>
//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>
If someone is wondering how to work with multidimensional object, here is the solution.
lets assume we have following object in service
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;
}
in component add following function
challenges;
constructor(testService : TestService){
this.challenges = testService.getChallenges();
}
keys() : Array<string> {
return Object.keys(this.challenges);
}
finally in view do following
<div *ngFor="#key of keys();">
<h4 class="heading">{{challenges[key].title}}</h4>
<p class="description">{{challenges[key].description}}</p>
</div>
I have been tearing my hair out with trying to parse and use data returned form a JSON query/ api call. Im not sure exactly where i was going wrong, i feel like i have been circling the answer for days, chasing various error codes like:
"Cannot find 'iterableDiff' pipe supporting object"
"Generic TYpe Array requires one argument(s)"
JSON parsing Errors, and im sure others
Im assuming i just had the wrong combination of fixes.
So here's a bit of a summary of gotchas and things to look for.
Firstly check the result of your api calls, your results may be in the form of an object, an array, or an array of objects.
i wont go into it too much, suffice to say the OP's original Error of not being iterable is generally caused by you trying to iterate an object, not an Array.
Heres some of my debugging results showing variables of both arrays and objects
So as we generally would like to iterate over our JSON result we need to ensure it is in the form of an Array. I tried numerous examples, and perhaps knowing what i know now some of those would in fact work, but the approach i went with was indeed to implement a pipe and the code i used was that the posted by 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;
Honestly i think one of the things that was getting me was the lack of error handling, by adding the 'return undefined' call i believe we are now allowing for non expected data to be sent to the pipe, which obviously was occurring in my case.
if you don't want to deal with argument to the pipe (and look i don't think it's necessary in most cases) you can just return the following
if (!obj)
return undefined;
return Object.keys(obj);
Some Notes on creating your pipe and page or component that uses that pipe
is i was receiving errors about ‘name_of_my_pipe’ not being found
Use the ‘ionic generate pipe’ command from the CLI to ensure the pipe modules.ts are created and referenced correctly. ensure you add the following to the mypage.module.ts page.
import { PipesModule } from ‘…/…/pipes/pipes.module’;
(not sure if this changes if you also have your own custom_module, you may also need to add it to the custommodule.module.ts)
if you used the 'ionic generate page' command to make your page, but decide to use that page as your main page, remember to remove the page reference from app.module.ts (here's another answer i posted dealing with that https://forum.ionicframework.com/t/solved-pipe-not-found-in-custom-component/95179/13?u=dreaser
In my searching for answers there where a number of ways to display the data in the html file, and i don't understand enough to explain the differences. You may find it better to use one over another in certain scenarios.
<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>
However what worked that allowed me to display both the value and the key was the following:
<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>
to make the API call it looks like you need to import HttpModule into app.module.ts
import { HttpModule } from '#angular/http';
.
.
imports: [
BrowserModule,
HttpModule,
and you need Http in the page you make the call from
import {Http} from '#angular/http';
When making the API call you seem to be able to get to the children data (the objects or arrays within the array) 2 different ways, either seem to work
either during the call
this.http.get('https://SomeWebsiteWithAPI').map(res => res.json().anyChildren.OrSubChildren).subscribe(
myData => {
or when you assign the data to your local variable
posts: Array<String>;
this.posts = myData['anyChildren'];
(not sure if that variable needs to be an Array String, but thats what i have it at now. It may work as a more generic variable)
And final note, it was not necessary to use the inbuilt JSON library
however you may find these 2 calls handy for converting from an object to a string and vica versa
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);
I hope this compilation of info helps someone out.
The dictionary is an object, not an array. I believe ng-repeat requires an array in Angular 2.
The simplest solution would be to create a pipe/filter that converts the object to an array on the fly. That said, you probably want to use an array as #basarat says.
If you have es6-shim or your tsconfig.json target es6, you could use ES6 Map to make it.
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>
In JavaScript this will translate to an object that with data might look like this
Interfaces in TypeScript are a dev time construct (purely for tooling ... 0 runtime impact). You should write the same TypeScript as your JavaScript.
Define the MapValuesPipe and implement 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;
}
}
Add your pipe in your pipes module. This is important if you need to use the same pipe in more than one components:
#NgModule({
imports: [
CommonModule
],
exports: [
...
MapValuesPipe
],
declarations: [..., MapValuesPipe, ...]
})
export class PipesAggrModule {}
Then simply use the pipe in your html with *ngFor:
<tr *ngFor="let attribute of mMap | mapValuesPipe">
Remember, you will need to declare your PipesModule in the component where you want to use the pipe:
#NgModule({
imports: [
CommonModule,
PipesAggrModule
],
...
}
export class MyModule {}
So I was going to implement my own helper function, objLength(obj), which returns just Object(obj).keys.length. But then when I was adding it to my template *ngIf function, my IDE suggested objectKeys(). I tried it, and it worked. Following it to its declaration, it appears to be offered by lib.es5.d.ts, so there you go!
Here's how I implemented it (I have a custom object that uses server-side generated keys as an index for files I've uploaded):
<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>{{file.value['fileName']}}</td>
<td class="actions">
<a title="Delete File" (click)="deleteAFile(file.key);">
</a>
</td>
</tr>
</table>
</div>
There's another way to loop over objects, using structural directives:
I prefer this approach because it "feels" most like the normal ngFor loop. :-)
(In this case for example I added Angular's context variables let i = index | even | odd | first | last | count) that are accessible inside my loop).
#Directive({
selector: '[ngForObj]'
})
export class NgForObjDirective implements OnChanges {
#Input() ngForObjOf: { [key: string]: any };
constructor(private templateRef: TemplateRef<any>, private viewContainerRef: ViewContainerRef) { }
ngOnChanges(changes: SimpleChanges): void {
if (changes.ngForObjOf && changes.ngForObjOf.currentValue) {
// remove all views
this.viewContainerRef.clear();
// create a new view for each property
const propertyNames = Object.keys(changes.ngForObjOf.currentValue);
const count = propertyNames.length;
propertyNames.forEach((key: string, index: number) => {
const even = ((index % 2) === 0);
const odd = !even;
const first = (index === 0);
const last = index === (count - 1);
this.viewContainerRef.createEmbeddedView(this.templateRef, {
$implicit: changes.ngForObjOf.currentValue[key],
index,
even,
odd,
count,
first,
last
});
});
}
}
}
Usage in your template:
<ng-container *ngForObj="let item of myObject; let i = index"> ... </ng-container>
And if you want to loop using an integer value, you can use this directive:
#Directive({
selector: '[ngForInt]'
})
export class NgForToDirective implements OnChanges {
#Input() ngForIntTo: number;
constructor(private templateRef: TemplateRef<any>, private viewContainerRef: ViewContainerRef) {
}
ngOnChanges(changes: SimpleChanges): void {
if (changes.ngForIntTo && changes.ngForIntTo.currentValue) {
// remove all views
this.viewContainerRef.clear();
let currentValue = parseInt(changes.ngForIntTo.currentValue);
for (let index = 0; index < currentValue; index++) {
this.viewContainerRef.createEmbeddedView(this.templateRef, {
$implicit: index,
index
});
}
}
}
}
Usage in your template (example: loop from 0 to 14 (= 15 iterations):
<ng-container *ngForInt="let x to 15"> ... </ng-container>

Categories

Resources