1

I am new to angular and started leaning angular 4. Data not binding with ngfor directive on a component using the async pipe. Please help

user service uses HTTP request to get data from API:

user.service.ts

import { Injectable } from '@angular/core';
import { Http, Response, Headers, RequestOptions } from '@angular/http';
import { Observable } from 'rxjs/Observable';
import 'rxjs/add/operator/map';
import 'rxjs/add/operator/do';
import 'rxjs/add/operator/catch';
import { User } from "../Models/user";

@Injectable()
export class UserService {
    constructor(private http: Http) { }

    get(url: string): Observable<User[]> {
        return this.http.get(url)
            .map(response => response.json() as User[])
            // .do(data => console.log("All: " + JSON.stringify(data)))
            .catch(this.handleError);
    }
    private handleError(error: Response) {
        console.error(error);
        return Observable.throw(error.json().error || 'Server error');
    }
}

Here i am using observable User[] interface for user list:

user.component.ts

import { Component, OnInit, ViewChild } from '@angular/core';
import { UserService } from '../Services/user.service';
import { FormBuilder, FormGroup, Validators } from '@angular/forms';
import { ModalComponent } from 'ng2-bs3-modal/ng2-bs3-modal';
import { User } from '../Models/user';
import { DBOperation } from '../Shared/enum';
import { Observable } from 'rxjs/Observable';
import { Global } from '../Shared/global';

@Component({
    templateUrl: 'app/Components/user.component.html'
})

export class UserComponent implements OnInit {
    @ViewChild('modal') modal: ModalComponent;
    users$: Observable<User[]>;
    user: User;
    msg: string;
    indLoading: boolean = false;
    userForm: FormGroup;
    dbops: DBOperation;
    modalTitle: string;
    modalBtnTitle: string

    constructor(private fb: FormBuilder, private userService: UserService) 
   { }

   ngOnInit(): void {
    this.userForm = this.fb.group({
        Id: [''],
        UserName: ['', Validators.required],
        Password: ['', Validators.required],
        FirstName: ['', Validators.required],
        LastName: ['', Validators.required],
        Gender: ['', Validators.required]
    });
    this.LoadUsers();
    }
    LoadUsers(): void {
        this.indLoading = true;
        this.users$ = this.userService.get('http://localhost:29712/api/userapi/');
        this.indLoading = false;
    }   
}

Template for the async pipe to subscribe observable users variable:

user.component.html

<div class='panel panel-primary'>
    <div class='panel-heading'>
        User Management
     </div>
     <div class='panel-body'>
         <div class='table-responsive'>
        <div class="alert alert-info" role="alert" *ngIf="indLoading"><img src="../../images/loading.gif" width="32" height="32" /> Loading...</div>
        <div *ngIf='users && users.length==0' class="alert alert-info" role="alert">No record found!</div>
        <table class='table table-striped' *ngIf='users && users.length'>
            <thead>
                <tr>
                    <th>First Name</th>
                    <th>Last Name</th>
                    <th>Gender</th>
                </tr>
            </thead>
            <tbody>
                <tr *ngFor="let user of users$ | async">
                    <td>{{user.FirstName}}</td>
                    <td>{{user.LastName}}</td>
                    <td>{{user.Gender}}</td>
                </tr>
            </tbody>
        </table>
        <div>
        </div>
    </div>
    <div *ngIf="msg" role="alert" class="alert alert-info alert-dismissible">
        <button type="button" class="close" data-dismiss="alert" aria-label="Close"><span aria-hidden="true">&times;</span></button>
        <span class="glyphicon glyphicon-exclamation-sign" aria-hidden="true"></span>
        <span class="sr-only">Error:</span>
        {{msg}}
    </div>
</div>

11
  • 1
    Since you are subscribing, you should not need the async pipe. Commented Jul 30, 2017 at 6:41
  • 1
    Your /api/userapi/ request will be executed twice Commented Jul 30, 2017 at 6:52
  • i'd check if response => response.json() as IUser[] resolves to null. are you sure that service responds with the same data structure as your IUser is? Commented Jul 30, 2017 at 7:36
  • @deezg His response is correct. Look at template ngIf="users && users.length" where users is Observable Commented Jul 30, 2017 at 7:44
  • Yes, I am also getting users json array in LoadUsers function Commented Jul 30, 2017 at 7:46

2 Answers 2

0

Since you are using async. Try removing susbcribe. on your original code. Here's a video of something similar. It's really helpful to use $ for Observable properties...

Sign up to request clarification or add additional context in comments.

4 Comments

Then he should change this part let user of users | async as well in template.
Its giving error that Type 'IUser' is not assignable to Type 'Observable<IUser[]>' because this.users is 'Observable<IUser[]>' Type
FYI: github.com/Microsoft/TypeScript/wiki/Coding-guidelines - Don't place I for interfaces or _ for private instances... I also normally put $ on end of variable to indicate it's an observable. So I'd have let user of users$ | async and change property in class
Not getting any error. Its not binding user list to template
0

Your *ngIf is making issue you cannot check directly your observable length without async pipe. Try this,

*ngIf="(users$ | async)?.length==0"

5 Comments

But length is 0 means there is no data and it will call one more http call
I would resolve async pipe once by using users$ | async as users
Why it call one more api. this.loads function will fire when ngOnInit runs and it will going to call the api.
We will have two subscriptions within ngIf and ngFor
@yurzui just to add ref to the docs: angular.io/api/common/NgIf . Part with 'storing conditional result in a variable'

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.