3

Trying to display data in template HTML from a component to service call which calls and returns an API, but I'm getting this error

ERROR Error: Cannot find a differ supporting object '[object Object]' of type 'object'. NgFor only supports binding to Iterables such as Arrays.

HTML

<li *ngFor="let item of testing"> 
   <a [routerLink]="[item.url]" > 
    <span>{{item.name}}</span>
   </a>
</li>

Component

testing: any;

this.arsSevice.getMenu()
     .subscribe(
         result => {
            this.testing = result;
            console.log('menu',result);
      },
      error => {
         console.log('menu error', error);
      }
      )

Service:

getMenu()  {
    return this.http.post(this.menuUrl, JSON.stringify({
        "UserID": 61525,
        "AppID": 15,
        "NavAppID":null,
        "AppGroupID": 116,
        "SelectedCaseID": 0,
        "SelectedRoleID":0            
    }), httpOptions)
        .map((response: Response)=> {
            return response;
        })
}

Image screenshot of the data

Screenshot image of the data

Update

I see a data problem

"menu" has data: and it what is HERE and NOT working.

the working one is from a different API call Notice that is has

data: Array(16)

How can I fix my data from object to array ?

enter image description here

  • Are you using Http or HttpClient? – Al-Mothafar Dec 04 '17 at 22:27
  • httpclient i'm using –  Dec 04 '17 at 22:33
  • @Jota.Toledo 1. I did do research, in fact I not looked, but i have looked , I'm also doing a very similar call in my service that works fine. 2. I don't appreciate being treated poorly as I cannot locate my old credentials where my core skills are not web... I had a SO account with over 20,000 points . –  Dec 04 '17 at 22:37
  • https://angular.io/guide/http – Jota.Toledo Dec 04 '17 at 23:21
  • Are these data came from firebase? because firebase arrays return as an object instead of the array even if they showed in the console as an array. – Al-Mothafar Dec 05 '17 at 08:43
  • No, not from firebase - i usually code the web api... but another guy was coding it –  Dec 05 '17 at 16:18

3 Answers3

3

The Http service returns a Response object, calling json() on which should give you the data returned by the backend.

Change return response; to return response.json();

  • `TypeError: response.json is not a function` –  Dec 04 '17 at 22:38
  • 1
    he using `HttpClient` and he mentioned already that he using Angular 5, so I think there is no need for `response.json()` as it is the default response to be json. – Al-Mothafar Dec 04 '17 at 23:02
  • plus it blows up / doesn't work ( response.json) with the httpClient –  Dec 04 '17 at 23:06
  • Added a new screenshot of my data –  Dec 05 '17 at 00:58
  • Both the APIs look different to me. One is returning the Status as success while other as fail. –  Dec 05 '17 at 10:02
  • i need to ask the guy that coded that , it pulls data both ways , not sure why the one says fail , i think he just has a flag that is default to fail , but doesn't update , which it is not working ... –  Dec 05 '17 at 16:20
1

Try this: *ngFor="let item of testing.data.Menu1Items". I do not believe you need the async pipe for this. I would also *ngIf which ever div is containing the *ngFor loop.

Like so:

<div *ngIf="testing">
    <li *ngFor="let item of testing.data.Menu1Items"> 
       <a [routerLink]="[item.url]" > 
        <span>{{item.name}}</span>
       </a>
    </li>
</div>

Let me know if this can help out with what you are trying to achieve.

EDIT:

So I would suggest formatting your data differently when returning a response to your client, this is how I would format the data before its returned:

[
  {
    actionType: '',
    displayName: 'MyiCIS',
    displayOrder: '1',
    displayOrder1: null,
    groupName: 'Data Entry',
    id: 420,
    url: 'MyiCIS.asp',
    type: 'Menu1Items'
  },
  {
    actionType: '',
    displayName: 'MyiCIS',
    displayOrder: '1',
    displayOrder1: null,
    groupName: 'Data Entry',
    id: 420,
    url: 'MyiCIS.asp',
    type: 'Menu1Items'
  },
  {
    actionType: '',
    displayName: 'MyiCIS',
    displayOrder: '1',
    displayOrder1: null,
    groupName: 'Data Entry',
    id: 420,
    url: 'MyiCIS.asp',
    type: 'Menu2Items'
  },
  {
    actionType: '',
    displayName: 'MyiCIS',
    displayOrder: '1',
    displayOrder1: null,
    groupName: 'Data Entry',
    id: 420,
    url: 'MyiCIS.asp',
    type: 'Menu2Items'
  }
];

