Unit 05 - 引入資源, 顯示圖片及部分頁面更新(Ajax)

3 minute read

目標

  • 瞭解如何引入資源, 如 CSS, JS, 至 JSF 專案中
  • 瞭解如何顯示圖片
  • 動作後, 更新部分頁面

知識

JSF 專案中資源的目錄位置

JSF 專案中的 web 資源指 CSS, JavaScript, images 等. 這些 Web 資源必須放在 /resources 目錄之下. 此目錄又稱為 Library Root. 第二層的目錄為 Library Name.

在產生動態頁面時, 預設的 JSF Resource Handler 會讀取此目錄下的資源. 若不依照此慣例存放 Web 資源, 會導致 h:outputStylesheet, h:outputScript, h:graphImage 等 facelets 無法正常運作.

ResourceHandler

ResourceHandler 是一個執行期的 API, 用來取得 web 資源. UIComponent 或者 Renderer 實體皆會使用此 API 取得 JSF 專案中的資源. 某些的 Facelet 也會使用到 ResourceHandler.

在處理請求時, JSF Runtime 會指示瀏覽器去做後續的資源請求(resource requests). 此時, JSF Runtime 會呼叫 ResourceHandler 的方法取得資源的 URI. 瀏覽器會使用此 URI 在向應用程式伺服器取得此 URI 的資源 [3].

瀏覽器在請求資源時, JSF Runtime 會呼叫 ResourceHandler 的方法, 將資源的內容以串流的方式回應給瀏覽器. 瀏覽器便可以顯示圖片或取得資源 [3].

Facelets

JSF 的 Command Components 相關的 facelet 標籤:

  • h:outputStylesheet: 引入樣式表
  • h:outputScript: 引入 JS
  • h:graphImage: 載入圖片

Ajax

Facelet Ajax tag 為 f:ajax, 用來更新部分頁面. 此標籤有數個重要的屬性, 決定 Ajax 的行為:

  • event: 監聽的客戶端事件, 例如 keyup
  • execute: 要送回伺服器端處理的元件
  • render: 處理完成後, 要更新的頁面元件

event 屬性

要觸發 Ajax 請求(Ajax Request)的事件. 事件可以是 HTML 元件產生的 DOM event (瀏覽器上的事件) 或者是 JSF 元件的事件 (伺服器端事件).

支援的 DOM Event 有:

  • Mouse events: click, dbclick, mousedown, mouseup, mouseover
  • Keyboard events: keydown, keypress, keyup.

支援的 JSF 元件事件有:

  • action: 按下 h:commandButton, h:commandlink 等元件所產生的事件
  • valueChange: 可編輯資料之元件, 如 h:selectOneRadio, h:inputText 等, 產生的資料內容異動事件.

在底下範例中, 當使用者選擇不同的圖片選項時, 因為 h:selectOneRadio 的值改變, 產生 valueChange event. 此事件觸發 Ajax Request. 在此 Request 中, id 為 sor1 的元件被送到伺服器端處理, JSF Runtime 會更新 Managed Bean 的 property. 之後, 再更新 id 為 display 的頁面元素.

1
2
3
4
5
6
7
8
9
<h:form>
    <f:ajax event="valueChange" execute="sor1" render="display">
           <h:selectOneRadio id="sor1" value="#{handler.currentSelect}">
               <f:selectItem itemLabel="Image01" itemValue="img01.jpg"/>
               <f:selectItem itemLabel="Image02" itemValue="img02.jpg"/>
           </h:selectOneRadio>
    </f:ajax>
</h:form>
<h:graphicImage id="display" library="img" name="#{handler.currentSelect}"/>

execute 屬性

指定哪些 JSF 元件要放在 Ajax Request 中送到後端伺服器處理. execute 屬性內可以放多個元素的 ID, ID 間以空白分開. 也可以放以下的關鍵字, 指定提交元件範圍:

  • @none: 沒有任何的元件.
  • @this: 內有 f:ajax 的元件
  • @form: 表單內的元件
  • @all: 在頁面中的所有元件

