Tonmoy
Tonmoy

Reputation: 2038

Angular interpolation lags rendering some values & how to remove one item from the array of typescript objects

I am working on an ASP.NET Core - Angular web app where I can choose a customer as the origin and find distance & duration to others using Google maps distance matrix service. I am getting the values fine (I think console log proves that the lag is not because of google maps API calls/accessing values from an array of objects) but when I am binding data to the template using angular interpolation, it lags to render the distance & duration values. Also to mention, the table only displays once the button is clicked - when isFindButtonClicked becomes true.

  1. Can you help me find the cause and fix it?

enter image description here

customers: Array<Customer> = [];
nearbyCustomers: Array<Customer> = [];

getCustomers() {
        this.customerService.getAll()
            .subscribe( result => {
                this.customers = result;
            }, error => {
                this.alertService.error(error._body);
                this.router.navigate(['/home']);
            });
}

ngOnInit() {
        this.getCustomers();
}

Typescript function to find and assign distance/duration values:

findNearbyCustomers(selectedId: number) {
    this.isFindButtonClicked = true;
    this.nearbyCustomers = this.customers;
    var origin = this.customers.find(c => c.id == selectedId);
    var originLatLng = origin.latitude.toString()+","+origin.longitude.toString();
    var service = new google.maps.DistanceMatrixService();

    for (let x = 0; x < this.customers.length; x++) {
            console.log(this.customers[x].id);
            var destinationLatLng = this.customers[x].latitude.toString()+","+this.customers[x].longitude.toString();

            service.getDistanceMatrix({
                origins: [originLatLng],
                destinations: [destinationLatLng],
                travelMode: google.maps.TravelMode.DRIVING
            }, (response, status) => {
                if (status.toString() == 'OK') {
                    console.log('Distance: '+response.rows[0].elements[0].distance.text+', Duration: '+response.rows[0].elements[0].duration.text);
                    this.nearbyCustomers[x].distance = response.rows[0].elements[0].distance.text;
                    this.nearbyCustomers[x].duration = response.rows[0].elements[0].duration.text;
                    console.log('DURATION: '+this.nearbyCustomers[x].duration+', DISTANCE:'+this.nearbyCustomers[x].distance);
                }
            })
    }
}

Partial markups from angular template:

<tbody>
       <tr *ngFor="let nearbyCustomer of nearbyCustomers">
            <td>{{ nearbyCustomer.businessName }}</td>
            <td>
                <p>{{ nearbyCustomer.streetNumber + ' ' + nearbyCustomer.streetName + ', ' + nearbyCustomer.suburb }}</p>
                <p>{{ nearbyCustomer.city + ' ' + nearbyCustomer.postCode + ', ' + nearbyCustomer.country }}</p>
            </td>
            <td>{{ nearbyCustomer.contactPerson }}</td>
            <td>{{ nearbyCustomer.contactNumber }}</td>
            <td>{{ nearbyCustomer.email }}</td>
            <td>{{ nearbyCustomer.distance }}</td>
            <td>{{ nearbyCustomer.duration }}</td>
      </tr>
 </tbody>
  1. Also Previously, I've tried with an if statement if (this.customers[x].id != this.selectedDeliveryDestinationId) and only pushed customers excluding the selected one (as origin) this.nearbyCustomers.push(this.customers[x]); and then when I tried to assign a value to object property & render it gave me Cannot set property 'distance' of undefined errors - seems I was messing with the index values. What should be the best way to exclude one item from the array for this purpose? Or, if I just can hide one specific row from HTML that would work as well.

Thanks.

Upvotes: 2

Views: 425

Answers (3)

Tonmoy
Tonmoy

Reputation: 2038

Thanks for your answers. NgZone helped with the change detection lagging issue. And for the second part of my question, here is how I fixed it (Not sure if this is the best solution but works fine for me)

I've filtered the customers and only assigned values except the selected one like this: this.nearbyCustomers = this.customers.filter(c => c.id != this.selectedDeliveryDestinationId);. This way, I only had to loop through the array of nearbyCustomers excluding the origin -saving one API call waste in each loop :-)

findNearbyCustomers() {
        this.isFindButtonClicked = true;
        this.nearbyCustomers = this.customers.filter(c => c.id != this.selectedDeliveryDestinationId);
        var origin = this.customers.find(c => c.id == this.selectedDeliveryDestinationId);
        var originLatLng = origin.latitude.toString()+","+origin.longitude.toString();
        var service = new google.maps.DistanceMatrixService();
        for (let x = 0; x < this.nearbyCustomers.length; x++) {
            var destinationLatLng = this.nearbyCustomers[x].latitude.toString()+","+this.nearbyCustomers[x].longitude.toString();        
            service.getDistanceMatrix({
                origins: [originLatLng],
                destinations: [destinationLatLng],
                travelMode: google.maps.TravelMode.DRIVING
            }, (response, status) => {
                if (status.toString() == 'OK') {
                    this._ngZone.run(() => {
                        this.nearbyCustomers[x].distance = response.rows[0].elements[0].distance.text;
                        this.nearbyCustomers[x].duration = response.rows[0].elements[0].duration.text;
                    })
                }
            })
        }
    }

Upvotes: 2

user6749601
user6749601

Reputation:

I'd suggest to use a Pipe to filter the currently selected nearbyCustomer.

<tbody>
    <tr *ngFor="let nearbyCustomer of nearbyCustomers | nearbyCustomerFilter">
        <td>{{ nearbyCustomer.businessName }}</td>
        <td>
            <p>{{ nearbyCustomer.streetNumber + ' ' + nearbyCustomer.streetName + ', ' + nearbyCustomer.suburb }}</p>
            <p>{{ nearbyCustomer.city + ' ' + nearbyCustomer.postCode + ', ' + nearbyCustomer.country }}</p>
        </td>
        <td>{{ nearbyCustomer.contactPerson }}</td>
        <td>{{ nearbyCustomer.contactNumber }}</td>
        <td>{{ nearbyCustomer.email }}</td>
        <td>{{ nearbyCustomer.distance }}</td>
        <td>{{ nearbyCustomer.duration }}</td>
    </tr>
</tbody>

Create a Service which provides the information about the currently selected nearbyCustomer to the Pipe.

Then, whenever you choose another customer, your List gets updated an the Pipe kicks this particular entry out of the current view.

Upvotes: 1

Saso
Saso

Reputation: 148

You need ngZone in this case to update your table.

import { NgZone } from '@angular/core';

in constructor add: private _ngZone: NgZone then in your getDistanceMatrix success:

this._ngZone.run(() => {
                this.nearbyCustomers[x].distance = response.rows[0].elements[0].distance.text;
                    this.nearbyCustomers[x].duration = response.rows[0].elements[0].duration.text;
            });

About the second question I'll check it later and edit the answer

Upvotes: 3

Related Questions