JAVA exPress > Archive > Issue 5 (2009-10-01) > To-do list in Grails

To-do list in Grails

In the previous issue of JavaExpress I showed how to start working with Grails; install the environment, create the first project, domain classes with restrictions and how to use dynamic scaffolding. Now let us create a simple to-do list in Grails bottom-up. You will see how to develop a controller, use basic facilities of GORM and create custom views – crafting a CRUD application manually, without generating anything.

Creating a project and a domain class

Let us begin with creating a new project named ToDo. As we all remember from the previous article, this is all you need to do to start working with a Grails project – HSQLDB and Jetty bundled with Grails are ready to work. Let us create a domain class named Task. The task will have a subject, a due date and a “finished” flag.

    class Task {
        String subject
        Date dueDate
        Boolean completed

        static constraints = {
        }
    }

Creating a controller

The next step is creating a Task controller (like with a domain class, a controller can be created using the context menu of the project or using the command line). Let us define the following methods in it: list, edit, save, delete. They will be responsible for: displaying a list of tasks, displaying a form to add/edit a record, saving a record (a new or modified one) and deleting a record accordingly.

Retrieving records

Let us take the list action first. Passing data from a controller to a view in Grails is done by returning a map from an action. Keys of the map will represent variables available in the view. Our action can look like this:

    def list = {
        [tasks:Task.findAll()]
    }

The map is created using the square brackets notation. There will be a list of tasks retrieved from the database under the key tasks. You may notice a Groovy language feature – the lack of the return keyword. By default the result of the last executed statement is returned, in the above case the created map.

To make things complete we need a GSP file which will render our tasks. Select Views and Layouts -> task from the project, New -> GSP File from the context menu and type list as the name. Grails uses the convention-over-configuration principle, so the view file is named exactly the same as the action. If needed Grails could render any other view, but now the out-of-the-box functionality is sufficient.

Let us add the following code to our view:

    <g:each in="${tasks}" var="task">
        ${task}
    </g:each>

We are using here a tag bundled with Grails which lets us to iterate over a collection of elements in the view. The in attribute holds the variable over which we will iterate. The var attribute defines the name of the element in the iterations. Without setting this attribute the element will be named it, just like the default parameter in closures. You could add a status attribute which will be the name of a variable storing a counter of the iterations (useful when creating numbered lists etc.)

You could try and run the project to test it, but we do neither have any records nor a view to add them.

BootStrap.groovy

We will get a little help from the BootStrap.groovy file. It contains the init section which is executed during the application start-up. We can add the following code to it:

    new Task(subject:"Pranie", priority:1, dueDate:new Date(), completed:false).save()

This code will create one task and store it in the database. The example shows also how to create class instances passing values of its attributes – it is another nice feature of Groovy. Grails also gives us the ability to create an instance of a domain class with an automatic assignment of HTTP request parameters to attributes. We will use it later in saving a new task.

After such a modification of the BootStrap file our application should display the first task on the list.

Adding and editing a record

Adding and editing a record will be done using a shared view. If no identifier is passed, the application will display an empty form to add a new task. In the case when there is an identifier, before displaying the form the application will retrieve the appropriate record to edit it. Storing the records will be done in a shared method save.

Let us prepare the edit view (similarly to the list view). You do not have to write anything in the controller in the edit action, because we want only to add a new record. An empty form will be sufficient. It may look like this:

    <g:form name="addForm" action="save" id="${task?.id}">
        Subject: <g:textField name="subject" value="${task?.subject}"/><br/>
        Due Date: <g:datePicker name="dueDate" value="${task?.dueDate?:(new Date())}"/><br/>
        Completed: <g:checkBox name="completed" value="${task?.completed}"/><br/>
        <g:submitButton name="save" value="Save" />
    </g:form>

A new g:form tag is show here. It is used to build HTML forms together with g:textField, g:datePicker, g:checkbox and g:submitButton. I encourage to read the documentation regarding the possible attributes used to configure these tags. Two new interesting Groovy features appear in this code as well: the ?. safe navigation operator which prevents a NullPointerException to be thrown (when the left operand is null the whole expression returns null as well) and ?: which works similar to its counterpart in Java (if the expression on the left is null or false the right expression is returned, otherwise it stays the same). Thanks to handling the null and default values the form can be used to both editing records and adding new ones.

