0

I am new to Angular. I have a Node and Express backend pulling data from an MS SQL database. If I go to the endpoint URL it displays my data as JSON. I am running on localhost so I set a proxy for CORS. I have a class that defines the data, a service that pulls the data from the endpoint and a component that tries to set an array equal to the data pulled from the service. The HTML has an *ngFor that is supposed to loop through the values and display them in a grid.

If I call my data in my component through my service, so this.userService.getUsers(), and do a console.log I can see the recordset in the browser console. I try to set the array equal to the userService.getUsers() and then call the array and I get "undefined". Being that I am new, I have tried to follow the Heroes tutorial and that did not work. I spent a day searching Google and trying different solutions that I have come across but they all come up as undefined. I will attach the code here. If someone can guide me a bit, it would be much appreciated.

User class defining User:

export class User{
    id: number;
    ccn: string;
    firstName: string;
    lastName: string;
    email: string;
}

User Service doing Http request:

import { Injectable } from '@angular/core';
import { User } from './user';
import { USERS } from './mock-users';
import { MessageService } from './message.service';
import { Observable, of } from 'rxjs';
import { HttpClient, HttpHeaders } from '@angular/common/http';
import { catchError, map, tap } from 'rxjs/operators';

@Injectable({
  providedIn: 'root'
})
export class UserService {

  private userURL = 'api/users'
  //private userURL = 'localhost:5000'

  httpOptions = {
    headers: new HttpHeaders({ 'Content-Type': 'application/json' })
  };

  constructor(
    private http: HttpClient,
    private messageService: MessageService) { }

  //getUsers(): Observable<User[]> {
  //  this.messageService.add('UserService: fetched users');
  //  return of(USERS);
  //}

  /** GET users from the server */
  getUsers(): Observable<User[]> {
    //console.log('getting users');
    return this.http.get<User[]>("http://localhost:5000/api/user")
      .pipe(
        tap(_ => this.log('Fetched users')),
        catchError(this.handleError<User[]>('getUsers', []))
      );
      //return this.http.get<User[]>("http://localhost:5000/api/user");
      //console.log('got users');
  }

  /* GET heroes whose name contains search term */
  searchUsers(term: string): Observable<User[]> {
    if (!term.trim()) {
      // if not search term, return empty hero array.
      return of([]);
    }
    return this.http.get<User[]>(`${this.userURL}/?ccn=${term}`).pipe(
      tap(_ => this.log(`found users matching "${term}"`)),
      catchError(this.handleError<User[]>('searchUsers', []))
    );
  }

  addUser (user: User): Observable<User> {
    return this.http.post<User>(this.userURL, user, this.httpOptions).pipe(
      tap((newUser: User) => this.log(`added user w/ id=${newUser.id}`)),
      catchError(this.handleError<User>('addUser'))
    );
  }

  private handleError<T> (operation = 'operation', result?: T) {
    return (error: any): Observable<T> => {
      console.error(error);

      this.log(`${operation} failed: ${error.message}`);

      return of(result as T);
    };
  }

  private log(message: string) {
    this.messageService.add(`User service: ${message}`);
  }
}

Display Users Component TS file:

import { Component, OnInit } from '@angular/core';
//import { USERS } from '../mock-users';
import { UserService } from '../user.service';
import { User } from '../user';
import { Observable, of } from 'rxjs';
import { catchError, map, tap } from 'rxjs/operators';
import { element } from 'protractor';

@Component({
  selector: 'app-display-users',
  templateUrl: './display-users.component.html',
  styleUrls: ['./display-users.component.css']
})
export class DisplayUsersComponent implements OnInit {
  users: User[] = [];

  constructor(private userService: UserService) { }

  //users$ = this.getUsers();

  ngOnInit() {
    this.getUsers();
    console.log(this.userService.getUsers());
    this.userService.getUsers().forEach(element => {
      console.log(element);
    });
  }

  getUsers(): void {
    /*this.userService.getUsers()
    .subscribe(users => this.users = users);*/
    const userObservable = this.userService.getUsers();
    userObservable.subscribe((userData: User[]) => {
      this.users = userData;
    });
  }

}

Display Users Component HTML:

<div class="clr-row">
    <div class="clr-col-lg-11 clr-col-md-11 clr-col-11 main-div">
        <div class="card card-style" style="box-shadow: 0 0 0 0;">
            <div class="card-header">
                <h1><img src="../assets/images/BSOLOGO_gray.png" class="title-img"><span class="title">&nbsp;&nbsp;Users</span></h1>
            </div>
            <div class="card-block">
                <div class="card-title">
                    <clr-datagrid>
                        <clr-dg-column>CCN</clr-dg-column>
                        <clr-dg-column>Last Name</clr-dg-column>
                        <clr-dg-column>First Name</clr-dg-column>
                        <clr-dg-column>Email</clr-dg-column>

                        <clr-dg-row *ngFor="let user of users">
                            <clr-dg-cell>{{user.ccn}}</clr-dg-cell>
                            <clr-dg-cell>{{user.lastName}}</clr-dg-cell>
                            <clr-dg-cell>{{user.firstName}}</clr-dg-cell>
                            <clr-dg-cell>{{user.email}}</clr-dg-cell>
                        </clr-dg-row>

                        <clr-dg-footer>{{users.length}} users</clr-dg-footer>
                    </clr-datagrid>
                </div>
            </div>
        </div>
    </div>
</div>

Any help would be greatly appreciated!

UPDATED enter image description here

