Blog on Enterprise Java with Jakarta EE and Web Development

This blog post is about creating a CRUD application by exposing a RESTful web service API using JAX-RS (Jakarta API for Restful Service) from a MySQL Database and consuming this endpoint using the ionic/angular framework on the front end.

Pre-requisite for this tutorial

Create the table in the MySQL database and populate the table with some data with the following code

CREATE  TABLE IF NOT EXISTS customers (
   id INT UNSIGNED NOT NULL AUTO_INCREMENT ,
   fullname VARCHAR(45) NOT NULL ,
   email VARCHAR(45) NOT NULL ,
   address VARCHAR(245) NOT NULL ,
   city VARCHAR(45) NOT NULL ,
   course VARCHAR(245) NOT NULL ,
   PRIMARY KEY (id) )
 ENGINE = InnoDB
 COMMENT = 'maintains customer details';
 INSERT INTO customers (fullname, email, address, city, course)
 VALUES ('Java DukeMan', 'duke@java.com', 'Sun Microsystems', 'California', 'Java Enterprise for Beginners');

Netbeans IDE is used for this tutorial and gives a lot of support for database and Jakarta EE applications. To get started with the project, create a New project in Netbeans > Java with Maven and select Payara Micro Application and name it customerApp.

Data Source Configuration

Create a web.xml deployment descriptor for the project. To do this in Netbeans, right click the project > New Other > and from the categories section select Web > Standard Deployment Descriptor (web.xml) and click Finish.

This generates a web.xml file in a new Folder/Directory called WEB-INF. Once the web.xml deployment descriptor has been created, add the following to configure the data source.

<data-source>
        <name>java:app/jdbc/CustomerDataSource</name>
        <class-name>com.mysql.jdbc.jdbc2.optional.MysqlXADataSource</class-name>
        <server-name>localhost</server-name>
        <port-number>3306</port-number>
        <database-name>customer?autoReconnect=true&useSSL=false</database-name>
        <user>root</user>
        <password>password</password>
        <!-- Example of how to use a Payara specific custom connection pool setting -->
        <property>
            <name>fish.payara.sql-trace-listeners</name>
            <value>com.sun.gjc.util.SQLTraceLogger</value>
        </property>
    </data-source>

Next create a new folder called lib in the WEB-INF directory and then add the MySQL JDBC jar file into the WEB-INF/lib. The jar file can be downloaded from here https://dev.mysql.com/downloads/connector/j/

Next, create a persistence unit (persistence.xml) for the project. In the Netbeans IDE, right click the project and choose New > Persistence > Persistence Unit to create persistence.xml. This process creates a persistence.xml file inside a src/main/resources/META-INF folder.

The content of the persistence.xml is listed below

<?xml version="1.0" encoding="UTF-8"?>
<persistence version="2.1" xmlns="http://xmlns.jcp.org/xml/ns/persistence" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/persistence http://xmlns.jcp.org/xml/ns/persistence/persistence_2_1.xsd">
  <persistence-unit name="customerAppPU" transaction-type="JTA">
      <jta-data-source>java:app/jdbc/CustomerDataSource</jta-data-source>
    <exclude-unlisted-classes>false</exclude-unlisted-classes>
    <properties/>
  </persistence-unit>
</persistence>

Next, create the Customer Entity/Model utilizing the Netbeans create Entity classes from Database feature. To do this, right click the project, choose New > Other > Persistence > Entity Classes from Database as shown in the figure below

The generated Customers.java file looks like this:

package omos.microsystems.customerapp;

import java.io.Serializable;
import javax.persistence.Basic;
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.NamedQueries;
import javax.persistence.NamedQuery;
import javax.persistence.Table;
import javax.validation.constraints.NotNull;
import javax.validation.constraints.Size;
import javax.xml.bind.annotation.XmlRootElement;

/**
 *
 * @author omozegieaziegbe
 */
