03 Building the first screen

簡介

此章節準備要建立第一個應用程式的網頁。這個網頁是顯示最近所發佈的文章以及列示出舊文章的清單。以下是模擬網頁概況:






Bootstrapping with default data

為了在應用程式啟動前,預設載入fixture檔案注入至Blog中,必須建立一個Bootstrap job。
Play job是HTTP請求前必須額外執行的項目之一,舉例來說,應用程式執行前或特定時間使用一個CRON job。

建立/yabe/app/Bootstrap.java job將載入所使用的Fixtures預設資料

import play.*; import play.jobs.*; import play.test.*; import models.*; @OnApplicationStart public class Bootstrap extends Job { public void doJob() { // Check if the database is empty if(User.count() == 0) { Fixtures.load("initial-data.yml"); } } } 


@OnApplicationStart annotation告訴Play,我們想要在應用程式執行時,同步執行這向工作。事實上,job是不同的運行模式。在DEV模式下,Play等待第一個請求至開始。因此,job將同步執行在第一個請求上。如果作業失敗,會發出錯誤訊息至瀏覽器上。然而,PROD模式,job將應用程式被執行時(synchrously with the play run command),阻止該應用程式啟動的情況下的一個錯誤。

你必須建立一個initial-data.yml於yabe/conf/ directory。當然可以重複使用在 data.yml內容當中。現在,來Play run並顯示於網頁瀏覽器上, http://localhost:9000/ 。



The blog home page

接著,開始編輯home page。為了能夠列示文章清單並加以顯示,我們必須編輯controllers.Application.index()方法。然後,此方法調用render()並執行 /yabe/app/views/Application/index.html 模板。開啟/yabe/app/controllers/Application.java並引用於index()方法中撰寫能夠讀取文章清單的程式:

package controllers; import java.util.*; import play.*; import play.mvc.*; import models.*; public class Application extends Controller { public static void index() { Post frontPost = Post.find("order by postedAt desc").first(); List<Post> olderPosts = Post.find( "order by postedAt desc" ).from(1).fetch(10); render(frontPost, olderPosts); } } 


接著,可以看到如何傳遞物件至render方法??它將使我們能夠訪問它們從模板上並使用相同名稱。在這種情況下,使用參數frontPost及olderPosts將在模板裡。

打開 /yabe/app/views/Application/index.html及修改它並顯示這些物件參數:

#{extends 'main.html' /} #{set title:'Home' /} #{if frontPost} <div class="post"> <h2 class="post-title"> <a href="#">${frontPost.title}</a> </h2> <div class="post-metadata"> <span class="post-author">by ${frontPost.author.fullname}</span> <span class="post-date">${frontPost.postedAt.format('MMM dd')}</span> <span class="post-comments"> &nbsp;|&nbsp; ${frontPost.comments.size() ?: 'no'} comment${frontPost.comments.size().pluralize()}</a> #{if frontPost.comments} , latest by ${frontPost.comments[0].author} #{/if} </span> </div> <div class="post-content"> ${frontPost.content.nl2br()} </div> </div> #{if olderPosts.size() > 1} <div class="older-posts"> <h3>Older posts <span class="from">from this blog</span></h3> #{list items:olderPosts, as:'oldPost'} <div class="post"> <h2 class="post-title"> <a href="#">${oldPost.title}</a> </h2> <div class="post-metadata"> <span class="post-author"> by ${oldPost.author.fullname} </span> <span class="post-date"> ${oldPost.postedAt.format('dd MMM yy')} </span> <div class="post-comments"> ${oldPost.comments.size() ?: 'no'} comment${oldPost.comments.size().pluralize()} #{if oldPost.comments} - latest by ${oldPost.comments[0].author} #{/if} </div> </div> </div> #{/list} </div> #{/if} #{/if} #{else} <div class="empty"> There is currently nothing to read here. </div> #{/else} 


在此模板中,它允許你訪問Java objects dynamically。此模板下使用了Groovy,之所以會有如此漂亮的結構都是從Groovy而來(如: "?"、":"運算符)。但並不真的需要因此而學習Groovy,如果開發者已經熟悉JSP所使用的JSTL也可以將其靈活運用。現在,來重新整理一下Blog home page。




雖然看起來不是很好但它可以啟動了!!!


儘管如此,還是可以看到重複代碼。因為,我們顯示文章的方式為full, full with comment, teaser,我們應該建立一些類似的函數可以調用於多個模板上。這正是屬於Play tag!!!

要建立一個Tag,就新增/yabe/app/views/tags/display.html file。Tag只是一個模板。它具有參數(像一個函數)。#{display /} tag將有2個參數:Post object來顯示及顯示方式有home、teaser、full擇其一。

*{ Display a post in one of these modes: 'full', 'home' or 'teaser' }* <div class="post ${_as == 'teaser' ? 'teaser' : ''}"> <h2 class="post-title"> <a href="#">${_post.title}</a> </h2> <div class="post-metadata"> <span class="post-author">by ${_post.author.fullname}</span>, <span class="post-date">${_post.postedAt.format('dd MMM yy')}</span> #{if _as != 'full'} <span class="post-comments"> &nbsp;|&nbsp; ${_post.comments.size() ?: 'no'} comment${_post.comments.size().pluralize()} #{if _post.comments} , latest by ${_post.comments[0].author} #{/if} </span> #{/if} </div> #{if _as != 'teaser'} <div class="post-content"> <div class="about">Detail: </div> ${_post.content.nl2br()} </div> #{/if} </div> #{if _as == 'full'} <div class="comments"> <h3> ${_post.comments.size() ?: 'no'} comment${_post.comments.size().pluralize()} </h3> #{list items:_post.comments, as:'comment'} <div class="comment"> <div class="comment-metadata"> <span class="comment-author">by ${comment.author},</span> <span class="comment-date"> ${comment.postedAt.format('dd MMM yy')} </span> </div> <div class="comment-content"> <div class="about">Detail: </div> ${comment.content.escape().nl2br()} </div> </div> #{/list} </div> #{/if} 


接著使用這個Tag可以看見home page上沒有重複代碼:

#{extends 'main.html' /} #{set title:'Home' /} #{if frontPost} #{display post:frontPost, as:'home' /} #{if olderPosts.size()} <div class="older-posts"> <h3>Older posts <span class="from">from this blog</span></h3> #{list items:olderPosts, as:'oldPost'} #{display post:oldPost, as:'teaser' /} #{/list} </div> #{/if} #{/if} #{else} <div class="empty"> There is currently nothing to read here. </div> #{/else} 


重新整理頁面之後並檢查一切良好。



Improving the layout

接著,你會發現,index.html模版繼承了main.html。這是因為我們想要為所有BLOG的網頁提供所共用的佈局頁面,我們需要修改以下文件:

<!DOCTYPE html > <html> <head> <title>#{get 'title' /}</title> <meta http-equiv="Content-Type" content="text/html; charset=utf-8"/> <link rel="stylesheet" type="text/css" media="screen" href="@{'/public/stylesheets/main.css'}" /> <link rel="shortcut icon" type="image/png" href="@{'/public/images/favicon.png'}" /> </head> <body> <div id="header"> <div id="logo"> yabe. </div> <ul id="tools"> <li> <a href="#">Log in to write something</a> </li> </ul> <div id="title"> <span class="about">About this blog</span> <h1><a href="#">${blogTitle}</a></h1> <h2>${blogBaseline}</h2> </div> </div> <div id="main"> #{doLayout /} </div> <p id="footer"> Yabe is a (not that) powerful blog engine built with the <a href="http://www.playframework.org">Play framework</a> as a tutorial application. </p> </body> </html>


重新整理之後發現blogTitleblogBaseLine變數沒有顯示。這是因為我們沒有通過render(....)調用。當然,我們可以將它增加到render()調用在index action。
但由於main.html文件將被用來作為所有應用程式的主要模版,我們不想要每次都增加它們。

有一種方式來執行針對每個controller action之間相同的程式碼,定義@Before攔截器。讓我們在Application controller上面新增addDefaults()方法吧!!如下:

@Before static void addDefaults() { renderArgs.put("blogTitle", Play.configuration.getProperty("blog.title")); renderArgs.put("blogBaseline", Play.configuration.getProperty("blog.baseline")); }

你必須 import play.Play 在Application.java 檔案中。

所有變數若增加到renderArgs之中的話就可以直接在模板中獲取出來。你可以看到,該方法讀取變數值是從Play.configuration物件。這個物件包含了所有配置從 /yabe/conf/application.conf檔案。在此檔案新增這兩組key:

# Blog engine configuration # ~~~~~ blog.title=Yet another blog blog.baseline=We won't write about anything






Add Some Style

現在BLOG首頁快要完成了,但是它還不夠漂亮。我們必須新增一些樣式來讓它更好。正如你所看到的,主要模板main.html套入了/public/stylesheets/main.css樣式表。我們就來新增更多的風格吧。。

下載樣式表,並貼上至/public/stylesheets/main.css,在重新整理頁面一次吧。








Comments