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: "[email protected]", image: "/profile/image/foobar/foobar.jpg", phoneNumber: "99999999"},
1: {fullName: "Foo Bar1", emailAddress: "[email protected]", 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
from rxjs
and emit the profiles from the response. In your template make use of the async
pipe. This would make sure that angular's change detection would pick up the changes.
private readonly profiles$$ = new BehaviorSubject([]);
public readonly profiles$ = this.profiles$$.asObservable();
// ...
search(term: string) {
const apiUrl = `${this.apiRoot}?search=${term}&rows=10&callback=JSONP_CALLBACK`;
return this.jsonp.request(apiUrl).map(res => {
let results = res.json();
this.profiles$$.next(results.profiles);
return results;
});
}
and in your template:
<li *ngFor="let profile of profiles$ | async">
<!-- ... -->
</li>
Btw: Remove the @Injectable
decorator from the component, components shouldn't be injectable
.
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.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With