render 屬性 指定要更新哪些元件, 在 Ajax Request 處理完成後. 屬性內可以放多個元素的 ID, ID 間以空白分開. 也可以放以下的關鍵字, 指定要更新的元件範圍[4]:

  • @none: 沒有任何的元件.
  • @this: 內有 f:ajax 的元件
  • @form: 表單內的元件
  • @all: 在頁面中的所有元件

User Story

User Story 1: Non-Ajac update

頁面上有兩個選項鈕, 供使用者選擇以顯示不同的圖片. 選擇完成後, 使用者按下 Show Image 按鈕, 在頁面下方顯示圖片內容.

User Story 2: Ajax update

頁面上有兩個選項鈕, 供使用者選擇以顯示不同的圖片. 選擇完成後, 系統馬上在下方顯示對應的圖片, 使用者不需要在按下 Show Image 按鈕.

實作程序

建立一個 Web Application 專案, 使用 JSF Framework. 依序完成以下步驟:

Step 在專案根目錄下, 建立 \resources\img 目錄.

Stepsource package 下建立 jsfBeans package.

Step 建立一個 Request Scoped Managed Bean:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
package jsfBeans;

import javax.annotation.PostConstruct;
import javax.enterprise.context.RequestScoped;
import javax.inject.Named;
import java.util.logging.Logger;

@Named(value = "handler")
@RequestScoped
public class ImageHandler {
    private String currentSelect;

    public ImageHandler() {
        currentSelect="img01.jpg";
    }

    @PostConstruct
    public void init(){
        Logger.getGlobal().info("Create ImageHandler");
    }

    public String getCurrentSelect() {
        return currentSelect;
    }

    public void setCurrentSelect(String currentSelect) {
        this.currentSelect = currentSelect;
    }

    public String showImage(){
        return null;
    }
}

Step 複製兩個 jpeg 檔案到 \resources\img, 名稱分別為 img01.jpgimg01.jpg.

Stepindex.xhtml 頁面加入 h:form 標籤.

Steph:form 標籤內,輸入以下的代碼:

1
2
3
4
5
6
7
 <h2>Non-Ajax Update</h2> <br/>
        <h:selectOneRadio value="#{handler.currentSelect}">
            <f:selectItem itemLabel="Image01" itemValue="img01.jpg"/>
            <f:selectItem itemLabel="Image02" itemValue="img02.jpg"/>
        </h:selectOneRadio>
        <br/>
        <h:commandButton action="#{handler.showImage}" value="Show Image"/>

Step 接著, 再輸入以下的代碼:

1
2
3
4
5
6
7
<h2>Ajax Update</h2>
    <f:ajax event="valueChange" execute="sor1" render="display">
        <h:selectOneRadio id="sor1" value="#{handler.currentSelect}">
            <f:selectItem itemLabel="Image01" itemValue="img01.jpg"/>
            <f:selectItem itemLabel="Image02" itemValue="img02.jpg"/>
        </h:selectOneRadio>
    </f:ajax>

Steph:form 的結尾標籤後方, 加入以下的程式碼, 顯示圖片:

1
 <h:graphicImage id="display" library="img" name="#{handler.currentSelect}"/>

Step 測試你的程式.

複習問題

  1. h:graphicImage 產生的資源 uri 的結構為何?
  2. h:graphicImagelibrary 屬性內的值對應到哪一個專案目錄?
  3. 有一個 JavaScript /resources/js/main.js. 要如何使用 facelet 引入到 JSF 專案中呢?

技術挑戰

請使用 JQuery 提供的 Tooltip 工具, 當滑鼠停留在選項時顯示提示文字. JavaScript 請使用 h:outputScript 標籤引入專案.

參考資料

[1] Chapter 5 JSF Configurations Using XML Files and Annotation. In Mastering JavaServer Faces 2.2.

[2] Oracle, 2014. Chapter 3 Creating JSF Pages Using Facelets. In JAVA EE 6: Develop Web Applications with JSF: Student Guide, vol 1.

[3] ResourceHandler (Java(TM) EE 7 Specification APIs)

[4] ajax (JSF 2.1 View Declaration Language: Facelets Variant)

[5] Tooltip in jQuery UI

Updated: