Who says you can’t do rapid application development in Java? Part Three: Customizing the user experience

Our pet food reviews app is now working perfectly well for an admin user who knows what data is in the database, but we could improve the browsing, creating, and editing process for a regular end-user. In this part, I’ll walk you through some customizations you can do on the Angular front-end and the Spring Boot RESTful API, to make sure that our data is human-readable, and that users can navigate and submit reviews in the exact places they may expect.

If  you haven’t already, make sure to run the API with “./mvnw” and run the front end Webpack development server with “yarn start”. If all goes well, you’ll be able to visit the client-side application at localhost:9000. Because it’s using Webpack hot-module reloading under the hood, any changes you make to the front-end app will show up in your browser, without you having to hit refresh.

To edit the front-end code, open up src/main/webapp. You’ll see a full Angular 6+ web app here, with the features organized into multiple Angular modules. The front-end representation of the entities we generated in Part 2 will be in a module you can find at src/main/webapp/app/entities. Each entity has its own module within the entities module. So, for example, you’ll see the Food module at src/main/webapp/app/entities/food.module.ts, with a route file for that module at src/main/webapp/app/entities/food.route.ts. Alongside those files, you’ll see the TypeScript and HTML files for the basic CRUD components: index (food.component.ts + food.component.html), detail, edit/update, create (shared with food-update.component.ts), and delete. When starting out on a JHipster project, these files will get you a lot of bang for your buck — editing, reusing, or copying them with small tweaks will cover 90% of your CRUD use cases, so that you can spend most of your remaining time on things that are unique to your project.

But for now, let’s start with some simple changes in wording and branding. You’ll notice that the navbar has a dropdown for “entities” — let’s change that to something that’ll let end-users know that this is the place to go to interact with most of our core features. Open up src/main/webapp/app/layouts/navbar/navbar.component.html. Find the <span> element that contains the text “Entities” and change it to “Browse”. Within the “ul” with the class “dropdown-menu”, find the “span” element that says “Pet” and change it to “Pets”. Do the same for “Food”, but change it to “Food Options”, and change “Food Review” to “Food Reviews”. Once you’ve completed these small changes, you’ll see  that the navbar has a dropdown that lets you “browse pets” or “browse food options” or “browse food reviews.” Of course, the sky’s the limit for any further UX improvements you want to do, but at this stage we’ve at least made our navbar read like plain English, which means we’re further along to an MVP.

Let’s now take a look at our core feature: the Food Review. Make sure you’ve populated the database with some pets and foods before you do this step, so that the food review form actually has some options to work with. Open up the form component at src/main/webapp/app/entities/food-review/food-review-update.component.html. Inside the <option> element with the text {[foodOption.id}}, change the text to {{foodOption.name}}. Do the same for the <option> element with the text {{petOption.id}}. Then, for the <option> element with the text {{userOption.id}}, change it to {{userOption.firstName}} {{userOption.lastName}}. These small changes in display names will now allow end-users to see meaningful names for each associated field, instead of id fields that don’t mean anything outside of the database records.

You  may also notice that the “body” field for food reviews is just one line long. Let’s change it to a text area with a few rows, since it’s the core content we want users to submit. Find the <input> element with an [(ngModel)] of “foodReview.body”, and change it to a <textarea> with all the same properties. Make sure to add an ending </textarea> tag. You’ll immediately see the input expand, and you can add a rows=”(number)” attribute if you want it to be even bigger.

From there, the only remaining cosmetic change is to open up src/main/webapp/app/home/home.component.html, and change the text to match your website’s theme. With these small cosmetic changes, our app is now usable for a regular visitor. We didn’t even have to modify the backend API, but now we have a perfectly usable MVP!

Stay tuned for Part Four, where we’ll define a custom use case that allows us to define which pets line up with and can eat which foods.

Who says you can’t do rapid application development in Java? Part Two: Creating our entities, migrating the database, and allowing users to add reviews

In Part One of this tutorial, I covered  how to install JHipster and  use it to generate a Monolithic Spring Boot API with an Angular front end. The app that JHipster generated already includes a lot of great authentication and authorization features, but now it’s time to add the entities that will make our app unique.

JHipster uses Liquibase to manage database migrations, so feel free to add a migration and entity manually if you feel comfortable doing  so. But to follow our theme of rapid application prototyping, we will use JDL Studio, a web application that lets you visualize your database schema and then import it into JHipster. The CLI for JHipster will then allow you to generate the appropriate migrations, entities, JPA repositories, AND front-end components.

The purpose of our application is to create, read, edit, and delete information about pets, pet foods, and reviews of how well each food works for each pet. So let’s start with three basic entities:

entity Pet {
name String
description String
slug String

entity Food {
name String
description String

entity FoodReview {
title String
body String
rating FoodRating

In other words, we create an entity by typing “entity <EntityName>”, and then we add some curly braces to define the entity’s fields. Each field is defined by writing a lowercase field name, followed by an upper-CamelCase field type. You may notice that one of our field types is an undefined FoodRating. Add that as an enum below:

enum FoodRating {



Our application will need some relationships between our entities if we want to have any meaningful way of browsing reviews for a particular pet and food. Create them with this snippet:

relationship ManyToOne {
FoodReview{author} to User,
FoodReview{food} to Food{review},
FoodReview{pet} to Pet{foodReview}

As you can see, we define all our relationships in one place. JDL Studio works best with ManyToOne relationships, but feel free to create join tables any time you need to simulate a ManyToMany relationship. We define each relationship by typing <EntityName for the “many” side>{<alias for the owning entity>} to <EntityName for the owning entity>. For our use case, we only needed three relationships to start:

  1. A user can have many food reviews, and is referred to as the reviews’ author. So we type: FoodReview{author} to User
  2. A food can have many reviews. So we type: FoodReview{food} to Food{review}
  3. A pet can have many food reviews. So we type FoodReview{pet} to Pet{foodReview}

You may have noticed that we haven’t set up any association between pets and foods. That means we won’t yet be able to query for all the reviews associated with the foods that a particular pet ate. Sure, we can use FoodReview as a join-table between pets and foods, but what if we want to specify additional data about the pet-food relationship, without depending on a user’s review? Use your judgment here, but I consider this use-case complex enough, and important enough, that it deserves to be programmed at a more-granular level in a later blog post. For now, we’ll start with an MVP that can get some reviews, foods, and pets onto the page.

Once you’ve typed out your entities and relationships, you can specify the types of Java service classes and pagination we want each entity to use:

service all with serviceImpl

paginate Food, FoodReview with pagination
paginate Pet with infinite-scroll

When you’re done, download this JDL file into a new directory called “src/main/scripts”. Then run this command to import the JDL file:

jhipster import-jdl src/main/scripts/jhipster-jdl.jh

You will be prompted about some file conflicts, such as in the liquibase migration config, the Angular front end navbar component, and the Angular entity module file. To keep things simple, say yes to all of these prompts.

Open up your web application again, log in as an admin user, and check out the “entity” dropdown in the navbar: you now have CRUD pages for food, pets, and food reviews, with all the associations available in the forms!

Try creating some food and pet entities, and then head over to the Food Review form. So far so good, but you’ll see that in the pet and food dropdowns, you’ll only see id numbers, not the names of the pets and foods you can select. Not to mention that you’ll probably want to just browse to a food or pet’s page and be able to add a review right there, and have the association filled in automatically.

In other words, our app is perfectly functional for a savvy admin, but we have some work to do to improve the experience for an end user. It’s time to roll up our sleeves and start coding some TypeScript and Java. Stay tuned for Part Three: