Objectives

Enhance the app to implement a simple UX based in static contentExtend the application to include a range of static content.Continue to explore the routes file, and implement a new route/controller combination to deliver a complete view.

Lab06 Exercises

This lab assumes you have competed Lab06. This is the project you may have imported last week (the final version):

This is the solution to Exercise 1 from last weeks lab. You may have already implemented it. If so, compare your solution with this one:

Lab 06: Exercise 1: Active Tab

If you look at the nav bar you will notice that there is no concept of an 'active' tab as such. i.e. user cannot clearly see which tab they have just selected.

The reason of this is that none of the <li> elements are have class="ui active item" (check the lab05-project to see what this looks like).

<nav class="ui menu">
  <a class="ui active item" href="/home">Home</a>
  <a class="ui item" href="/members">Members</a>
  <a class="ui item" href="/profile">Profile</a>
  <a class="ui item" href="/login">Logout</a>
</nav>

Lab 06: Exercise 1 Solution

Currently, each page has an identical navigation bar - with none of the nav items marked active. We would to rework the navigation to fix this - i.e. have each navigation bar customised to each view.

views/Home/index.html

<nav class="ui menu"> 
  <a class="ui active item" href="/home">Home</a>      
  <a class="ui item" href="/members">Members</a>   
  <a class="ui item" href="/profile">Profile</a>   
  <a class="ui item" href="/login">Logout</a>      
</nav>

views/Members/index.html

<nav class="ui menu"> 
  <a class="ui item" href="/home">Home</a>      
  <a class="ui active item" href="/members">Members</a>   
  <a class="ui item" href="/profile">Profile</a>   
  <a class="ui item" href="/login">Logout</a>      
</nav>

views/Profile/index.html

<nav class="ui menu"> 
  <a class="ui item" href="/home">Home</a>      
  <a class="ui item" href="/members">Members</a>   
  <a class="ui active item" href="/profile">Profile</a>   
  <a class="ui item" href="/login">Logout</a>      
</nav>

Running the App

Your project should still look like this:

Run the app from the command line as before. I.e. in the Command Prompt, navigate to the folder containing the project and enter:

play run
~        _            _ 
~  _ __ | | __ _ _  _| |
~ | '_ \| |/ _' | || |_|
~ |  __/|_|\____|\__ (_)
~ |_|            |__/   
~
~ play! 1.3.0, http://www.playframework.org
~
~ Ctrl+C to stop
~ 
Listening for transport dt_socket at address: 8000
08:02:40,017 INFO  ~ Starting /Users/edeleastar/dev/webdevws/spacebook
08:02:40,847 WARN  ~ You're running Play! in DEV mode
08:02:40,959 INFO  ~ Listening for HTTP on port 9000 (Waiting a first request to start) ...

Browse to:

and the application should appear:

Verify that by selecting any of the tabs, a different message appears on each page.

Home Page Content

Examine again the 'static' version of spacebook:

This is not in any sense 'live', but it does contains useful hints as to how we might structure our application. In this step we will bring over some of the content so that our web app closely resembles the content of that site.

Home Page - views/Home/index.html

Open the view in eclipse:

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

<nav class="ui menu"> 
  <a class="ui active item" href="/home">Home</a>      
  <a class="ui item" href="/members">Members</a>   
  <a class="ui item" href="/profile">Profile</a>   
  <a class="ui item" href="/login">Logout</a>      
</nav>

<p>
  Welcome to the Spacebook Home!
</p>

Delete the final paragraph (leave everything up to the <p> intact), and replace with the static content we have already prototyped in lab05-spacebook project:

<section class="ui segment">
  <h2 class="ui header">SpaceBook: Homer's Home Page</h2>
  <div class="ui two column grid segment">
    <div class="ui row">
      <div class="ui column">
        <h2>Friends</h2>
        <div class="ui list">
          <div class="item">
            <i class="right triangle icon"></i>
            <div class="content">
              <a href="marge.html">marge</a>, (<a href="drop/marge">drop</a>)
            </div>  
          </div>
          <div class="item">
            <i class="right triangle icon"></i>
            <div class="content">
              <a href="lisa.html">lisa</a>, (<a href="drop/lisa">drop</a>)
            </div>  
          </div>
        </div>
      </div>
      <div class="ui column">
        <h2>Messages</h2>
        <div class="ui list">
          <div class="item">
            <i class="right triangle icon"></i> 
            <div class="ui content">
              marge says..."Hey there Homer, when are you going to work?"
            </div>
          </div>
          <div class="item">
            <i class="right triangle icon"></i> 
            <div class="content">            
              lisa says..."Move off the couch dad!"
            </div>  
          </div>
        </div>
      </div>
    </div>
  </div>
</section>

Check

and the new content should appear on the home page

Do the same with these two pages, replacing the paragraph with the content as shown below:

Members Page - views/Members/index.html

<section class="ui segment">
  <h2 class="ui header">SpaceBook's Members</h2>
  <div class="ui list">
    <div class="item">
      <i class="right triangle icon"></i> 
      <div class="content">
        marge [<a href="home.html">follow</a>]
      </div>
    </div>
    <div class="item">
      <i class="right triangle icon"></i> 
      <div class="content">
        bart [<a href="home.html">follow</a>]
      </div>
    </div>
    <div class="item">
      <i class="right triangle icon"></i> 
      <div class="content">
        lisa [<a href="home.html">follow</a>]
      </div>
    </div>
    <div class="item">
      <i class="right triangle icon"></i> 
      <div class="content">
        maggie [<a href="home.html">follow</a>]
      </div>
    </div>
  </div>
</section>

Profile Page - views/Profile/index.html

<section class="ui segment">
  <h2 class="ui header">Homer's Profile</h2>
  <div class="ui two column grid segment">
    <div class="ui row">
      <div class="ui column">
        <p>
          <img src="images/homer.gif" />
        </p>
        <form action="homeprofile/upload" method="post" enctype="multipart/form-data">
          <input type="file" name="userfile" value="" /> <input type="submit" name="submit" value="upload" />
        </form>
      </div>
      <div class="ui column form segment">
        <form action="homeprofile/changetext" method="post">
          <h3 class="ui inverted teal block header">Enter Status</h3>
          <textarea class="ui field" name="profiletext"> </textarea>
          <input class="ui blue button" type="submit" name="submit" value="Change" />
        </form>
      </div>
    </div>
  </div>
</section>

Save all files in eclipse, and reload the browser (making sure play is running):

The 'live' app should match the static version:

Links to User Profiles

Returning to the home page, notice that homer has 2 friends, Marge and Lisa. Selecting either of these generates an error:

These links are generated by these html elements in 'views/Home/index.html':

...
  <a href="marge.html">marge</a>, (<a href="drop/marge">drop</a>)
...
  <a href="lisa.html">lisa</a>, (<a href="drop/lisa">drop</a>)
...

We would like to changes these links such that they load a page. We do this by having them call a method in one of the controllers, and have that controller then 'render' the page.

Change above elements to the following:

...
  <a href="/publicprofile">marge</a>, (<a href="drop/marge">drop</a>)
...
  <a href="/publicprofile">lisa</a>, (<a href="drop/lisa">drop</a>)
...

For this to work, we need to make sure the file 'conf/routes' is properly configured. Edit this now and make sure it contains the following entry:

# Public Profiles
GET     /publicprofile                          PublicProfile.index

Place this after the "Profile page" entry.

Make sure the the view/PublicProfile/index.html contains the following:

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

<nav class="ui menu">   
  <a class="ui active item" href="/home">Back to Home</a> 
</nav>

<p>
  Welcome to the Public Profile!
</p>

Make sure to save all files in Eclipse, browse to the site and select 'marge' or 'lisa' from the home page. What do you see? The error should be gone, and you should have something like this:

