Lesson Monday

As we saw at the end of the previous lesson, variables allow us to easily DRY up our code. However, their real power is allowing us to do things with information before we even know what the information is. That is, we can use a variable as a placeholder, then later insert a custom value into that variable. You are familiar with this principle from your time in intro! This allows our websites to customize themselves based on input from a user. And the most common manner of gathering input from users is through forms.

Let's rewrite the letter to our friend from the last lesson. We'll create a more generic greeting card site where anyone can create a custom card for their friends. To do this, we'll use a form to gather the user's name and their friends names. Then, we'll dynamically add those names into our letter, and display the customized results for the user to see. If you are creating a new project, make sure you make a layout.hbsfile.

Integrating Forms into Spark

Adding a New Route

Add the following route to App.java:

custom-greeting/src/main/java/App.java
...
get("/form", (request, response) -> {
   Map<String, Object> model = new HashMap<String, Object>();
   return new ModelAndView(model, "form.hbs");
}, new HandlebarsTemplateEngine());
...

As you can see, this route is called "/form". That means that when the user navigates to localhost:4567/form, the code in this route will execute. Next, let's create a template for this route to render. It will contain an HTML form to collect input from the user.

Creating a Form Template

In our templates directory, create a file called form.hbs and add the following:

custom-greeting/src/main/resources/templates/form.hbs
{{#partial "title" }}
  Create a Custom Greeting!
{{/partial}}

<!--form.hbs.-->
{{#partial "content"}}

<h1>Fill in your name and your friend's name to create your custom greeting!</h1>
<form action='/greeting_card'>
 <div class="form-group">
   <label for="sender">Your name</label>
   <input id="sender" name="sender" class="form-control" type="text">
 </div>
 <div class="form-group">
   <label for="recipient">Your friend's name</label>
   <input id="recipient" name="recipient" class="form-control" type="text">
 </div>
 <button type="submit" class="btn">Go!</button>
</form>
<!--form.hbs.-->
{{/partial}}

{{> layout.hbs}}

Let's take a closer look at this form. You already know a lot about forms from intro, but there are additional considerations that come into play when using them in conjunction with Spark or any other server side framework, so pay attention closely:

  • The <form> tag defines the start and end of the form. But! Notice it says action="/greeting_card". It takes an attribute that tells Spark where to submit the information users provide through this form. This attribute should be set to the address of another Spark route. We will set it to a "/greeting_card" route that we will create in a moment.

  • Inside of the <form> tags, we need one <label> and one <input> tag for each piece of information we want the user to fill in. The <input> tag creates the field for the user to type into. It must have 3 attributes set: name, id, and type.

    • We will use the name attribute to retrieve the value of each input field, so it should be something that clearly describes the input. We will name ours "sender"
    • The id can be anything as long as it is unique on this page. Generally, you should use the same value for both name and id.
    • As you know, the <input> tag has many different possible values for the type attribute: Everything from checkboxes to dates, colors, and numbers. We set it to "text".
  • The <label> tag provides the text to display next to the input box. It takes one attribute called for, which should match the id attribute of the corresponding <input> tag. For example, the <input> tag with attribute id="recipient" should have a <label> tag with the attribute for="recipient".

  • The <button> tag creates the button that submits the form. It has one attribute called type. This attribute must be set to "submit".

Submitting Forms in Spark

Let's check out our new form in the browser by navigating to http://localhost:4567/form. Then, we'll fill it out and submit it.

After submitting, you'll receive a 404 error. This is simply because we haven't created the /greeting_card route yet. But notice the URL that appears in the browser after submitting. It should look something like: http://localhost:4567/greetingcard?sender=Lucy&recipient=George_, containing whatever names you entered for sender and recipient form fields.

Spark automatically loads this URL because of the form action attribute we included in our template: <form action="/greeting_card">. When the form is submitted, it requests whatever route is included in this attribute. In our case, the greeting_card route. The form action attribute should always refer to the route the application should navigate to after the user submits the form.

Additionally, the portion that looks like sender=Lucy&recipient=George are our query parameters. As you can see, they contain the name attribute of each form field (sender and recipient), and the corresponding information the user placed in those form fields (Lucy and George).

Dynamically Rendering Form Data

Now that we have a form to submit the data, let's add yet another web page and route to our friend letter application from the last lesson. The page will look exactly like our previous friend letter, but this time we'll remove the specific names in our template. Instead, we'll use variables as placeholders, then insert whatever names our application's users submit in our form.

Template

Let's create a greeting_card.hbs template with the following code:

custom-greeting/src/main/resources/templates/greeting_card.hbs
{{#partial "title" }}
  Your custom greeting
{{/partial}}


{{#partial "content"}}

<h1>Hello From Afar</h1>
<p>Dear {{ recipient }},</p>
<p>How are you? I hope that you are having a nice weekend. I'm vacationing in the Iceland while I learn programming! </p>
<p>{{ recipient }}, you would not believe how cold it is here. I should have gone to Hawaii instead.</p>
<p>But I like programming a lot, so I've got that going for me. </p>
<p>Looking forward to seeing you soon. I'll bring you back a souvenir. </p>
<p>Cheers,</p>
<p>Travel Enthusiast {{ sender }}</p>

{{/partial}}

{{> layout.hbs}}

Here, {{ recipient }}, and {{ sender }} are something new and cool - they are Handlebars variables. When the form is rendered in the browser, the names the user provides in our form will replace these variables.

Route

Now, let's add the code to retrieve the user's form input, then insert it into our greeting_card.hbs template to create their custom letter. We'll create a new route to handle this. We will call this route /greeting_card because it must match the form's action attribute. When they match, Spark will automatically execute the code in this route when the form is submitted.

Our new route should look like this:

custom-greeting/src/main/java/App.java
...
get("/greeting_card", (request, response) -> {
   Map<String, Object> model = new HashMap<String, Object>();
   String recipient = request.queryParams("recipient");
   String sender = request.queryParams("sender");
   model.put("recipient", recipient);
   model.put("sender", sender);
   return new ModelAndView(model, "greeting_card.hbs");
}, new HandlebarsTemplateEngine());
...

Here, String sender = request.queryParams(); retrieves whatever the user entered in our form's name field. We had two <input> tags, one with name="sender" and one with name="recipient". This created two query parameters with the same name as seen in the url http://localhost:4567/greeting_card?sender=Lucy&recipient=George. Spark can access the sender query parameter by calling request.queryParams("sender");.

Once we have the value from the query parameter, we can make them available to our template using our model HashMap with model.put("recipient", recipient); and model.put("sender", sender);.

If we launch the application and visit localhost:4567/form, we should be able to submit our form, and be greeted with a custom letter containing the sender and recipient names we placed in our form. This is awesome!

Troubleshooting

If you are having trouble figuring out if your parameters are going through, you can write System.out.println(request.queryParams()), navigate to the URL, and then check your command line. It will print out an array of the query names to your console like: [recipient, sender].

Forms are a very powerful part of programming. Gathering information from the user opens up a world of different website possibilities.


Example GitHub Repo for Custom Greeting

Terminology


  • Query Parameters: The information from your form, packaged up and placed in the URL after you submit, like this: http://localhost:4567/greeting_card?sender=Lucy&recipient=George. In the route that matches the form's action attribute, you can retrieve the query parameters with request.queryParams("nameAttribute");. ("nameAttribute" refers to the name attribute assigned to the form field whose input you're collecting).

Overview


  • The HTML form's action attribute must match the route in App.java responsible for collecting, manipulating, and/or displaying the information collected in the form.