Unit 07 - 頁面樣板(JSF Templates)
目標
- 瞭解如何使用 JSF 樣板製作頁面, 使得每個頁面可以有相同的頁首, 頁尾或共同的顯示外觀。
- 如何在樣板中使用 JS
知識: 使用 JSF 樣板所需呀的 Facelets
樣板原理
- 樣板檔案(Template File): 做為頁面樣板的 Facelets Page. 頁面內預留數個區域, 供套用樣板時, 放置特定頁面的不同內容。
- 樣板客戶頁面(Template Client): 套用樣板頁面製作特定的頁面。只需在樣板內的預留區域, 填入特定內容, 產生一個特定的頁面。
Source: Figure 4-1 in [1]
Facelets 標籤
ui:composition
有兩個用途: 1) 在樣板客戶頁面中套用指定的樣板; 或者 2) 用來定義組合區塊。
組合區塊(Composition Block)讓我們模組化頁面中的元素, 以便在樣板頁面或者樣板客戶頁面中使用. 如此, 我們可以以需求組合不同的組合區塊。
當要套用樣板時, 使用 template
屬性指定樣板檔案。
例如:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
<h:body>
<ui:composition template="/templates/masterLayout.xhtml">
<ui:define name="contentArea">
<h3>Customer List</h3>
<ul>
<li>Spider Man</li>
<li>Captain Marvel</li>
<li>Hulk</li>
</ul>
</ui:define>
<ui:define name="navbar_script">
<script>
$(".nav").find(".active").removeClass("active");
$(".nav #customerItem").addClass("active");
</script>
</ui:define>
</ui:composition>
</h:body>
注意, ui:composition
facelet 外的全部內容會被忽略。
此例中, ui:define
facelet 定義樣板預留區域中的內容。
如果用於定義組合區塊, 則不需使用 template
屬性。
例如, 底下在 sideBarLeft.xml
定義一個 composition block.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
<h:head>
<title>Facelet Title</title>
</h:head>
<h:body>
<ui:composition>
<ul class="nav nav-stacked nav-pills">
<li><h:link class="btn btn-primary"
outcome="/customerList">
Customer List
</h:link> </li>
<li><h:link class="btn btn-primary"
value="Show Emails"
outcome="/emailList" /></li>
</ul>
</ui:composition>
</h:body>
我們可以在樣板頁面中引入此組合區塊:
1
2
3
4
5
6
<div id="sideBarLeft" class="col-xs-12 col-sm-12 col-md-3 col-lg-3">
<p>Left Side Bar</p>
<ui:insert name="sideBarLeft">
<ui:include src="/templates/sideBarLeft.xhtml"/>
</ui:insert>
</div>
ui:include
facelet 讓我們引入組合區塊, 製作頁面.
ui:decorate
:
使用樣板來裝飾頁面上特定的元素。
例如, 有一樣版檔案 content_title.xml
定義如下:
1
2
3
4
5
6
7
8
<ui:composition xmlns="http://www.w3.org/1999/xhtml"
xmlns:ui="http://xmlns.jcp.org/jsf/facelets"
xmlns:h="http://xmlns.jcp.org/jsf/html">
<h3>
<h:outputText value="#{title}"/>
</h3>
<ui:insert />
</ui:composition>
假若在頁面中有一個元素要被裝飾, 這個元素是一段文字, 則使用方式如下:
1
2
3
4
5
<ui:decorate template="templates/contentTitle.xhtml">
<ui:param name="title" value="內容標題"/>
點選左側的按鈕, 切換不同的頁面. 這些頁面有相同的: 上端導覽列, 左側導覽列.
只有在內容區域顯示不同的內容. (這些內容會被放到沒有名稱的 <code>ui:insert</code> facelet 中。
</ui:decorate>
如此, 這個元素上方會有一個 <h3>
的標籤, 內容由 ui:param
facelet 傳遞參數進行設定。
參考: decorate(JSF 2.2 View Declaration Language: Facelets Variant)
ui:insert
在樣版檔案上標示預留區, 供樣版客戶使用 ui:define
facelet 定義要顯示的特定內容。每個預留區上使用 name
屬性指定預留區的名稱。
使用 ui:define
定義內容時利用其 name
屬性指定要放置的預留區域。
例如, 底下的 ui:define
內的內容會放到樣板檔案的 ui:insert name='contentArea'
的預留區域中。
1
2
3
4
5
6
7
8
9
<ui:composition template="/templates/masterLayout.xhtml">
<ui:define name="contentArea">
<ui:decorate template="templates/contentTitle.xhtml">
<ui:param name="title" value="內容標題"/>
點選左側的按鈕, 切換不同的頁面. 這些頁面有相同的: 上端導覽列, 左側導覽列.
只有在內容區域顯示不同的內容. (這些內容會被放到沒有名稱的 <code>ui:insert</code> facelet 中。
</ui:decorate>
</ui:define>
</ui:composition>
ui:insert
可以用在以下的 facelet 內:
ui:composition
ui:component
ui:decorate
參考: insert (JSF 2.2 View Declaration Language: Facelets Variant)
ui:define
用來定義樣板內預留區的內容。
The ui:define
tag can be used inside <ui:composition template="template_name.xml">
and <ui:decorate template="template_name.xml">
參考: define (JSF 2.2 View Declaration Language: Facelets Variant)
ui:include
and ui:param
使用 ui:include
在標籤出現的地方, 引入外部的 Facelet Page 檔案 (xhtml)。檔案內使用 ui:composition
定義組合區塊。
組合區塊內的內容可以進行參數化, 在 ui:include
標籤內使用 ui:param
傳遞參數。在組合區塊中使用 EL 語法取得特定參數名稱的值。
Embed ui:param
tags in either ui:include
, ui:composition
, or ui:decorate
to pass the parameters.
參考:
- include (JSF 2.2 View Declaration Language: Facelets Variant)
- param (JSF 2.2 View Declaration Language: Facelets Variant)
User Story
- 使用者點選上方導覽列的選單項目, 切換不同的頁面. 頁面的內容顯示在右下方的區域。
- 使用者也可以點選左側的導覽列上的按鈕, 切換不同的頁面.
- 系統高亮度顯示選單項目, 指示目前所在的頁面名稱.
實作程序
Step 建立側邊欄的組合區塊。
在專案中建立目錄 /templates
, 並建立 sideBarLeft.xhtml
facelets page. 使用 ui:composition
facelet 建立如下的內容:
<?xml version='1.0' encoding='UTF-8' ?>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml"
xmlns:h="http://xmlns.jcp.org/jsf/html"
xmlns:ui="http://xmlns.jcp.org/jsf/facelets">
<h:head>
<title>Facelet Title</title>
</h:head>
<h:body>
<ui:composition>
<ul class="nav nav-stacked nav-pills">
<li><h:link class="btn btn-primary"
outcome="/customerList">
Customer List
</h:link> </li>
<li><h:link class="btn btn-primary"
value="Show Emails"
outcome="/emailList" /></li>
</ul>
</ui:composition>
</h:body>
</html>
這個 sidebar 內有兩個連結, 皆套用 bootstrap 的 btn
CSS 樣式。我們會在樣板頁面中使用此組合區塊, bootstrap 的樣式也會在樣板頁面中引入。
Step 建立內容標題的組合區塊。
建立 /templates/contentTitle.xhtml
facelet page, 用來顯示內容區域的標題, 檔案內容如下:
<ui:composition xmlns="http://www.w3.org/1999/xhtml"
xmlns:ui="http://xmlns.jcp.org/jsf/facelets"
xmlns:h="http://xmlns.jcp.org/jsf/html">
<h3>
<h:outputText value="#{title}"/>
</h3>
<ui:insert />
</ui:composition>
此 facelet page, 只有使用 ui:composition
facelet, 將 namespace 的宣告放在起始標籤內。這是合法的使用, 因為 JSF 會捨棄 ui:composition
標籤以外的內容。
此組合區塊有一個參數 title
, 使用此組合區塊時, 使用 ui:param
facelet 傳遞參數。
Step 建立樣板檔案。
建立 /templates/masterLayout.xhtml
做為主要的樣板檔案。在樣板檔案中會包括以下類型的內容:
- 樣板內容: 會在每個樣板使用頁面出現的內容. 樣板內容可以是一般的 HTML 元素、Facelet 標籤、或者組合區塊。
- 預留區塊: 使用
ui:insert
facelet 標示的預留區塊。預留區塊內也可以先放置一般內容或組合區塊。樣板使用頁面可以再定義預留區塊內的內容, 覆蓋原有的內容。
masterLayout.xhtml
的內容如下:
<?xml version='1.0' encoding='UTF-8' ?>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml"
xmlns:h="http://xmlns.jcp.org/jsf/html"
xmlns:ui="http://xmlns.jcp.org/jsf/facelets">
<h:head>
<title>Facelet Title</title>
<link rel="stylesheet"
href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/css/bootstrap.min.css"
integrity="sha384-BVYiiSIFeK1dGmJRAkycuHAHRg32OmUcww7on3RYdg4Va+PmSTsz/K68vbdEjh4u"
crossorigin="anonymous"></link>
<style>
body { padding-top: 70px; }
</style>
</h:head>
<h:body>
<nav class="navbar navbar-default navbar-fixed-top">
<div class="container-fluid" >
<div class="navbar-header">
<h:link styleClass="navbar-brand" outcome="/index">Unit07 ${siteName} </h:link>
</div>
<div>
<ul class="nav navbar-nav">
<li id="customerItem" class="active"><h:link outcome="/customerList">Customer List</h:link> </li>
<li id="emailItem"><h:link outcome="/emailList" >Email List</h:link> </li>
</ul>
</div>
</div>
</nav>
<div id="heading" class="container">
<div class="jumbotron">
<h1>JSF Templates</h1>
<p>
使用 JSF 樣板製作頁面, 使得每個頁面可以有相同的頁首, 頁尾或共同的顯示外觀.
</p>
</div>
</div>
<div class="container">
<div class="row">
<div id="sideBarLeft" class="col-xs-12 col-sm-12 col-md-3 col-lg-3">
<p>Left Side Bar</p>
<ui:insert name="sideBarLeft">
<ui:include src="/templates/sideBarLeft.xhtml"/>
</ui:insert>
</div>
<div id="content" class="col-xs-12 col-sm-12 col-md-9 col-lg-9">
<ui:insert name="contentArea">
<p>Content Area</p>
</ui:insert>
</div>
</div>
<div class="row">
<ui:remove>
<ui:decorate template="footer.xhtml" />
</ui:remove>
</div>
</div>
<!--javascripts-->
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.12.4/jquery.min.js"></script>
<script src="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/js/bootstrap.min.js" integrity="sha384-Tc5IQib027qvyjSMfHjOMaLkfuWVxZxUPnCJA7l2mCWNIpG9mGCD8wGNIcPD7Txa" crossorigin="anonymous"></script>
<ui:insert name="navbar_script" />
</h:body>
</html>
樣板檔案中, 使用 <ui:insert name="insert_area_name">
定義預留區塊。樣板檔案中也引入了所需的 CSS 及 JS。
Step 製作 index.xhtml
頁面。
此頁面使用<ui:composition template="/templates/masterLayout.xhtml">
標籤
套用 /templates/masterLayout.xhtml
樣板檔案建立頁面。
在樣板客戶頁面中, <ui:define name="contentArea">
定義樣板內預留區塊 <ui:insert name="contentArea">
內的內容。另外, 使用 <ui:decorate template=/templates/contentTitle.xhtml>
裝飾內容區域內的文字, 讓區域文字上方顯示標題。標題的內容透過 <ui:param name="title" value="內容標題">
設定組合區塊中的參數。
<ui:define name="navbar_script">
定義樣板檔案內的 `
<?xml version='1.0' encoding='UTF-8' ?>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml"
xmlns:h="http://xmlns.jcp.org/jsf/html"
xmlns:ui="http://xmlns.jcp.org/jsf/facelets">
<h:head>
<title>Facelet Title</title>
</h:head>
<h:body>
<ui:composition template="/templates/masterLayout.xhtml">
<ui:define name="contentArea">
<ui:decorate template="templates/contentTitle.xhtml">
<ui:param name="title" value="內容標題"/>
點選左側的按鈕, 切換不同的頁面. 這些頁面有相同的: 上端導覽列, 左側導覽列.
只有在內容區域顯示不同的內容. (這些內容會被放到沒有名稱的 <code>ui:insert</code> facelet 中。
</ui:decorate>
</ui:define>
<ui:define name="navbar_script">
<script>
$(".nav").find(".active").removeClass("active");
</script>
</ui:define>
</ui:composition>
</h:body>
</html>
Step 製作 customerList.xhtml
頁面。
此頁面同樣<ui:composition template="/templates/masterLayout.xhtml">
標籤
套用樣板檔案建立頁面。
<ui:define name="contentArea">
定義樣板內預留區塊 <ui:insert name="contentArea">
的內容, 顯示顧客清單。<ui:define name="navbar_script">
內放入 JS code, 更新上方導覽列的高亮度顯示選單項目, 表示目前在 customerList.xhtml
頁面。
<?xml version='1.0' encoding='UTF-8' ?>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml"
xmlns:h="http://xmlns.jcp.org/jsf/html"
xmlns:ui="http://xmlns.jcp.org/jsf/facelets">
<h:head>
<title>Facelet Title</title>
</h:head>
<h:body>
<ui:composition template="/templates/masterLayout.xhtml">
<ui:define name="contentArea">
<h3>Customer List</h3>
<ul>
<li>Spider Man</li>
<li>Captain Marvel</li>
<li>Hulk</li>
</ul>
</ui:define>
<ui:define name="navbar_script">
<script>
$(".nav").find(".active").removeClass("active");
$(".nav #customerItem").addClass("active");
</script>
</ui:define>
</ui:composition>
</h:body>
</html>
Step 製作 emailList.xhtml
頁面。
此頁面的製作和前一步驟的頁面做法相同。
<?xml version='1.0' encoding='UTF-8' ?>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml"
xmlns:h="http://xmlns.jcp.org/jsf/html"
xmlns:ui="http://xmlns.jcp.org/jsf/facelets">
<h:head>
<title>Facelet Title</title>
</h:head>
<h:body>
<ui:composition template="/templates/masterLayout.xhtml">
<ui:define name="contentArea">
<h3>Email List</h3>
<ul>
<li>spider-man@gmail.com</li>
<li>caption-marvel@gmail.com</li>
<li>hulk@gmail.com</li>
</ul>
</ui:define>
<ui:define name="navbar_script">
<script>
$(".nav").find(".active").removeClass("active");
$(".nav #emailItem").addClass("active");
</script>
</ui:define>
</ui:composition>
</h:body>
</html>
Step 測試你的程式。
複習問題
- JSF 頁面樣板的工作原理為何?
ui:composition
facelat 有那兩個用途?- 組合區塊(composition)
comblock
中 留有一參數title
, 要如何設定此參數呢? ui:composition
和ui:decorate
兩個 facelet 有什麼差異呢?
技術挑戰
參考資料
[1] Burns, E. and Schalk, C., JavaServer Faces 2.0: the complete reference. McGraw Hill, 2010.
[2] Overview (JSF 2.2 View Declaration Language: Facelets Variant) , accessed on 2018/10/29