Note carefully the url in the address bar.

The same page is being displayed for both users, we would like to change this, such that we at least get a message indicating which user we are visiting. First lets change the urls in the views/Home/index.html page, this time we extend the url to indicate that we want to 'visit' a specific users page:

...
   <a href="/publicprofile/marge">marge</a>, (<a href="drop/marge">drop</a>)
...
   <a href="/publicprofile/lisa">lisa</a>, (<a href="drop/lisa">drop</a>)
...

Save this page in eclipse, and refresh the browser. Try to click on one of the links - 'marge' or 'lisa'. You will get the same error you may have seen previously.

This is a 'routing error' - that is a url has arrived at your app that it cannot recognize. These errors are usually a result of a mismatch in the 'conf/routes' file. Make the following changes to it:

First, delete this line (the one we have just entered):

GET     /publicprofile                           PublicProfile.index

... and replace it with this entry:

GET     /publicprofile/{name}                   PublicProfile.visit

Save everything and restart the Play application. To restart the application, enter Control-C in the console running the play application, and enter play run again start it.

Try to visit marge's page again. We should still have the routing error. Specifically, note that it says:

UserProfile.visit action not found

Now, put an empty visit method into the controllers/UserProfile class:

public class PublicProfile extends Controller
{
  public static void index()
  {
    render();
  }

  public static void visit(String name)
  {
  }  
}

Save everything and reload - this time you should get a blank page:

Reflect again on these three fragments:

 <a href="/publicprofile/marge">marge</a>, (<a href="drop/marge">drop</a>)
GET     /publicprofile/{name}                   PublicProfile.visit
  public static void visit(String name)
  {
  }  

Can you see how they are related? In the next step we will try to have some content displayed on these blank pages

Public Profile I

We have just made the following changes:

...
   <a href="/publicprofile/marge">marge</a>, (<a href="drop/marge">drop</a>)
...
   <a href="/publicprofile/lisa">lisa</a>, (<a href="drop/lisa">drop</a>)
...
# Public Profiles
GET     /publicprofile/{name}                   PublicProfile.visit
  public static void visit(String name)
  {
  }  

and browsing to

takes us to a blank page. How do we know if the name of the user got through? We can 'log' the call via a simple statement in the visit method:

  public static void visit(String name)
  {
    Logger.info("Just visiting the page for " + name);
  }

Save and refresh. The page will still be blank - but have a look at our 'console' - i.e. the command prompt from which you initially ran the app:

11:00:49,877 INFO  ~ Just visiting the page for lisa

This is where the output of any log messages go - useful for debugging your app.

Our page is still blank however. We can fix this with the following change:

  public static void visit(String name)
  {
    Logger.info("Just visiting the page for " + name);
    render();
  }

We are calling 'render()'

Try this now. You will get an error something like this:

Read the error message carefully - it is attempting to display a file called 'visit.html' from the PublicProfile folder. Back in eclipse, rename the index.html file in PublicProfile to 'visit.html'. (Rename option is available via the Refactor menu in Eclipse).

Save everything, and try to visit marge or lisa's page again. You should get this:

To recap, we introduced a call to a render() method in the UserProfile.visit() function:

  public static void visit(String name)
  {
    Logger.info("Just visiting the page for " + name);
    render();
  }

And then we made sure there was a file called 'visit.html' in the views/PublicProfile folder.

Public Profile II

How do we communicate from our java method:

  public static void visit(String name)
  {
    Logger.info("Just visiting the page for " + name);
    render();
  }

To our 'views/PublicProfile/visit.html' view?

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

<nav class="ui menu">   
  <a class="ui active item" href="/home">Back to Home</a> 
</nav>

<p>
  Welcome to the Public Profile!
</p>

Specifically, the name of the member we are visiting?

Try this. We can pass this as a parameter to render:

  public static void visit(String name)
  {
    Logger.info("Just visiting the page for " + name);
    render(name);
  }

