Kamran Pervaiz Kamran Pervaiz - 3 months ago 41
TypeScript Question

angular 2 wait for synchronous method

I have 2 dropdowns and I populate 2nd dropdown based on the value of dropdown 1. I have scenario to Edit and populate the dropdown values based on the grid.

enter image description here

private populateContacts(relationship: Relationship) {
this.getContacts(relationship.businessAreaId);
this.selectedContact = this.contacts.find(contact => contact.id === relationship.contacts[0].id);
}

private getContacts(businessAreaObjorId) {
if (businessAreaObjorId === NaN)
businessAreaObjorId = businessAreaObjorId.id;
this.businessAreaService.getContacts(businessAreaObjorId)
.subscribe(
contacts => this.contacts = contacts,
error => this.errorMessage = error);
}


Html is below

<tr *ngFor="let relationship of relationships">
<td>{{relationship.supplierName}}</td>
<td>{{relationship.businessArea}}</td>
<td>{{relationship.contacts[0].name}}</td>
<td><a href="javascript:void(0)" (click)="onEdit(relationship)">Edit</a></td>
</tr>
<tr class="active">
<td>
<select class="form-control" name="supplier" required
[(ngModel)]="selectedSupplier" #supplier="ngModel">
<option *ngFor="let supplier of suppliers" [ngValue]="supplier">{{supplier.name}}</option>
</select>
<div [hidden]="supplier.valid || supplier.pristine" class="alert alert-danger">
Supplier is required
</div>
</td>
<td>
<select class="form-control" name="businessArea" required
[(ngModel)]="selectedBusinessArea" #businessArea="ngModel" (ngModelChange)="getContacts($event)">
<option *ngFor="let businessArea of businessAreas" [ngValue]="businessArea">{{businessArea.name}}</option>
</select>
<div [hidden]="businessArea.valid || businessArea.pristine" class="alert alert-danger">
Business Area is required
</div>
</td>
<td>
<select class="form-control" name="contact" required
[(ngModel)]="selectedContact" #contact="ngModel">
<option *ngFor="let contact of contacts" [ngValue]="contact">{{contact.name}}</option>
</select>
<div [hidden]="contact.valid || contact.pristine" class="alert alert-danger">
Contact is required
</div>
</td>
<td>
<input type="button" value="Add" class="btn btn-default" (click)="onSubmit()" [disabled]="!factoryRelationshipForm.form.valid" />
</td>
</tr>


When I click on Edit link onEdit method is called and selects/assigns the values but fails on selction of contacts.

private onEdit(relationship: Relationship): void {
relationship.inEditMode = true;
this.selectedSupplier = this.suppliers.find(supplier => supplier.id === relationship.supplierId);
this.selectedBusinessArea = this.businessAreas
.find(businessArea => businessArea.id === relationship.businessAreaId);
this.populateContacts(relationship);
}

private populateContacts(relationship: Relationship) {
this.getContacts(relationship.businessAreaId);
this.selectedContact = this.contacts.find(contact => contact.id === relationship.contacts[0].id);
}


It fails on the line when i am trying to find contact in contacts array.

enter image description here

I assume the reason is service.getcontacts is http call and it takes time and when i am trying to find the contact object in this.contacts array which is undefined.

Is there a way to wait for getContacts in populateContacts method?

Edit 1:

my new code looks like this

private populateContacts(relationship: Relationship) {
//TODO: find a way to use this.contacts/getContacts
this.businessAreaService.getContacts(relationship.businessAreaId)
.subscribe(val => {
console.log(val);
this.contacts = val;
this.selectedContact = this.contacts.find(contact => contact.id === relationship.contacts[0].id)
});
}

private getContacts(businessAreaObj) {
this.businessAreaService.getContacts(businessAreaObj.id)
.subscribe(
contacts => this.contacts = contacts,
error => this.errorMessage = error);
}


Edit 2:

@Gunter' solution works for Edit case but if i manually select dropdown value on BA it does not load contacts in contacts dropdown.

enter image description here

Answer

You need to return the Observable. For this you must not call subscribe()

private getContacts(businessAreaObjorId) {
    if (businessAreaObjorId === NaN)
        businessAreaObjorId = businessAreaObjorId.id;
    return this.businessAreaService.getContacts(businessAreaObjorId)
        .catch(error => this.errorMessage = error)
        .map(
        contacts => this.contacts = contacts);

    }
}

then you can call it, get an Observable you can subscribe to, and in the callback you call the follow-up code:

private populateContacts(relationship: Relationship) {
    this.getContacts(relationship.businessAreaId)
    .subscribe(val => this.selectedContact = this.contacts.find(contact => contact.id === relationship.contacts[0].id));
}