@Entity
@Table(name = "customers", catalog = "customer", schema = "")
@XmlRootElement
@NamedQueries({
    @NamedQuery(name = "Customers.findAll", query = "SELECT c FROM Customers c")
    , @NamedQuery(name = "Customers.findById", query = "SELECT c FROM Customers c WHERE c.id = :id")
    , @NamedQuery(name = "Customers.findByFullname", query = "SELECT c FROM Customers c WHERE c.fullname = :fullname")
    , @NamedQuery(name = "Customers.findByEmail", query = "SELECT c FROM Customers c WHERE c.email = :email")
    , @NamedQuery(name = "Customers.findByAddress", query = "SELECT c FROM Customers c WHERE c.address = :address")
    , @NamedQuery(name = "Customers.findByCity", query = "SELECT c FROM Customers c WHERE c.city = :city")
    , @NamedQuery(name = "Customers.findByCourse", query = "SELECT c FROM Customers c WHERE c.course = :course")})
public class Customers implements Serializable {

    private static final long serialVersionUID = 1L;
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    @Basic(optional = false)
    @Column(name = "id", nullable = false)
    private Integer id;
    @Basic(optional = false)
    @NotNull
    @Size(min = 1, max = 45)
    @Column(name = "fullname", nullable = false, length = 45)
    private String fullname;
    // @Pattern(regexp="[a-z0-9!#$%&'*+/=?^_`{|}~-]+(?:\\.[a-z0-9!#$%&'*+/=?^_`{|}~-]+)*@(?:[a-z0-9](?:[a-z0-9-]*[a-z0-9])?\\.)+[a-z0-9](?:[a-z0-9-]*[a-z0-9])?", message="Invalid email")//if the field contains email address consider using this annotation to enforce field validation
    @Basic(optional = false)
    @NotNull
    @Size(min = 1, max = 45)
    @Column(name = "email", nullable = false, length = 45)
    private String email;
    @Basic(optional = false)
    @NotNull
    @Size(min = 1, max = 245)
    @Column(name = "address", nullable = false, length = 245)
    private String address;
    @Basic(optional = false)
    @NotNull
    @Size(min = 1, max = 45)
    @Column(name = "city", nullable = false, length = 45)
    private String city;
    @Basic(optional = false)
    @NotNull
    @Size(min = 1, max = 245)
    @Column(name = "course", nullable = false, length = 245)
    private String course;

    public Customers() {
    }

    public Customers(Integer id) {
        this.id = id;
    }

    public Customers(Integer id, String fullname, String email, String address, String city, String course) {
        this.id = id;
        this.fullname = fullname;
        this.email = email;
        this.address = address;
        this.city = city;
        this.course = course;
    }

    public Integer getId() {
        return id;
    }

    public void setId(Integer id) {
        this.id = id;
    }

    public String getFullname() {
        return fullname;
    }

    public void setFullname(String fullname) {
        this.fullname = fullname;
    }

    public String getEmail() {
        return email;
    }

    public void setEmail(String email) {
        this.email = email;
    }

    public String getAddress() {
        return address;
    }

    public void setAddress(String address) {
        this.address = address;
    }

    public String getCity() {
        return city;
    }

    public void setCity(String city) {
        this.city = city;
    }

    public String getCourse() {
        return course;
    }

    public void setCourse(String course) {
        this.course = course;
    }

    @Override
    public int hashCode() {
        int hash = 0;
        hash += (id != null ? id.hashCode() : 0);
        return hash;
    }

    @Override
    public boolean equals(Object object) {
        // TODO: Warning - this method won't work in the case the id fields are not set
        if (!(object instanceof Customers)) {
            return false;
        }
        Customers other = (Customers) object;
        if ((this.id == null && other.id != null) || (this.id != null && !this.id.equals(other.id))) {
            return false;
        }
        return true;
    }

    @Override
    public String toString() {
        return "omos.microsystems.customerapp.Customers[ id=" + id + " ]";
    }
    
}

Next, create the business logic for the application. Create a CustomerService.java file which handles the CRUD (Create, Read, Update, Delete) service for the application. The code for the CustomerService.java file is shown below:


package omos.microsystems.customerapp;

import java.util.List;
import javax.enterprise.context.ApplicationScoped;
import javax.persistence.EntityManager;
import javax.persistence.PersistenceContext;
import javax.transaction.Transactional;

/**
 *
 * @author omozegieaziegbe
 */
@ApplicationScoped
public class CustomerService {

    @PersistenceContext(unitName = "customerAppPU")
    EntityManager em;

    public List getAll() {
        return em.createNamedQuery("Customers.findAll", Customers.class).getResultList();
    }

    public Customers findById(Integer id) {
        return em.find(Customers.class, id);
    }

    @Transactional
    public void update(Customers customer) {
        em.merge(customer);
    }

    @Transactional
    public void create(Customers customer) {
        em.persist(customer);
    }

    @Transactional
    public void delete(Customers customer) {
        if (!em.contains(customer)) {
            customer = em.merge(customer);
        }
        em.remove(customer);
    }
}

Next, create an ApplicationConfig java class which extends the Application class. The content of the ApplicationConfig class is shown below:


package omos.microsystems.customerapp;

import java.util.Set;
import javax.ws.rs.core.Application;

/**
 *
 * @author omozegieaziegbe
 */
@javax.ws.rs.ApplicationPath("webresources")
public class ApplicationConfig extends Application {

    @Override
    public Set<Class<?>> getClasses() {
        Set<Class<?>> resources = new java.util.HashSet<>();
        addRestResourceClasses(resources);
        return resources;
    }

    /**
     * Do not modify addRestResourceClasses() method.
     * It is automatically populated with
     * all resources defined in the project.
     * If required, comment out calling this method in getClasses().
     */
    private void addRestResourceClasses(Set<Class<?>> resources) {
        resources.add(omos.microsystems.customerapp.CustomerEndpoint.class);
    }
    
}

Next, create the CustomerEndpoint.java file which defines the URI endpoints for the Customers. Expose the REST API’s with the HTTP GET, POST, PUT and DELETE Methods. The code for the CustomerEndpoint class looks like this:


package omos.microsystems.customerapp;

import javax.enterprise.context.RequestScoped;
import javax.inject.Inject;
import javax.ws.rs.Consumes;
import javax.ws.rs.DELETE;
import javax.ws.rs.GET;
import javax.ws.rs.POST;
import javax.ws.rs.PUT;
import javax.ws.rs.Path;
import javax.ws.rs.PathParam;
import javax.ws.rs.Produces;
import javax.ws.rs.core.MediaType;
import javax.ws.rs.core.Response;

/**
 *
 * @author omozegieaziegbe
 */
@RequestScoped
@Path("customers")
@Produces({MediaType.APPLICATION_JSON,MediaType.APPLICATION_XML})
@Consumes({MediaType.APPLICATION_JSON,MediaType.APPLICATION_XML})
public class CustomerEndpoint {

    @Inject
    CustomerService customerService;

    @GET
    public Response getAll() {
        return Response.ok(customerService.getAll()).build();
    }

    @GET
    @Path("{id}")
    public Response getCustomer(@PathParam("id") Integer id) {
        Customers customer = customerService.findById(id);
        return Response.ok(customer).build();
    }

    @POST
    public Response create(Customers customer) {
        customerService.create(customer);
        return Response.ok().build();
    }

    @PUT
    @Path("{id}")
    public Response update(@PathParam("id") Integer id, Customers customer) {
        Customers updateCustomer = customerService.findById(id);
        updateCustomer.setFullname(customer.getFullname());
        updateCustomer.setAddress(customer.getAddress());
        updateCustomer.setCourse(customer.getCourse());
        updateCustomer.setCity(customer.getCity());
        updateCustomer.setEmail(customer.getEmail());
        customerService.update(updateCustomer);
        return Response.ok().build();
    }

    @DELETE
    @Path("{id}")
    public Response delete(@PathParam("id") Integer id) {
        Customers getCustomer = customerService.findById(id);
        customerService.delete(getCustomer);
        return Response.ok().build();
    }
      

}

The Maven pom.xml file for the customerApp is as follows

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>

    <groupId>omos.microsystems</groupId>
    <artifactId>customerApp</artifactId>
    <version>1.0</version>
    <packaging>war</packaging>

    <name>customerApp</name>

    <properties>
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
        <maven.compiler.source>1.8</maven.compiler.source>
        <maven.compiler.target>1.8</maven.compiler.target>
        <failOnMissingWebXml>false</failOnMissingWebXml>
        <version.javaee>8.0</version.javaee>
        <version.jca>1.7</version.jca>
        <version.payara>5.192</version.payara>
        <version.microprofile>2.2</version.microprofile>
    </properties>
    
    <dependencies>
        <dependency>
            <groupId>org.eclipse.persistence</groupId>
            <artifactId>org.eclipse.persistence.core</artifactId>
            <version>2.5.2</version>
        </dependency>
        <dependency>
            <groupId>org.eclipse.persistence</groupId>
            <artifactId>org.eclipse.persistence.asm</artifactId>
            <version>2.5.2</version>
        </dependency>
        <dependency>
            <groupId>org.eclipse.persistence</groupId>
            <artifactId>org.eclipse.persistence.antlr</artifactId>
            <version>2.5.2</version>
        </dependency>
        <dependency>
            <groupId>org.eclipse.persistence</groupId>
            <artifactId>org.eclipse.persistence.jpa</artifactId>
            <version>2.5.2</version>
        </dependency>
        <dependency>
            <groupId>org.eclipse.persistence</groupId>
            <artifactId>org.eclipse.persistence.jpa.jpql</artifactId>
            <version>2.5.2</version>
        </dependency>
        <dependency>
            <groupId>org.eclipse.persistence</groupId>
            <artifactId>javax.persistence</artifactId>
            <version>2.1.0</version>
        </dependency>
        <dependency>
            <groupId>org.eclipse.persistence</groupId>
            <artifactId>org.eclipse.persistence.jpa.modelgen.processor</artifactId>
            <version>2.5.2</version>
            <scope>provided</scope>
        </dependency>
        <dependency>
            <groupId>javax</groupId>
            <artifactId>javaee-web-api</artifactId>
            <version>${version.javaee}</version>
            <scope>provided</scope>
        </dependency>
        <dependency>
            <groupId>javax.resource</groupId>
            <artifactId>javax.resource-api</artifactId>
            <version>${version.jca}</version>
            <scope>provided</scope>
        </dependency>
        <dependency>
            <groupId>org.eclipse.microprofile</groupId>
            <artifactId>microprofile</artifactId>
            <version>${version.microprofile}</version>
            <scope>provided</scope>
            <type>pom</type>
        </dependency>
    </dependencies>

    <build>
        <plugins>
            <plugin>
                <groupId>fish.payara.maven.plugins</groupId>
                <artifactId>payara-micro-maven-plugin</artifactId>
                <version>1.0.5</version>
                <configuration>
                    <payaraVersion>${version.payara}</payaraVersion>
                    <deployWar>false</deployWar>
                    <commandLineOptions>
                        <option>
                            <key>--autoBindHttp</key>
                        </option>
                        <option>
                            <key>--deploy</key>
                            <value>${project.build.directory}/${project.build.finalName}</value>
                        </option>
                    </commandLineOptions>
<!--                    <contextRoot>""</contextRoot>-->
                </configuration>
            </plugin>
        </plugins>
    </build>

</project>

To download the dependencies, build and run the project, Open a command prompt, navigate to the root of the project directory and type the following command

mvn clean install
mvn clean package payara-micro:start

This starts payara micro and deploys the application to payara micro using the Payara Micro Maven Plugin. The output on the command prompt looks like this

The customerApp REST Service Endpoint can now be accessed by visiting the following URL http://localhost:8080/customerApp-1.0/webresources/customers

Ionic 4/Angular Development on the Front End