The form sends data to the save action which stores a new record:

    def task = new Task(params)
    task.save()
    redirect(action:"list")

This action creates a new Task object based on the passed map of HTTP request parameters (params). Then it stores the record and performs a redirect to the list action.

Let us add a link allowing us to edit a record and modify the edit action to retrieve a record before displaying the form. Start with the list view:

    <g:each in="${tasks}" var="task">
        <g:link action="edit" id="${task.id}">${task}</g:link><br/>
    </g:each>

The g:link tag is introduced here which allows us to create links. As you might have noticed Grails supports passing names of controllers, actions and identifiers in forms and links. Adhering to the Grails convention simplifies work, but it is possible to control it if needed.

The modified edit action looks like this:

    if (params.id) {
        [task:Task.get(params.id)]
    }

If the request parameter named id is passed, the application is retrieving a Task record with that id. The condition used here looks quite interesting. The params variable is a map containg the HTTP request parameters which are of type String. How does it work then? It is another interesting feature; Groovy Truth. Not only boolean type expressions can be used in conditions. 0 (zero), an empty string, null, an empty array on an empty map are also treated as false in conditions. It simplifies many expressions.

The only remaining thing is modifying the save action. Before it only created a new record based on the request parameters, now it will have to update an existing record as well:

    def task
    if (params.id) {
        task = Task.get(params.id)
        task.properties = params
    } else {
        task = new Task(params)
    }
    task.save()
    redirect(action:"list")

Deleting a record

In order to delete record let us add a new link next to each record which would trigger the delete action. Everyone will manage to create it by himself.

And how will the controller action look like? Not to complex as expected:

    def task = Task.get(params.id)
    task.delete()
    redirect(action:"list")

Similarly to the save action the application retrieves a record based on the request parameter. Next it is deleted and the list of tasks is displayed.

Summary

In this article I have shown how to create a simple Grails application with all CRUD operations. This time no scaffolding was used and everything was done manually. The application is not very complex, but it is fully functional and what is most important – it did not take too much time. The basic features of GORM (Grails Object Relationship Mapping) were introduced which allowed us to retrieve, store and delete records. We know what a domain class, a controller and views (the basic components of Grails) look like. At the same time some interesting features of the Groovy language were mentioned.

What next

It is widely known that solving problems improves learning. As an exercise I suggest performing the following tasks:

  • modifying the task list to be more readable and user-friendly;
  • introducing data validation (e.g. the task subject not being empty). As a hint: the constraints section of a domain class and validate method. The g:renderErrors tag will be useful as well;
  • adding a sidebar menu on the page. As a hint: modifying the page layout by adding an additional element which is the menu.

The source code is located at http://code.google.com/p/javaexpress-grails-todo/. Maybe some of the readers would like to contribute to this project. Best wishes and have fun.

Translation by Paweł Cegła

Comments

  1. Odniosłem wrażenie, że artykuł napisany jest po łebkach :/ Wykonuje instrukcje krok po kroku i niestety pierwsze uruchomienie w moim przypadku kończy się błędem. I to jeszcze w NB w którym wszystko działa out of the box.

  2. Udało mi się uruchomić przykład, ale dalej mam wrażenie, że niektóre punkty można napisać jaśniej (np. gdzie dokładnie utworzyć list.gsp).

  3. Super, że się udało. W każdej chwili możesz skorzystać z grupy dyskusyjnej JAVA exPress. Na 100% znajdzie się ktoś, kto będzie Ci w stanie pomóc. Czasami ciężko jest trafić do każdego. Z jednej strony są ludzie tacy jak Ty, którzy potrzebują dokładnych instrukcji (co jest zrozumiałe w przypadku technologii której nie znasz), a z drugiej strony pojawiały się głosy (z tego co pamiętam to na dzone), że przykład jest zbyt uproszczony.
    W przypadku dalszych problemów spróbuj napisać na naszą grupę dyskusyjną. Na 100% znajdą się osoby, co będą chciały Ci pomóc.

  4. Spoko :) Dzięki za zainteresowanie. Jasne, że ciężko trafić do każdego, ale dobrze, że przynajmniej podejmuje się próbe. Fakt, że Grails nie znam zupełnie. No i wielkie szacun za JavaExpress!

Only logged in users can write comments

Developers World