Explore the model developed in the last lab a little further by attaching a Semantic UI. Experiment with UI elements that change the model.
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.
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.
GET /divisions/delete/{id} Divisions.delete
<td>
<a class="ui ui icon button" href="/divisions/delete/${division.id}">
<i class="delete red icon"></i>
</a>
</td>
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?
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
GET /sponsors/delete/{id} Sponsors.delete
<td>
<a class="ui ui icon button" href="/sponsors/delete/${sponsor.id}">
<i class="delete red icon"></i>
</a>
</td>
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?
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
#{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.
Archive of project so far:
On the clubs page, have the table display the sponsors.
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.
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?