The first step to ionic development is to have Node.js installed on your machine. Node.js can be installed from https://nodejs.org/en/

To Configure Netbeans to use Typescript, follow the instructions on https://blog.programster.org/configure-netbeans-with-typescript

To Configure Netbeans to use Ionic with the Ionic Netbeans Plugin, follow the instructions on https://github.com/hexaviewtech/ionic-netbeans-plugin

Next, install Ionic CLI from a terminal or command prompt with the following command

npm install -g cordova ionic

Next, navigate to the folder/directory where a new Ionic project is to be created and enter the following command:

ionic start customerFrontend blank --type=angular

Next, navigate to the the newly created customerFrontend folder and serve the ionic application with the following command

cd customerFrontend
ionic serve

This process will compile and run the customerFrontend ionic application and open up a browser which looks like this:

Next, import the customerFrontend project into Netbeans by clicking on New Project ->HTML5/JavaScript -> HTML5/JS Application with existing sources. Click Next and under Site Root browse to the location of the customerFrontend project. The screenshots looks like:

Below is a screenshot image of the Ionic Project Structure in Netbeans

Next, create the HTTPClient Service which will be used for handling http request to the backend customerApp payara micro application. Navigate to the root of the ionic project and enter the command

ionic g service service/customerservice

This generates a service/customerservice.service.ts file inside the src/app folder. The CustomerService service is used to get the data from the backend by calling the JAX-RS (RESTful Services) API. The Customers class is also defined here for working with the customers. The full code for the customerservice.service.ts file looks like this:

import { Injectable } from '@angular/core';
import { HttpClient, HttpHeaders } from "@angular/common/http";
import { Observable } from 'rxjs';


export class Customers{
  constructor(
    public id:number,
    public fullname:string,
    public email:string,
    public address:string,
    public city:string,
    public course:string,
  ) {}
}

@Injectable({
  providedIn: 'root'
})
export class CustomerserviceService {

  constructor(private http: HttpClient) { }
  
   getCustomers(): Observable<any> {
    return this.http.get('http://localhost:8080/customerApp-1.0/webresources/customers');
  }
  
  createCustomers(customers : Customers): Observable<Object>{
       let headers = new HttpHeaders({
            'Content-Type': 'application/json'
        });
        let options = {
            headers: headers
        }
    return this.http.post<Customers>('http://localhost:8080/customerApp-1.0/webresources/customers', customers, options);
  }
 
  
   deleteCustomers(id : number): Observable<any>{     
       return this.http.delete<Customers>('http://localhost:8080/customerApp-1.0/webresources/customers' + "/" + id);
  }
  
   updateCustomers(customer: Customers, id: any): Observable<Object>{
   return this.http.put<Customers>(`http://localhost:8080/customerApp-1.0/webresources/customers/${id}`, customer);
 }
  
  getCustomer(id:any): Observable<any> {
    return this.http.get(`http://localhost:8080/customerApp-1.0/webresources/customers/${id}`);
  }
  
}

Next, we import the Angular HttpClientModule which is used to make HTTP request to our RESTful backend API into our ionic project app/app.module.ts file. The full code listing for the edited app.module.ts looks like this :

import { NgModule } from '@angular/core';
import { BrowserModule } from '@angular/platform-browser';
import { RouteReuseStrategy } from '@angular/router';

import { IonicModule, IonicRouteStrategy } from '@ionic/angular';
import { SplashScreen } from '@ionic-native/splash-screen/ngx';
import { StatusBar } from '@ionic-native/status-bar/ngx';

import { AppComponent } from './app.component';
import { AppRoutingModule } from './app-routing.module';
import { HttpClientModule } from "@angular/common/http";

import {BrowserAnimationsModule} from '@angular/platform-browser/animations';



@NgModule({
  declarations: [AppComponent],
  entryComponents: [],
  imports: [BrowserModule, BrowserAnimationsModule, HttpClientModule, IonicModule.forRoot(), AppRoutingModule],
  providers: [
    StatusBar,
    SplashScreen,
    { provide: RouteReuseStrategy, useClass: IonicRouteStrategy }
  ],
  bootstrap: [AppComponent]
})
export class AppModule {}

