Objectives

Explore the model developed in the last lab a little further by attaching a Semantic UI. Experiment with UI elements that change the model.

Semantic UI

Using your project form the last lab, or this one here if you did'nt complete it:

We wish to incorporate the latest version of the semantic ui css framework into your application, and restyle all of the views to take advantage of the framework.

Download the latest Semsntic UI archive here:

Unzip the archvive. It contains subfolder called packaged. Rename this to semantic, and drag the newly renamed subfolder into the public folder of your project.

We should also update the version of JQuery in the public/javascripts folder. It can be downloaded here:

Select the 2.x 'compressed' version.

We can now replace the views/main.html with this version:

<!DOCTYPE html>
<html>
  <head>
    <meta charset="utf-8">
    <title>#{get 'title' /}</title>
    <link rel="stylesheet" type="text/css" href="@{'/public/semantic/css/semantic.min.css'}">
    <script src="@{'/public/javascripts/jquery-2.1.0.min.js'}"></script> 
    <script src="@{'/public/semantic/javascript/semantic.min.js'}"></script>   
  </head>    
  <body>
    #{doLayout /}
  </body>
</html>

To test out the UI, simply give each <nav> element this class class="ui menu" :

<nav class="ui menu">

You should see a minimally styled menu on all screens.

Now make each section have the following class:

<section class="ui raised segment">   

A light border should appear around each view.

Finally, style all of the tables:

<table class="ui table">

Finally, adjust the link on views/players/index.html to look more like a button via the class ui mini teal button

<td> <a class="ui mini teal button" href="/players/delete/${player.id}">Delete</a> </td>

An alternative might be to use an icon button:

<td> 
  <a class="ui ui icon button" href="/players/delete/${player.id}">
    <i class="delete red icon"></i>
  </a> 
</td>

Try out these styles now.

Delete Club

First introduce the delete button in the clubs view:

          <td> 
            <a class="ui ui icon button" href="/clubs/delete/${club.id}">
             <i class="delete red icon"></i>
            </a> 
          </td>

We need a supporting route:

GET    /clubs/delete/{id}                       Clubs.delete

... and this method in the Clubs controller:

  public static void delete(Long id)
  {
    Club club = Club.findById(id);
    if (club != null)
    {
      Logger.info("Trying to delete " + club.name);
      List<Division> divisions = Division.findAll();
      for (Division division : divisions)
      {
        if (division.members.contains(club))
        {
          division.members.remove(club);
          division.save();
          Logger.info ("removing club from division");
        }
      }
      club.delete();
    }
    index();
  } 

Try deleting clubs now.

Delete Divisions

New routes:

GET    /divisions/delete/{id}                   Divisions.delete

views/Divisions/index.html

          <td> 
            <a class="ui ui icon button" href="/divisions/delete/${division.id}">
             <i class="delete red icon"></i>
            </a> 
          </td>

controllers/Divisions.java

  public static void delete(Long id)
  {
    Division division = Division.findById(id);
    division.delete();
    index();
  }

When you delete a division, pay close attention to the clubs and players tables.

Can you explain what is happening?

Show Sponsors

Currently the Spnosor table is incomplete - it only lists the sponsor names, and not the club being sponsored:

  <table class="ui table">
    <thead>
      <tr>
        <th>Sponsor</th>
        <th></th>
        <th></th>
      </tr>
    </thead>
    <tbody>
        #{list items:sponsors, as:'sponsor'}
          <tr>
            <td>${sponsor.name}</td>
            <td></td>
            <td></td>
          </tr>
        #{/list}
    </tbody>
  </table>

We can fix this now:

  <table class="ui table">
    <thead>
      <tr>
        <th>Sponsor</th>
        <th>Clubs</th>
        <th></th>
      </tr>
    </thead>
    <tbody>
        #{list items:sponsors, as:'sponsor'}
          <tr>
            <td>${sponsor.name}</td>
          <td> 
            <table class "ui table">
              <tr>
               #{list items:sponsor.support, as:'club'}
                 <td>${club.name}</td> </tr>
               #{/list}
               </tr>
             </table>            
          </td>
            <td></td>
          </tr>
        #{/list}
    </tbody>
  </table>

However, of you run the app you will see that the sponsors table doesnt contain any clubs. This is because there are no sponsor-club relationships in our test data.

