0

I am calling a JSON file using rx/js Observable.

Service

constructor(private http: Http){

}

 getUserById():Observable<User[]> {

let url = this.mockData;
let header = new Headers({'Content-Type': 'application/json'});

return this.http.get(url)
    .map((res:Response) =><User[]>res.json())
    .do(data => console.log("User Data: "+JSON.stringify(data)))
    .catch((error:any) => Observable.throw(error) || "Mock Data Error get by role")
};

component:

    @Input() users: User[] = [];

        userTemp: User[] = [];

getUserById(userId){

        this._searchService.getUserById()
            .filter(users => {
                for(let user of users){
                    if(user.userName === userId){
                        this.userTemp.push(user);
                    }}});

        return this.userTemp;

    }

under users => { says when I hover over it (Users: Users[]) => void is not assignable) => boolean.

I know there is a small detail I am missing but I am not sure where it is. Any advice would be greatly appreciated.

------------------------------update 1-------------------------- Trying this now but and it compiles but doesn't work.

this._searchService.getUserById()
            ._do(users => {
                for(let user of this.users){
                    if(user.id == userId){
                        this.userTemp.push(user);
                    }
                }
            });

---------------------Update 2-------------------------- I got some advice from the posts below and now I am trying to do it like this, but it is rendering nothing and giving no errors.

In component:

@Input() users: IUser[] = [];

Method being called in component:

case 'role':
          this.users = this.getUserByRole(this.role);
          console.log(this.role);
          break;

method in component:

 getUserByRole(role){

        this._searchService.getUsers()
            .subscribe(users => {
                for(let user of users) {
                    if(user.role == role){
                        this.users.push(user);
                    }}});

        return this.users;
    }

Service method:

getUsers(): Observable<IUser[]> {
        let users = this.http
            .get(this.url)
            .map(res => res.json());
        return users;
    }

In the console it will print whatever I put in the input box and I confirmed the url for the http call is valid.

I am trying to do it like this so all I need to do is change the http url to have headers and a real url later without have to change to much.

--------------Update 3----------------------

In the HTML I have:

 <td>{{data.role | async}}</td>

method in component:

getUserByRole(role){

        return this._searchService.getUsers()
            .map(data => {
                return data.filter((x:any) => x.role == role)
            });
    }

Field:

 @Input() users: Observable<IUser[]>;

Calling the method:

case 'role':
         console.log(this.getUserByRole(this.role));
         this.users = this.getUserByRole(this.role);
         console.log(this.users);
         break;

error:

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

In my searchService I am now looking at doing it this way but still not working:

 findUserByRole(user: IUser) {
        return this.http.get(this.url)
            .map(res => res.json())
            .subscribe(
                data => {

                }
            );
    }
Mike3355
  • 11,305
  • 24
  • 96
  • 184
  • When you put a `console.log(this.users);` just before you return it, what do you see in the log? – eko Mar 30 '17 at 18:33
  • @echonax before and after just renders `[ ]` – Mike3355 Mar 30 '17 at 18:42
  • Do you know how async operations work? Or have you read AJT_82's answer carefully? – eko Mar 30 '17 at 18:46
  • @echonax Yes I did and AJT made a very nice example. However I am unable to get it to work. The end goal is to not call `getUsers` all the time. I might only get one and the backend will only send one but I still need an array on my end to loop through in my HTML file just in case. I get that filter will do that for me so I am implementing that now. Please see update 3. – Mike3355 Mar 30 '17 at 19:14
  • @echonax if I put `*ngFor='let data of users | async'` I get an error saying `data.filter is not a function` – Mike3355 Mar 30 '17 at 19:24

2 Answers2

1

EDIT based on updates:

Not entirely sure, what you are trying to achieve with: <td>{{data.role | async}}</td>, but if you want to filter users based on a specific role using async pipe would be to declare users as an Observable like you have. When you want to filter, have your switch statement (like you have):

case 'role':
      this.users = this.getUserByRole(this.role);
      break;

and in this method you return an observable of the filtered array

getUserByRole(role){
    return this._searchService.getUsers()
        .map(data => {
            return data.filter((x:any) => x.role == role)
        });
}

which you then can use together with async pipe, which displays the users which have the filtered values from your switch-case, whatever that filter value might be. In this case we have filtered above based on a role value.

<div *ngFor="let user of users | async">

DEMO

Update 2:

Now seeing your JSON, the problem should be that your array is in an object named data. You want the array from the response, so you need to extract that array from the object, so change your mapping a bit:

return this.http.get(url)
    .map((res:Response) =><User[]>res.json().data) // here! Extract the array from the object "data"
    .do(data => console.log("User Data: "+JSON.stringify(data)))
    .catch((error:any) => Observable.throw(error) || "Mock Data Error get by role")
};

Original post:

filter actually does the filtering for you, you do not need to iterate the response. Unfortunately I do not see that you are able to return userTemp other than as an observable in your filtering. You actually have to subscribe to it and assign the values from the filter to your userTemp, the following will always return undefined as you are returning the array outside the callback:

getUserById(userId){
   this._searchService.getUserById()
     .map(users => {
        ......
     });
     return this.userTemp; // data has not been assigned yet, when you return it.
}

This is just how it works. You need to do manipulate your data inside the callback! Here is some more info: How do I return the response from an Observable/http/async call in angular2?

But back to your code, You can map several times, so here you would use map together with filter to get the data you want:

getUserById(userId) {
 return this.service.getUsers()
   .map(data => {
     return data.filter((x:any) => x.userName == userId)
   });
}

So this returnes an observable to where you need it.

But as said, AFAIK you cannot return the userTemp as a plain JS array.

Community
  • 1
  • 1
AT82
  • 71,416
  • 24
  • 140
  • 167
  • Thank you for the response. I been trying to get this to work but it seems there is still something I am missing. Please see update 2. – Mike3355 Mar 30 '17 at 18:28
  • This is so frustrating. I am doing exactly that to which you are doing in your update. Only different is using `@Input() users: Observable;` and not `@Input() users: any;` but that didn't seem to matter. I am getting an error saying: `Cannot find a differ supporting object '[object Object]' of type 'object'. NgFor only supports binding to Iterables such as Arrays. ` – Mike3355 Mar 31 '17 at 14:23
  • Could you perhaps fork my plunker and make a MCV, I'd be happy to take a look :) Somewhere, something has to be wrong, since it doesn't work. Try and reproduce the issue in a plunker and I'll take a look. Is there btw a reason why you use `@Input()` here? – AT82 Mar 31 '17 at 14:32
  • Maybe stupid, but have you made sure there is data coming in? That error can be thrown if there's nothing to iterate. The other option that I can think of that it's simply not an array, but "something else". Take a close look at your incoming data. Somewhere there lies the issue, pretty sure ;) – AT82 Mar 31 '17 at 14:34
  • Could you add a sample of your JSON? – AT82 Mar 31 '17 at 14:40
  • You are trying to iterate an Object `data`. You need to adjust the map in your service and extract the array from your json, so: `.map((res:Response) =>res.json().data)` I'll edit my answer. Then your problem should be solved I think ;) – AT82 Mar 31 '17 at 15:15
  • Let us [continue this discussion in chat](http://chat.stackoverflow.com/rooms/139607/discussion-between-drew1208-and-ajt-82). – Mike3355 Mar 31 '17 at 15:25
0

The callback for .filter requires that you return a boolean, but you're returning nothing (aka void). You need either need to return a boolean or use a different method.

CWSpear
  • 3,230
  • 1
  • 28
  • 34