Next, we inject the HTTPClient Service (CustomerserviceService) into the home.page.ts component. This service is used to make a call to the JAX-RS API Backend to get and display a list of customers. The methods to create a customer, update a customer and delete a customer is also added here.

import {CustomerserviceService, Customers} from '../service/customerservice.service';

The full code listing for home.page.ts looks like

import {Component, OnInit} from '@angular/core';
import {Observable} from "rxjs";
import {CustomerserviceService, Customers} from '../service/customerservice.service';
import {Router} from "@angular/router";
import {NgForm} from '@angular/forms';



@Component({
    selector: 'app-home',
    templateUrl: 'home.page.html',
    styleUrls: ['home.page.scss'],
})
export class HomePage {


    submitted = false;
    customers: Observable<Customers[]>;
    selectedCustomer: Customers = {id: null, fullname: null, email: null, address: null, city: null, course: null};



    constructor(private customerservice: CustomerserviceService, private router: Router) {}

    ngOnInit() {
        this.reloadData();
    }

    reloadData() {
        this.customers = this.customerservice.getCustomers();
    }

 newCustomer(): void {
    this.submitted = false;
    this.selectedCustomer = {id: null, fullname: null, email: null, address: null, city: null, course: null};
  }
  
    onSubmit(customer: Customers) {
        this.submitted = true;
        this.customerservice.createCustomers(customer)
            .subscribe((data) => {
                this.resetForm();
                console.log(data);
                this.reloadData();
            }, error => {
                console.log(error);
            });
    }


    deleteCustomer(id: number) {
        if (confirm('Are you sure you want to delete this customer?'))
            this.customerservice.deleteCustomers(id).subscribe(customer => {
                console.log(customer);
                this.reloadData();
            },
                error => console.log(error));
    }

    updateCustomer(customer: NgForm) {
        // let id = this.route.snapshot.paramMap.get('id');
        let id = this.selectedCustomer.id;
        this.customerservice.updateCustomers(customer.value, id)
            .subscribe((customers: Customers) => {
                console.log(customers), error => console.log(error)
            });
        this.resetForm();
        this.router.navigate(['/home']);
    }

    resetForm() {
        this.selectedCustomer = {id: null, fullname: null, email: null, address: null, city: null, course: null};
    }

    customerDetails(id: number) {
        this.router.navigate(['details', id]);
    }

    selectCustomer(customer: Customers) {
        this.selectedCustomer = customer;
    }

}

Next, we will be displaying a list of customers from the backend on the ionic app on the home.page.html using PrimeNG dataTable component. First add PrimeNG to our ionic app using the following command,

npm install primeng@7.0.0 --save
npm install primeicons@1.0.0 --save

Next, we update home.module.ts to import the PrimeNG components we want to use in our ionic app. The PrimeNG ButtonModule, TableModule and PanelModule is added to the home.module.ts. The full code listing looks like this

import { NgModule } from '@angular/core';
import { CommonModule } from '@angular/common';
import { IonicModule } from '@ionic/angular';
import { FormsModule } from '@angular/forms';
import { RouterModule } from '@angular/router';

import { HomePage } from './home.page';
import {ButtonModule} from 'primeng/button';
import {TableModule} from 'primeng/table';
import {PanelModule} from 'primeng/panel';


@NgModule({
  imports: [
    CommonModule,
    FormsModule,
    IonicModule,
    ButtonModule,
    PanelModule,
    TableModule,
    RouterModule.forChild([
      {
        path: '',
        component: HomePage
      }
    ])
  ],
  declarations: [HomePage]
})
export class HomePageModule {}

Next, update home.page.html with the following code which includes a PrimeNG dataTable to view a list of customers with buttons to delete customers and view customer details and a form to add and update customers. The full code listing for home.page.html looks like this

<ion-header>
    <ion-toolbar>
        <ion-buttons slot="start">
            <ion-menu-button></ion-menu-button>
        </ion-buttons>
        <ion-title>
            Home
        </ion-title>
    </ion-toolbar>
</ion-header>

<ion-content>
    <ion-item>
        <div  style="width:500px; margin: 0 auto">
  <ion-label class="ion-text-wrap" color="primary">
   Ionic/Angular with JAX-RS (Jakarta RESTFul Services) CRUD App
  </ion-label>
        </div>
</ion-item>
    <p-table [value]="customers | async" [style]="{'margin-top':'20px'}"  [rows]="10" [paginator]="true" [pageLinks]="3" [responsive]="true">
        <ng-template pTemplate="header">
            <tr>
                <!--                <th>id</th>-->
                <th>address</th>
                <th>email</th>
                <th>city</th>
                <th>full name</th>
                <th>course</th>
            </tr>
        </ng-template>
        <ng-template pTemplate="body" let-customer>
            <tr>
                <!--                <td>{{customer.id}}</td>-->
                <td>{{customer.address}}</td>
                <td>{{customer.email}}</td>
                <td>{{customer.city}}</td>
                <td>{{customer.fullname}}</td>
                <td>{{customer.course}}</td>

                <td><button pButton type="button" label="Customer Details" (click)="customerDetails(customer.id)"></button></td>
                <td><button pButton type="button" label="Delete Customer" (click)="deleteCustomer(customer.id)"></button></td>
                <td><button pButton type="button" label="Update Customer" (click)="selectCustomer(customer)"></button></td>
            </tr>
        </ng-template>
    </p-table>

    <div [hidden]="!submitted" style="width:500px; margin: 0 auto; font-size: 16px; color: green; margin-bottom: 20px; margin-top: 10px">
        <h6>Customer Details submitted successfully!</h6>
         <button class="btn btn-success" (click)="newCustomer()">Add New Customer</button> 
    </div>
    
    
    <div [hidden]="submitted" style="width:550px; margin: 0 auto">
        <p-panel header="Customer Form" [style]="{'margin-top':'20px', 'margin-bottom':'20px','width':'500px','margin-left':'auto','margin-right':'auto'}">

            <form #customerForm="ngForm" (ngSubmit)="onSubmit(customerForm.value)"><br/>

                <!--            <h5>Id</h5>
                            <span class="ui-float-label">
                            <input id="float-input" type="text" size="5" pInputText [(ngModel)]="selectedCustomer.id" [disabled]="disabled" name="id"> 
                              
                            </span>-->

                <h5>Address</h5>
                <span class="ui-float-label">
                    <input id="float-input" type="text" size="50" pInputText [(ngModel)]="selectedCustomer.address" name="address"> 

                </span>

                <h5>Email</h5>
                <span class="ui-float-label">
                    <input id="float-input" type="text" size="50" pInputText [(ngModel)]="selectedCustomer.email" name="email"> 

                </span>

                <h5>City</h5>
                <span class="ui-float-label">
                    <input id="float-input" type="text" size="50" pInputText [(ngModel)]="selectedCustomer.city" name="city"> 

                </span>

                <h5>Full Name</h5>
                <span class="ui-float-label">
                    <input id="float-input" type="text" size="50" pInputText [(ngModel)]="selectedCustomer.fullname"  name="fullname"> 

                </span>

                <h5>Course</h5>
                <span class="ui-float-label">
                    <input id="float-input" type="text" size="50" pInputText [(ngModel)]="selectedCustomer.course" name="course"> 

                </span>


                <button pButton type="submit" label="Save" style="margin-top: 1em"></button>
                <button pButton type="button" label="Update Customer" style="margin-left: 1em" (click)="updateCustomer(customerForm)"> </button>
                <button pButton type="button" label="Clear Form" style="margin-left: 1em" (click)="resetForm()" routerLink="/"> </button>
            </form>
        </p-panel>
    </div>



</ion-content>

Next, create the details page used to view a particular customer details with the following ionic command