To fix this takes a bit of work, because we have a ManyToMany relationship, which requires definition on both sides.

Open the data.yaml file (the one in the conf directory), and copy the following in at the very top of the file (we already have these at the end, leave them there as well):

Sponsor(pub):
    name: pub 

Sponsor(newsagent):
    name: newsagent    

Now adjust fenor and tramore to have some sponsors:

Club(tramore):
    name: tramore
    sponsors:
          - pub
          - newsagent

Club(fenor):
    name: fenor
    sponsors:
          - newsagent 

and finally, at the end of the yaml file, include the 'other side' of the relationship in the sponsors:

Sponsor(newsagent):
    name: newsagent
    support:
         - tramore
         - fenor

Sponsor(pub):
    name: pub 
    support:
         - tramore

Try this out now, and sponsors should show the clubs they support.

Here is the complete yaml file:


Sponsor(pub):
    name: pub 

Sponsor(newsagent):
    name: newsagent    

Club(dunmore):
    name: dunmore

Club(tramore):
    name: tramore
    sponsors:
          - pub
          - newsagent

Club(fenor):
    name: fenor
    sponsors:
          - newsagent   

Player(jim):
    name: jim
    club: dunmore

Player(mary):
    name: mary
    club: dunmore

Player(sam):
    name: sam
    club: tramore

Player(john):
    name: john
    club: tramore

Player(mike):
    name: mike
    club: fenor

Player(linda):
    name: linda
    club: fenor    

Division(senior):
    name: senior
    members:
            - tramore
            - dunmore

Division(junior):
    name: junior
    members:
            - fenor

Sponsor(newsagent):
    name: newsagent
    support:
         - tramore
         - fenor

Sponsor(pub):
    name: pub 
    support:
         - tramore

Delete Sponsors

New route:

GET    /sponsors/delete/{id}                    Sponsors.delete

views/Sponsors/index.html

          <td> 
            <a class="ui ui icon button" href="/sponsors/delete/${sponsor.id}">
             <i class="delete red icon"></i>
            </a> 
          </td>

controllers/Sponsors.java

  public static void delete(Long id)
  {
    Sponsor sponsor = Sponsor.findById(id);

    for (Club club  : sponsor.support)
    {
      club.sponsors.remove(sponsor);
      club.save();
    }

    sponsor.delete();
    index();
  }  

Look carefully at the delete method. Can you make sense of it?

New Player

We would like a way of adding a new player via the UI.

First, bring in a new button on the player view to trigger the creating of a player:

  <a class="ui blue button" href="/players/addplayer">
    <i class="user icon"></i> Add Player
  </a> 

Then the routes:

GET    /players/addplayer                       Players.addPlayer
POST   /players/newplayer                       Players.newPlayer

..and the method in Players to display the add player form:

  public static void addPlayer()
  {
    render();
  }

  public static void newPlayer(String name)
  {
    Player player = new Player (name);
    player.save();
    index();
  }

We also need a form

views/Players/addplayer.html

#{extends 'main.html' /}
#{set title:'Player Details' /}

<section class="ui raised form segment">
  <form action="/players/newplayer" method="POST">
    <div class="field">
      <label> Player Name </label>
      <input type="text" name="name">
    </div>  
    <button class="ui blue submit button">Add</button>
  </form>
</section>

Try this out now.

You will get an error - as you have added a player, however not associated the player with a club. The club is a 'null' reference in the database, and causes the view to fail.

We can fix this easily, permitting 'unattached' players. Open views/Players/index.html and replace this line:

<td>  ${player.club.name} </td>

with this:

<td>  ${player.club?.name} </td>

There is a very small difference. This is using a null-safe operator ? when trying to access the clubs name. If the club is null (i.e. there is no club), we will not attempt to reach into the name field.

Make sure this works now as expected.

Exercises

Archive of project so far:

Exercise 1

On the clubs page, have the table display the sponsors.

Exercise 2

Following on from step 7, provide buttons/views to create Clubs, Divisions and Sponsors. You may need to use the null safe operator from step 7 to do this effectively.

Exercise 3

We still dont have a way of enabling the user to establish associations between the different models. E.g. have new player belong to a club, or clubs be in a division etc..

How would you approach this?