user41814
  • 19
  • 6
  • '*I try to set the array equal to the userService.getUsers() and then call the array and I get "undefined"*' -- that's because it is an *asynchronous* call. You will need to subscribe to it and then fetch the data as you have already done. – Nicholas K Jan 07 '20 at 07:23
  • Hey Nick, I don't quite follow. Do I need to subscribe in another location? – user41814 Jan 07 '20 at 13:45
  • You already subscribed to it in the `ngOnInit()` – Nicholas K Jan 07 '20 at 16:30

3 Answers3

0

Ypu can replace getUsers on both classes by these. HTML looks fine to me. I converted users to public too.

//userService
getUsers(callback: Function) {
    return this.http.get<User[]>("http://localhost:5000/api/user")
      .subscribe(
        response => callback(response)
      );
  }

//Component
public users: User[] = [];
getUsers(): void {
    this.userService.getUsers((result) => {this.users = result;})
  }
Mohamed Farouk
  • 1,089
  • 5
  • 10
  • Thank you for the response. I made those changes, but I'm still getting the same error. This is the error that shows up in the browser console window: – user41814 Jan 07 '20 at 09:41
  • ERROR Error: "Error trying to diff '[object Object]'. Only arrays and iterables are allowed" Perhaps it is the way the data is getting pulled from within the node.js side? This is how it is displaying in my browser. (In another comment as it won't fit in this one. – user41814 Jan 07 '20 at 09:48
  • recordsets 0 0 JSON_F52E2B61-18A1-11d1-B105-00805F49916B "[{\"userID\":2,\"userCCN\":\"id number\",\"userFirst\":\"first name\",\"userLast\":\"last name\",\"userEmail\":\"email\"}]" recordset 0 JSON_F52E2B61-18A1-11d1-B105-00805F49916B "[{\"userID\":2,\"userCCN\":\"id number\",\"userFirst\":\"first name\",\"userLast\":\"last name\",\"userEmail\":\"email\"}]" output {} rowsAffected 0 7 – user41814 Jan 07 '20 at 09:50
  • Please console.log(result) – Mohamed Farouk Jan 07 '20 at 11:38
  • [WDS] Disconnected! client:172 Angular is running in the development mode. Call enableProdMode() to enable the production mode. core.js:38781 [WDS] Live Reloading enabled. client:52 ERROR Error: "Error trying to diff '[object Object]'. Only arrays and iterables are allowed" Angular 7 View_DisplayUsersComponent_0 DisplayUsersComponent.html:15 Angular 23 RxJS 5 Angular 9 DisplayUsersComponent.html:15:24 ERROR CONTEXT Object { view: {…}, nodeIndex: 77, nodeDef: {…}, elDef: {…}, elView: {…} } DisplayUsersComponent.html:15:24 – user41814 Jan 07 '20 at 13:44
  • I did find something that says map((data: any) => data.result) needs to be added within the .pipe on the service side. That got rid of the only arrays and iterables are allowed error but I am still getting undefined. – user41814 Jan 07 '20 at 13:47
  • Please remove ngFor temporarily and send me the result of console.log(result) as it seems the array in wrapped into another object. – Mohamed Farouk Jan 07 '20 at 20:42
  • I just removed the ngFor and updated the original post with a picture of the error. My array is length 0 so I know my object isn't populating the array, I'm just not sure how to properly subscribe to it. – user41814 Jan 08 '20 at 00:18
  • Can you send a screenshot of Response Preview tab under Network to make sure there is data sent from the server and how it looks like – Mohamed Farouk Jan 08 '20 at 00:59
  • Also for troubleshooting, you can replace this.http.get( by this.http.get( – Mohamed Farouk Jan 08 '20 at 00:59
0

If you do not need it to be Observable you can use toPromise() and using async/await makes it waaay easier

Service

  async getUsers(): Promise<User[]> {
    return await this.http.get<User[]>('http://localhost:5000/api/user').toPromise();
  }

Component.ts

  users: User[] = [];

  async ngOnInit() {
    this.users = await this.userService.getUsers();
  }

Component.html

                 <clr-datagrid *ngIf="users">
                    <clr-dg-column>CCN</clr-dg-column>
                    <clr-dg-column>Last Name</clr-dg-column>
                    <clr-dg-column>First Name</clr-dg-column>
                    <clr-dg-column>Email</clr-dg-column>

                    <clr-dg-row *ngFor="let user of users">
                        <clr-dg-cell>{{user.ccn}}</clr-dg-cell>
                        <clr-dg-cell>{{user.lastName}}</clr-dg-cell>
                        <clr-dg-cell>{{user.firstName}}</clr-dg-cell>
                        <clr-dg-cell>{{user.email}}</clr-dg-cell>
                    </clr-dg-row>

                    <clr-dg-footer>{{users.length}} users</clr-dg-footer>
                </clr-datagrid>
Eray Ismailov
  • 305
  • 3
  • 8
0

My issue has been resolved. In my SQL statement I was calling SELECT * FROM table FOR JSON PATH which was creating a weird object being pulled from the server. Removing the FOR JSON PATH provided JSON data. Then the second part of my issue was mapping my DB fields with my user class.

This was done like this:

    request.query('SELECT * FROM Table ORDER BY myField', function (err, recordset) {
        if (err) console.log(err);    
        const records = recordset.recordset;
        const result = records.map(r => { return { id: r.tableID, field1: r.dbField1, field2: r.dbField2, field3: r.dbField3, field4: r.dbField4}});
        res.send(result);
    });

I hope this helps someone! Thanks to everyone that posted to help me.

user41814
  • 19
  • 6