In this tutorial, we will walk through the process of developing an AngularJS frontend for a REST server application. AngularJS is a popular JavaScript framework that simplifies the development of dynamic web applications. We will assume that you have basic knowledge of HTML, CSS, and JavaScript.
Introduction
While this tutorial focuses on developing a REST application using Jakarta EE and AngularJS, it’s important to note that AngularJS is now considered a legacy framework. For modern web development, Angular (the successor to AngularJS) is a more appropriate choice due to its enhanced performance, modular architecture, and better support for mobile development.
Why Choose Angular Over AngularJS?
- Performance: Angular offers improved performance with a more efficient change detection mechanism.
- Modularity: Angular’s component-based architecture makes it easier to manage and scale applications.
- Mobile Support: Angular is designed with mobile support in mind, making it more suitable for today’s web applications.
- Active Support: Angular is actively maintained and regularly updated by Google, ensuring you have access to the latest features and security updates.
For a more up-to-date approach, I recommend checking out my article on developing a Jakarta EE 10 application with an Angular frontend: Jakarta EE 10 Application with an Angular Frontend
Prerequisites
- Basic knowledge of HTML, CSS, and JavaScript.
- JDK and a Java Application Server such as WildFly
To keep this tutorial simple, we will not install Node Package Manager (npm) to bootstrap our AngularJS project. We do recommend to use it if you want to advance your AngularJS skills.
Do I need NPM to develop Angular JS applications?
Using the npm package manager is not mandatory but it provides several advantages for managing dependencies and building AngularJS applications. Npm simplifies dependency management by allowing you to specify dependencies and their versions in a package.json file. It also integrates well with version control systems and offers module resolution for organized code. Finally, npm enables the use of build tools and task runners for automation and optimization. Overall, npm enhances development workflow, maintainability, and collaboration in AngularJS projects.
Coding the Back end REST Application
Firstly, we will code our REST Application which will expose a set of CRUD (Create, Read, Update and Delete) methods to manage an Entity Class.
Therefore, let’s begin from the Entity Customer Class which follows here:
@Entity @NamedQuery(name = "Customers.findAll", query = "SELECT c FROM Customer c ORDER BY c.id") public class Customer { @Id @SequenceGenerator( name = "customerSequence", sequenceName = "customerId_seq", allocationSize = 1, initialValue = 1) @GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "customerSequence") private Long id; @Column(length = 40) private String name; @Column(length = 40) private String surname; // Getters/Setters omitted for brevity }
To manage our Entity application we will add a Repository Class which is a CDI Bean with a set of @Transactional methods and one method to fetch the list of Customers:
@ApplicationScoped public class CustomerRepository { @PersistenceContext private EntityManager entityManager; public List<Customer> findAll() { return entityManager.createNamedQuery("Customers.findAll", Customer.class) .getResultList(); } public Customer findCustomerById(Long id) { Customer customer = entityManager.find(Customer.class, id); if (customer == null) { throw new WebApplicationException("Customer with id of " + id + " does not exist.", 404); } return customer; } @Transactional public void updateCustomer(Customer customer) { Customer customerToUpdate = findCustomerById(customer.getId()); customerToUpdate.setName(customer.getName()); customerToUpdate.setSurname(customer.getSurname()); } @Transactional public Customer createCustomer(Customer customer) { entityManager.persist(customer); entityManager.flush(); // Ensures the ID is populated after persistence return customer; } @Transactional public void deleteCustomer(Long customerId) { Customer c = findCustomerById(customerId); entityManager.remove(c); } }
Finally, all the Service methods are mapped by a REST Endpoint which produces and consumes JSON Content:
@Path("customers") @ApplicationScoped @Produces("application/json") @Consumes("application/json") public class CustomerEndpoint { @Inject CustomerRepository customerRepository; @GET public List<Customer> getAll() { return customerRepository.findAll(); } @POST public Response create(Customer customer) { Customer c = customerRepository.createCustomer(customer); return Response.status(201).entity(customer).build(); } @PUT public Response update(Customer customer) { customerRepository.updateCustomer(customer); return Response.status(204).build(); } @DELETE public Response delete(@QueryParam("id") Long customerId) { customerRepository.deleteCustomer(customerId); return Response.status(204).build(); } }
Connecting the Back end to the Datasource
This simple REST Application will connect to the application server’s default Datasource, which is for WildFly the H2 Database available at the JNDI Binding java:jboss/datasources/ExampleDS
<persistence xmlns="https://jakarta.ee/xml/ns/persistence" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="https://jakarta.ee/xml/ns/persistence https://jakarta.ee/xml/ns/persistence/persistence_3_1.xsd" version="3.1"> <persistence-unit name="primary"> <jta-data-source>java:jboss/datasources/ExampleDS</jta-data-source> <properties> <!-- Properties for Hibernate --> <property name="hibernate.hbm2ddl.auto" value="create-drop" /> <property name="hibernate.show_sql" value="false" /> </properties> </persistence-unit> </persistence>
Coding the AngularJS Front end
Our front end includes just a single inde.xhtml page which contains an AngularJS Controller that manages access to the REST Endpoint. Here is the index.html page:
<!DOCTYPE html> <html lang="en" ng-app="myApp"> <head> <meta charset="UTF-8"> <title>Customer Management</title> <script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.8.2/angular.min.js"></script> </head> <body ng-controller="CustomerController"> <h1>Customer Management</h1> <form ng-submit="createCustomer()"> <input type="text" ng-model="newCustomer.name" placeholder="Name" required> <input type="text" ng-model="newCustomer.surname" placeholder="Surname" required> <button type="submit">Create</button> </form> <table> <tr> <th>ID</th> <th>Name</th> <th>Surname</th> <th>Actions</th> </tr> <tr ng-repeat="customer in customers"> <td>{{customer.id}}</td> <td>{{customer.name}}</td> <td>{{customer.surname}}</td> <td> <button ng-click="editCustomer(customer)">Edit</button> <button ng-click="deleteCustomer(customer.id)">Delete</button> </td> </tr> </table> <form ng-show="isEditing" ng-submit="updateCustomer()"> <input type="text" ng-model="editedCustomer.name" required> <input type="text" ng-model="editedCustomer.surname" required> <button type="submit">Update</button> <button ng-click="cancelEditing()">Cancel</button> </form> <script> var app = angular.module('myApp', []); app.controller('CustomerController', function($scope, $http) { $scope.customers = []; $scope.newCustomer = {}; $scope.editedCustomer = {}; $scope.isEditing = false; // Fetch all customers $http.get('rest/customers') .then(function(response) { $scope.customers = response.data; }); // Create a new customer $scope.createCustomer = function() { $http.post('rest/customers', $scope.newCustomer) .then(function(response) { $scope.customers.push(response.data); $scope.newCustomer = {}; }); }; // Edit a customer $scope.editCustomer = function(customer) { $scope.isEditing = true; $scope.editedCustomer = angular.copy(customer); }; // Update a customer $scope.updateCustomer = function() { $http.put('rest/customers', $scope.editedCustomer) .then(function(response) { $scope.isEditing = false; // Reload all customers $http.get('rest/customers') .then(function(response) { $scope.customers = response.data; }); }); }; // Delete a customer $scope.deleteCustomer = function(customerId) { $http.delete('rest/customers?id=' + customerId) .then(function(response) { var index = $scope.customers.findIndex(function(customer) { return customer.id === customerId; }); $scope.customers.splice(index, 1); }); }; // Cancel editing $scope.cancelEditing = function() { $scope.isEditing = false; }; }); </script> </body> </html>
the Angular controller CustomerController
contains several methods that handle different functionalities of the customer management application. Here’s an explanation of each method:
createCustomer()
: This method is invoked when the form is submitted to create a new customer. It sends an HTTP POST request to the'rest/customers'
endpoint, passing thenewCustomer
object. Upon successful response, it adds the newly created customer to thecustomers
array and resets thenewCustomer
object.editCustomer(customer)
: This method is triggered when the “Edit” button is clicked for a customer. It sets theisEditing
flag totrue
and makes a copy of the selectedcustomer
object, storing it ineditedCustomer
. This enables editing the customer’s details.updateCustomer()
: This method is executed when the form for updating a customer is submitted. It sends an HTTP PUT request to the'rest/customers'
endpoint, updating the customer details with theeditedCustomer
object. Upon successful response, it setsisEditing
tofalse
and reloads all customers by making an HTTP GET request to'rest/customers'
.deleteCustomer(customerId)
: When the “Delete” button is clicked for a customer, this method is called. It sends an HTTP DELETE request to the'rest/customers'
endpoint, passing thecustomerId
as a query parameter. Upon successful response, it finds the index of the deleted customer in thecustomers
array and removes it usingsplice()
.cancelEditing()
: This method is triggered when the “Cancel” button is clicked during editing. It setsisEditing
tofalse
, canceling the editing mode.
These methods utilize the $http
service to make HTTP requests to interact with the REST server and update the application’s data accordingly. They are responsible for creating, editing, updating, and deleting customers, as well as fetching the initial list of customers from the server.
Running and Testing our application
We can deploy the application on WildFly with a simple set of Maven goals:
mvn install wildfly:deploy
And then you can access the index.html page at: http://localhost:8080/jaxrs-demo/
The Create button will let you add another Customer Entity through the AngularJS Controller and the REST Service. The Edit and Delete Buttons will let you modify or delete an existing one.
Conclusion
In conclusion, by integrating AngularJS as the frontend framework for your Jakarta EE REST application, you can deliver a modern and engaging user experience, streamline data communication, and build a robust full-stack web application. So, go ahead and start building your AngularJS frontend to complement your Jakarta EE backend and unlock the full potential of your REST application.
Source code: https://github.com/fmarchioni/mastertheboss/tree/master/jax-rs/angularjs