angulartypescriptionic4angular-template-variable

How to refer a funtion as a template variable in Ionic Angular app?


I have an Order object and a Customer Object. The JSON payload for the Order object is like the following:

{
  "order_number" : 1,
  "customer_id": 1
}

And this is the JSON payload for the Customer object

{
  "customer_id": 1,
  "customer_name" : 1,
}

I have Orders page where I want to display the list of orders. But instead of order.customer_id it was to display the customer_name

For the I have getCustomerById which takes the customer_id as a parameter and returns the customer_name.

This is my OrdersPage class:

import { Component, OnInit } from '@angular/core';
import { OrderService } from '../../services/order.service';
import { Order } from '../../models/order.model';
import { NavController, LoadingController } from '@ionic/angular';
import { Router } from '@angular/router';
import { Subscription } from 'rxjs';
import { CustomerService } from 'src/app/services/customer.service';
import { Customer } from 'src/app/models/customer.model';

@Component({
  selector: 'app-orders',
  templateUrl: './orders.page.html',
  styleUrls: ['./orders.page.scss'],
})
export class OrdersPage implements OnInit {
  sender;
  customerName: string;
  destinationName: string;
  // viewOrders = false;
  error;
  orders: Order[];
  subscription: Subscription;
  constructor(private orderService: OrderService,
              private navCtrl: NavController,
              private router: Router,
              private customerService: CustomerService
            ) { }

  ngOnInit() {
    this.orderService.refreshNeeded
      .subscribe(() => {
        this.getAllOrders();
      });
    this.getAllOrders();

  }

  getAllOrders() {

    this.orderService.getAllOrders().subscribe(
      (res: Order[]) => {
        this.orders = res;

      },
      (error) => {
        this.error = error;

      });
  }

  getCustomerById(customerId: number): string {

    this.customerService.getCustomerById(customerId).subscribe(
      (customer: Customer) => {
        this.customerName = customer.name;
      }
    );
    return this.customerName;
  }

}

This is orders.page.html

<ion-header>
  <ion-toolbar color="dark">
    <ion-button slot="end">
      <ion-menu-button> </ion-menu-button>
    </ion-button>
    <ion-title>Orders</ion-title>
  </ion-toolbar>
</ion-header>

<ion-content>
  <ion-row>
    <ion-col size-md="8" offset-md="2">
      <ion-row class="header-row ion-text-center">
        <ion-col>
          Order number
        </ion-col>
        <ion-col>
          Customer
        </ion-col>
      </ion-row>
      <ion-row *ngFor="let order of orders; let i = index" class="data-row ion-text-center">
        <ion-col>
          {{order.order_number}}
        </ion-col>
        <ion-col>
          {{order.customer_id}}
        </ion-col>

        <!-- <ion-col>
        {{getCustomerById(order?.customer_id)}}
      </ion-col> -->
      </ion-row>
    </ion-col>
  </ion-row>
</ion-content>

This html works but it returns the order.customer_id not the customer_name I have tried to get the name by calling the funtion in the template this way {{getCustomerById(order?.customer_id)}} doesn't work and no error in the console as well.

What is the best way to get the customer_name field in the order list?

This is my customer.service.ts

import { Injectable } from '@angular/core';
import { HttpClient } from '@angular/common/http';
import { Observable, Subject } from 'rxjs';
import { Customer } from '../models/customer.model';
import { catchError, tap } from 'rxjs/operators';

@Injectable({
  providedIn: 'root'
})
export class CustomerService {
  url = 'http://api.mydomain.com';

  constructor( ) { }

  getAllCustomers(): Observable<Customer[]> {
    return this.httpClient.get<Customer[]>(`${this.url}/customers`).pipe();
  }

  getCustomerById(id: number): Observable<Customer> {
    return this.httpClient.get<Customer>(`${this.url}/customer/${id}`).pipe();
  }


}

Solution

  • As mentionned by @Muhammad Umair it is not a good design to make a request to a server for each customer name. Best is to make one request to fetch all wanted curstomer names. The solution below do not take this into consideration.

    Best here is to use a pipe.

    "A pipe takes in data as input and transforms it to a desired output." Angular doc

    Notice that your request to get your curstomer name is asynchronous (this is why nothing is displayed in your template), here you need to use the async pipe as well :

    <ion-col> 
        {{ order.customer_id | getCustomerName | async }} 
    </ion-col>
    

    And here is the pipe (that you should insert into your declarations in your component's module.

    import { Pipe } from '@angular/core';
    
    @Pipe({
      name: 'getCustomerName'
    })
    export class CustomerNamePipe {
    
      constructor(private customerService: CustomerService) { }
    
      transform(userIds, args) {
         return this.customerService.getCustomerById(curstomerId);
      }
    
    }