Then if you want to group the data by whichever field you choose, run the array through this method and it will spit out the data grouped into separate arrays:

transformArray(array: Array<any>, field) {
    if (array) {
      const groupedObj = array.reduce((prev, cur) => {
        if (!prev[cur[field]]) {
          prev[cur[field]] = [cur];
        } else {
          prev[cur[field]].push(cur);
        }
        return prev;
      }, {});
      return Object.keys(groupedObj).map(key => ({ key, value: groupedObj[key] }));
    }
    return [];
  }

Here is a console log of the data before its transformed and then after its been transformed:

enter image description here

So the first array is just what should be returned from the server, and the second array is after the transformation.

Then to loop through this in your markup, here is the structure of the *ngFor:

<div *ngFor"let category of data">
  <div>{{ category.key }}</div>
  <div *ngFor="let item of category.value">
    {{ value.name }}
  </div>
</div>

I hope this can be of help, I think your first step should be formatting that array before its returned to the client as an array of objects not grouped by a key, and then manipulate the data once it hits your client.

Nicholas Pesa
  • 2,156
  • 2
  • 24
  • 45
  • didn't work, but I added a new screenshot showing data that works for another call, and the data that doesn't . Perhaps I need some conversion to an Array? –  Dec 05 '17 at 00:59
  • So what is the actual format of the array you are getting back from your HTTP request? I see two different formats above. – Nicholas Pesa Dec 05 '17 at 16:11
  • the one with "menu" is the one i need, the other one is an example of a similar one ( web api -> angular service - component - html ) but the data is data: vs an data array –  Dec 05 '17 at 16:22
  • You are formatting the data from your API with those indexes as keys such as 'Menu1Items'? If so, you should return that data with indexes as numbers instead of text and then have another field inside of the object in the array called 'type' which has a value of 'Menu1Items'. If you can copy ad paste the returned object from the HTTP request I can format it correctly as an example for you. – Nicholas Pesa Dec 05 '17 at 18:33
  • Currently I am doing this `this.menu1 = result["data"].Menu1Items` –  Dec 05 '17 at 19:20
  • then my loop works `
    `
    –  Dec 05 '17 at 19:21
  • So this is correctly working now? If I was of any help, I always appreciate a marked answer :) – Nicholas Pesa Dec 06 '17 at 22:46
0

That's because you're trying to iterate through data object. Try:

<li *ngFor="let item of testing.Menu1Items"> 
Mateusz Witkowski
  • 1,646
  • 10
  • 24
  • `TypeError: Cannot read property 'Menu1Items' of undefined` –  Dec 04 '17 at 22:33
  • That's because `testing` is undefined before Observable from the service emitts anything. To prevent this error you can use `async` pipe or guard against undefined by wrapping `li` element in `ng-container` with `*ngIf="testing"`. – Mateusz Witkowski Dec 04 '17 at 22:56
  • | async ? or wrapping will prevent ? –  Dec 04 '17 at 22:57
  • `Error: InvalidPipeArgument: '[object Object]' for pipe 'AsyncPipe'` –  Dec 04 '17 at 22:59
  • `
  • ` is what i was trying ..
  • –  Dec 04 '17 at 23:00
  • New Screenshot should show you my problem thx –  Dec 05 '17 at 00:59
  • @MateuszWitlowski - nearly there, I believe you need a 'safe navigation operator' to cover the period of time before the post has returned, e.g `testing?.Menu1Items`. That gets rid of the 'can't read property of undefined' error. – Richard Matsen Dec 05 '17 at 04:47