Observable to Array to display *ngFor showing as undefined

2,737 views
Skip to first unread message

Ronald Faircloth

unread,
Jan 7, 2020, 10:20:58 AM1/7/20
to Angular and AngularJS discussion
Hey All,

I am working on my first Angular project and I'm having a bit of trouble with something.  I have a node.js backend with Express pulling data from an MS SQL Server database.  I can display the data through the web browser from the backend and I can see the JSON data being returned.  Next I connected my endpoint and setup CORS for my local host.  I have a service setup to pull the data from the endpoint.  I implement that service within my component and if I print the data out to the console from within my component I get the recordset.  The issue I am having is setting an array from my endpoint data.  As I am very new to Angular I have tried following the Heroes version which I could not get and then I tried a few other versions as well that I came across online and none of them work.  When I set my array equal to my observable and then print the array to the console I get undefined.  Below is my code if anyone could be of assistance.

User Class:

export class User{
    idnumber;
    ccnstring;
    firstNamestring;
    lastNamestring;
    emailstring;
}

User Service:

import { Injectable } from '@angular/core';
import { User } from './user';
import { USERS } from './mock-users';
import { MessageService } from './message.service';
import { Observableof } from 'rxjs';
import { HttpClientHttpHeaders } from '@angular/common/http';
import { catchErrormaptap } 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 httpHttpClient,
    private messageServiceMessageService) { }

  //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(termstring): 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 (userUser): Observable<User> {
    return this.http.post<User>(this.userURLuserthis.httpOptions).pipe(
      tap((newUserUser=> this.log(`added user w/ id=${newUser.id}`)),
      catchError(this.handleError<User>('addUser'))
    );
  }

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

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

      return of(result as T);
    };
  }

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

Display Users Component TS File:

import { ComponentOnInit } from '@angular/core';
//import { USERS } from '../mock-users';
import { UserService } from '../user.service';
import { User } from '../user';
import { Observableof } from 'rxjs';
import { catchErrormaptap } 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 {
  usersUser[] = [];

  constructor(private userServiceUserService) { }

  //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((userDataUser[]) => {
      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!


Ronnie

Jean Marc

unread,
Jan 7, 2020, 10:32:26 AM1/7/20
to Angular and AngularJS discussion
Hello, you do some things wrong.
You need ever to subscribe to an observable to get the answer, if you don't you can't receive the answers.
An observable is an object as a stream, so a forEach has not the same behaviour than an array, this not what you want to do there.
The assignment inside the ".subscribe" is the good way.
You can see your users name, email..etc already no ?

Ronald Faircloth

unread,
Jan 7, 2020, 11:20:02 AM1/7/20
to ang...@googlegroups.com
Hey Jean,

Yes, I can see the data within the recordset in the browser console window. What I am finding is similar to what you are saying but I'm not really sure how to fix it. I've tried a few different things including adding map() within my .pipe in the service. If I'm following right on what you are saying and what I'm finding online I need to subscribe to the observable and make it an array but I'm not sure how to go about doing that and what I have found online so far has not worked for me.

Ronnie

--
You received this message because you are subscribed to the Google Groups "Angular and AngularJS discussion" group.
To unsubscribe from this group and stop receiving emails from it, send an email to angular+u...@googlegroups.com.
To view this discussion on the web visit https://groups.google.com/d/msgid/angular/6a8578e5-918a-4782-8be6-42e9a48ae92f%40googlegroups.com.

Arnaud Deman

unread,
Jan 7, 2020, 11:22:22 AM1/7/20
to Angular and AngularJS discussion
Hey Ronald,

I think Jean Marc is true, you are writing  the observable object to the console when you do:
console.log(this.userService.getUsers());

I you want to trace the users fetched from your backend you could do it in your service in the tap operator for instance:
tap(users => this.log('Fetched users, users')),

The interest of doing it here is that you will  see all the calls to your backend. Sometime there is more than you expect if you subscribe several times to your observable. In that case you can use an operator like ShareReplay.

In DisplayUsersComponent you can subscribe to the observable and store the array of users but I think that to avoid memory leak, you will have to store also the subscription in order to unsubscribe when the component is destroyed (ngOnDestroy hook).

Another solution would be to store only the observable in DisplayUsersComponent:
users$: Observable<User[]> = this.userService.getUsers()

You can then use it in the template with the async pipe :
<clr-dg-row *ngFor="let user of users$ | async">

The async pipe will handle the subscription / unsubscrition for you.

Regards,
Arnaud.

Ronald Faircloth

unread,
Jan 7, 2020, 11:29:11 AM1/7/20
to ang...@googlegroups.com
Thank you Arnaud, I had tried something similar with async but probably implemented it wrong. I will try again following what you have and see if that gets me further. Traveling at the moment so won't be able to test it until I get situated at my hotel. I'll let you both know how it works. I appreciate the assistance. It can be quite frustrating trying to learn something new and spending days trying to solve what is probably a very simple problem. 

Ronnie

--
You received this message because you are subscribed to the Google Groups "Angular and AngularJS discussion" group.
To unsubscribe from this group and stop receiving emails from it, send an email to angular+u...@googlegroups.com.

Ronald Faircloth

unread,
Jan 7, 2020, 7:28:52 PM1/7/20
to Angular and AngularJS discussion
Hey Arnaud,

I am having trouble determining how to implement users$: Observable<User[]> = this.userService.getUsers().  I get an error at [] and =.  At [] it says "an element access expression should take an argument."  At = it says "expression expected".

Ronnie 

Arnaud Deman

unread,
Jan 8, 2020, 4:49:50 AM1/8/20
to Angular and AngularJS discussion
Hey Ronnie,

There is maybe a Typo but the global idea would be:

(...)
export class DisplayUsersComponent implements OnInit {
  users$: Observable<User[]>;

(...)

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

I hope this will help !

Regards,
Arnaud.

456avijit .

unread,
Jan 8, 2020, 5:14:07 AM1/8/20
to ang...@googlegroups.com
Hi.

If you are using observable then you need to use subscribe to observable object to get data to local var.

--
You received this message because you are subscribed to the Google Groups "Angular and AngularJS discussion" group.
To unsubscribe from this group and stop receiving emails from it, send an email to angular+u...@googlegroups.com.

Arnaud Deman

unread,
Jan 8, 2020, 5:47:24 AM1/8/20
to Angular and AngularJS discussion
Hi,

One possibilty is to subcribe and store the result. Another one is to store the observable and use the async pipe in the template (cf previous messages). The subscription / unsubscription is then handled by the pipe.

Regards,
Arnaud.

On Wednesday, 8 January 2020 11:14:07 UTC+1, 456avijit . wrote:
Hi.

If you are using observable then you need to use subscribe to observable object to get data to local var.

To unsubscribe from this group and stop receiving emails from it, send an email to ang...@googlegroups.com.

Ronald Faircloth

unread,
Jan 8, 2020, 9:21:23 PM1/8/20
to Angular and AngularJS discussion
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.

muhammad haris

unread,
Jan 10, 2020, 1:36:24 AM1/10/20
to ang...@googlegroups.com
Hello dear , your return type in service is observable and if you need that data to be use in your component you need to subscribe it first . 
Regards
Haris

--
You received this message because you are subscribed to the Google Groups "Angular and AngularJS discussion" group.
To unsubscribe from this group and stop receiving emails from it, send an email to angular+u...@googlegroups.com.
To view this discussion on the web visit https://groups.google.com/d/msgid/angular/211d16d5-1f43-4662-8fa5-f2d7db793e8f%40googlegroups.com.
Reply all
Reply to author
Forward
0 new messages