The above fragment is a new version of the visit method in the UserProfile controller class. Look carefully at how we have changed the call to render() - and make this change in your own version.

In 'views/UserProfile/visit.html', we can pick it up in the header, or work it into the body:

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

<nav class="ui menu">   
  <a class="ui active item" href="/home">Back to Home</a> 
</nav>

<p>
  This is the profile page for ${name}
</p>

Make the above change to visit.html and visit Lisa and Marges pages and verify that the correct names are being displayed.

Note carefully the use of ${name} - this is substituted, when the page is assembled, by whatever value is passed to render.

User Profile III

How about displaying some content for marges and lisa's profile pages.

Open 'visit.html' - incorporate this content replacing the <p> element

<section class="ui raised segment">
  <div class="ui small header"> ${name}'s Profile</div>
  <section class="ui  two column grid segment">
    <div class="row">
      <div class="column">
        <div class="ui medium image">
          <img src=""> 
        </div>  
      </div>
      <div class="column">
        <h2> Messages </h2> 
        <ul> 
          <li>homer says..."What time is dinner?"</li>      
          <li>lisa says..."Where is my saxaphone?"</li>      
          <li>homer says..."Where are you?"</li>      
        </ul> 
      </div>
    </div>
  </section>    
</section>

Save and refresh the profile page for lisa and marge. Verify that the user name and list of messages appears. However, no images appear yet. Download and save these two images:

and save them in the public/images folder of your project. Now replace the <img> element in the page with the following:

    <img src="/public/images/${name}.gif"/> 

Verify now that we are getting the correct image when we visit these pages. The messages, however, don't change.

One last piece of tidying up to do. Our Routes file currently begins with this line:

# Landing page
GET     /                                       Application.index

Change this to the following:

# Landing page
GET     /                                       Home.index

Do you see the significance of this? Try some experiments with/without this change to see if you can understand the effect.

You can now delete Application.java and the Application view from the project

Exercises:

Archive of lab so far:

Exercise 1

Currently marge and lisa appear on the home page as 'friends'. Add bart and maggie. Make sure that, when visiting the pages, the correct images appear.

Exercise 2

Visit the members page - which shows a list of members + a 'follow' link. This is the source of members/index.html here:

...
<section class="ui segment">
  <h2 class="ui header">SpaceBook's Members</h2>
  <div class="ui list">
    <div class="item">
      <i class="right triangle icon"></i> 
      <div class="content">
        marge [<a href="home.html">follow</a>]
      </div>
    </div>
    <div class="item">
      <i class="right triangle icon"></i> 
      <div class="content">
        bart [<a href="home.html">follow</a>]
      </div>
    </div>
    <div class="item">
      <i class="right triangle icon"></i> 
      <div class="content">
        lisa [<a href="home.html">follow</a>]
      </div>
    </div>
    <div class="item">
      <i class="right triangle icon"></i> 
      <div class="content">
        maggie [<a href="home.html">follow</a>]
      </div>
    </div>
  </div>
</section>

Note that each of the follow links is incorrect, and should give you a routing error (try them). Using Step 5 as a guide, implement links in the above such that whenever the 'follow' link is selected, we log to the console the name of the member we want to follow.

Eg - the Terminal/Command prompt might show:

This will require that you create a new method in Java, and implement a call to the logger (as in step 5) . NB: This exercise will also require a changes to the 'conf/routes' file. Specifically, you will need to introduce a new route to support the follow method, very similar to the 'visit' route we have implemented earlier

Exercise 3

If you get Exercise 3 working, then turn your attention back to the list of friends on the home page.

What should the 'drop' link do? Implement something similar to the last exercise: i.e. have pressing 'drop' log a message to the console.

Do this as follows:

If all goes according to plan, clicking on the links should cause log message to appear. However, the user interface may take you to a blank page each time. Can you change this? Try calling "index();" in the drop method.

Can you explain what is happening?