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

148 views Asked by At

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();
  }


}
3

There are 3 answers

2
Ostn On BEST ANSWER

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);
  }

}
0
Reactive_learner On

As @Noelmout mention using pipe I was able to get the customer_name with a little bit of change.

This is the CustomerNamePipe

import { Pipe, PipeTransform } from '@angular/core';
import { CustomerService } from '../services/customer.service';
import { Customer } from '../models/customer.model';
import { pluck } from 'rxjs/operators';

@Pipe({
  name: 'getCustomerName'
})
export class CustomerNamePipe implements PipeTransform {

  customer: Customer;

  constructor(private customerService: CustomerService) { }

  transform(curstomerId, args) {
    return this.customerService.getCustomerById(curstomerId).pipe(pluck('customer_name'));

  }


}

This is the order.page.html

<ion-col> 
    {{ order.customer_id | getCustomerName | async }} 
</ion-col>
0
Muhammad Umair On

Again not a great solution but given the circumstances as you cant change anything in the API. You can modify your file to this.

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.getAllCustomers();
      });

    this.getAllOrders();
    this.getAllCustomers();

  }

  getAllOrders() {

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

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

      });
  }

  getAllCustomers() {

    this.customerService.getAllCustomers().subscribe(
      (customers: Customer[]) => {
        this.customers = customers;
      }
      (error) => {
        this.error = error;

      });
  }

  getCustomerById(customerId: number): string {
    const customer = this.customers.filter(customer => customer.customer_id === customerId );
    return customer.customer_name;
  }

}