ionic g page customer-details

Next, update the customer-details.page.ts by adding the following code to it

import { Component, OnInit } from '@angular/core';
import { Router, ActivatedRoute } from "@angular/router";
import {CustomerserviceService} from '../service/customerservice.service';

@Component({
  selector: 'app-customer-details',
  templateUrl: './customer-details.page.html',
  styleUrls: ['./customer-details.page.scss'],
})
export class CustomerDetailsPage implements OnInit {

customer: any;

  constructor(private customerservice: CustomerserviceService,private route: ActivatedRoute,private router: Router,) { }

  ngOnInit() {
            let id = this.route.snapshot.paramMap.get('id');
      this.customerservice.getCustomer(id).subscribe(res => {
      this.customer = res;
    });
  }

list(){
    this.router.navigate(['home']);
  }
}

And update the customer-details.page.html which is used to display the details of the selected customer by adding the following code listing


<ion-header>
  <ion-toolbar color="primary">
    <ion-buttons slot="start">
      <ion-back-button defaultHref="/"></ion-back-button>
    </ion-buttons>
    <ion-title>{{ customer?.fullname }}'s Details</ion-title>
  </ion-toolbar>
</ion-header>

<ion-content>
<ion-card *ngIf="customer">
    <ion-card-content>
        Full Name: {{ customer.fullname }}<br><br>
     Email: {{ customer.email }}<br><br>
      Address: {{ customer.address }}<br><br>
      City: {{ customer.city }}<br><br>
      Course: {{ customer.course }}
    </ion-card-content>
    
    
  </ion-card>
    <button (click)="list()" class="btn btn-primary" style="margin-left: 9px">Back to Customer List</button><br><br><br><br>
</ion-content>

Next, we configure the routing for the Ionic/Angular app in the app-routing.module.ts. The complete code listing for the app-routing.module.ts is shown below

import { NgModule } from '@angular/core';
import { PreloadAllModules, RouterModule, Routes } from '@angular/router';

const routes: Routes = [
  { path: '', redirectTo: 'home', pathMatch: 'full' },
  { path: 'home', loadChildren: () => import('./home/home.module').then( m => m.HomePageModule)},
  { path: 'customer-details', loadChildren: './customer-details/customer-details.module#CustomerDetailsPageModule' },
  {
    path: "details/:id",
    loadChildren: "./customer-details/customer-details.module#CustomerDetailsPageModule"
  },
];

@NgModule({
  imports: [
    RouterModule.forRoot(routes, { preloadingStrategy: PreloadAllModules })
  ],
  exports: [RouterModule]
})
export class AppRoutingModule { }

Running the Ionic/Angular Front End Application

To run the ionic/angular frontend app, ensure that the JAX-RS API backend is running and enter the following command to run the ionic app

ionic serve

By default, ionic applications run on port 8100 and runs the browser automatically on http://localhost:8100/home after running the ionic serve command. Go to the URL http://localhost:8100/home to visit the app. The various screenshots shows the User Interface of the customerFrontend app which interacts with the JAX-RS Backend REST API

Customer List Page

Update Form after a particular customer has been selected

Update Form

Customer Details Page

Customer Details Page

The complete code listing for the customerApp JAX-RS API backend can be downloaded from Github here
The complete code listing for the Ionic/Angular customerFrontend app can be downloaded from GitHub here

References

Hayri, C. (February 24. 2019). Building an API Backend with MicroProfile [ebook]. Retrieved from https://kodnito.com/posts/building-an-api-backend-with-microprofile-ebook/

Josh, J. (November 29. 2017). Easy Java EE Microservices with Payara Micro [Web log post]. Retrieved from http://jj-blogger.blogspot.com/2017/11/easy-java-ee-microservices-with-payara.html

Simon, G. (2019). HTTP Calls [Web log post]. Retrieved from https://ionicacademy.com/ionic-http-calls/

Leave a Reply

Your email address will not be published. Required fields are marked *

Do NOT follow this link or you will be banned from the site!