I'm making an API call and storing a bunch of user profiles, and I want to be able to dynamically create cards (Angular Material Design md-card) for each profile. The number of profiles returned can vary, so this needs to be dynamic.
This is my component file which makes the JSONP request and stores the profiles in the profiles
variable:
import {Component, Injectable, OnInit} from '@angular/core';
import {Jsonp} from '@angular/http';
@Component({
selector: 'app-staff',
templateUrl: './staff.component.html',
styleUrls: ['./staff.component.css']
})
@Injectable()
export class StaffComponent implements OnInit {
public searchField: string;
apiRoot = 'this/is/my/api/url';
public results: JSON;
public profiles;
constructor(private jsonp: Jsonp) {
}
ngOnInit() {
}
setSearchField(field: string){ // ignore this method
this.searchField = field;
console.log('Received field: ' + this.searchField);
}
search(term: string) {
const apiUrl = `${this.apiRoot}?search=${term}&rows=10&callback=JSONP_CALLBACK`;
return this.jsonp.request(apiUrl).map(results => { this.results = results.json(); console.log(this.results['profiles'][0]); this.profiles = results['profiles']; return results.json(); });
}
}
This is the template for the above component, where I'm trying to use *ngFor
to create a list of md-card
:
<div class="container-fluid">
<div class="row justify-content-center">
<div class="col-md-auto">
<ul>
<li *ngFor="let profile of profiles">
<md-card class="example-card">
<md-card-header>
<div md-card-avatar class="example-header-image"></div>
<md-card-title>{{profile.fullName}}</md-card-title>
<md-card-subtitle>Department</md-card-subtitle>
</md-card-header>
<img md-card-image src="../assets/image.png">
<md-card-content>
<p>
This section of the card will contain information about the result being searched for. It could also be
accompanied by additional information such as links.
</p>
</md-card-content>
<md-card-actions>
<button md-button>APPLY</button>
<button md-button>GO TO xyz</button>
</md-card-actions>
</md-card>
</li>
</ul>
</div>
</div>
</div>
My profiles data is in the form of an array (assume that the array doesn't exceed a length of 10) and takes the following form:
0: {fullName: "Foo Bar", emailAddress: "foobar@foobar.com", image: "/profile/image/foobar/foobar.jpg", phoneNumber: "99999999"},
1: {fullName: "Foo Bar1", emailAddress: "foobar1@foobar.com", image: "/profile/image/foobar1/foobar1.jpg", phoneNumber: "919999999"}
However, there are no md-card
s being rendered. I have checked that profiles
isn't empty. How do I dynamically create cards based on the number of profiles and populate the content with values from the profile objects?
You could try to use a
BehaviorSubject
fromrxjs
and emit the profiles from the response. In your template make use of theasync
pipe. This would make sure that angular's change detection would pick up the changes.and in your template:
Btw: Remove the
@Injectable
decorator from the component, components shouldn't beinjectable
.Note: You should really consider moving the api call into a shared service, this would keep your component's logic clean and you could use it in other components too. Also you could inform yourself about the
redux
"pattern", for this you can look into@ngrx/store
and@ngrx/effects
. More information can be found at the @ngrx/platform monorepo. It will give you huge control about your application's state and it's easier to manage your data and control requests/responses from an external APIs.Your result is processed into a map() and still returns
Observable|promise
So you need to handle theasync
behavior of the value coming from the server.Angular provide a
async
pipe you can use in your template to say that when ever the data will come reflect it in my UI.you can move your server call to a
@Injectable()
service which will return Observable and store that in your component variable and then in your UI you can simple useasync
pipeThis works perfectly with Observable and http calls in angular api. when your angular loads, it finishes the template binding but the data from the server comes after that that is why your data does not reflect in UI. so you have to handle that async behavior. hope this helps :)