Ch6 : Play Scala & html

[Scala in html]
Scala本身是可以編譯於JVM的程式語言,寫起來風格相似JAVAJavaScript,另外它還擁有type inference(型態推論)與Functional programming(函數程式)的優勢,會讓Scala語言寫起來比JAVA更為簡潔,可以參考Reference的介紹,會有更多說明。下面就開始介紹Play使用Scala[name].scala.html上的操作,範例會比之前量多很多,需要多多練習。


專案參考 : https://github.com/loveu8/playMyBtaisMariaDB/tree/Ch6


To be, or not to be , that is the question. by William Shakespeare


開始之前,我們跟之前一樣新增一隻controller來練習這次的內容。

app/conf新增ScalaController來練習。

package controllers;

import play.mvc.Controller;

public class ScalaController extends Controller{

}

Play Scala with String
回傳簡單的字串回到頁面上。
app/controller/ScalaController.java

public Result String() {
    // 這邊單純回傳字串到網頁上
    return ok("String!!");
}

conf/routes

# http://127.0.0.1:9000/scala/string 
GET     /scala/string                 controllers.ScalaController.String()

Request

GET : http://127.0.0.1:9000/scala/string

Response

Hi

Play Scala with variable
我們可以在頁面宣告scala variable(變數)來使用。

app/controller/ScalaController.java

// 畫面上寫變數
public Result varPage(){
    return ok(varPage.render());
}

app/views/book,新增bookpackage再來新增varPage.scala.html

@*
 這邊我們可以發現到三件事

 1.如果要import物件時,寫法跟Java一樣
   當有寫好公用工具時,也可以這樣去呼叫
 2.宣告變數時,一定要寫在同一行,而不能寫分段寫
   var 宣告,意思變數是可以被修改的
 3.如果要在頁面上使用變數或是判斷式等等
   前面要使用小老鼠符號,才可以順利使用

*@

@import java.math.BigInteger; var java_v=0
@import java.lang.String; var name = "Tom"

Java 變數          : @java_v         <br>
Java 變數 + 1    : @(java_v+1)    <br>
<hr>
Java 字串        : @name                 <br>
Java 字串加長    : @(name + " Clancy  湯姆克蘭西:全境封鎖")    <br>

conf/routes

# http://127.0.0.1:9000/scala/varPage     
GET     /scala/varPage                controllers.ScalaController.varPage()

Request

GET : http://127.0.0.1:9000/scala/varPage

Response


Play Scala with method
當頁面上有重覆性程式碼過多時,或者是邏輯過於複雜時,可以考慮在頁面宣告method(方法),來抽離過於複雜的部份,等需要時再呼叫,可增加程式可讀性與維護性。

app/controller/ScalaController.java

// 畫面上寫Scala方法
public Result method(){
    return ok(method.render());
}

app/views/book,再來新增method.scala.html

@import java.math.BigInteger; var index = 0

@* 我們可以設定一個method來被呼叫到 
   裡面接收的參數,需要指定物件型態  
   每次被呼叫到就+1
   方法結束後,也需要回傳結果回去*@   
@count(temp_index :Int ) = @{
  var result = 0 
  result = temp_index + 1 
  result
}

@for(timer <-0 until 10){
    @count(timer) <br>
}

conf/routes

# http://127.0.0.1:9000/scala/method     
GET     /scala/method                 controllers.ScalaController.method()

Request

GET : http://127.0.0.1:9000/scala/method

Response

1
2
3
4
5
6
7
8
9
10

Play Scala with Array
陣列是常見表達資料的一種方式,以下我們簡單傳遞一個二維陣列變數,丟到畫面上顯示結果。以下示範經典範例之一,九九乘法表。

app/controller/ScalaController.java

// 99乘法表,計算出結果後放入陣列
public Result arrayInt(){
    int[][] numbers = new int[10][10];
    for(int row = 1 ; row < 10 ; row ++){
        for(int col = 1 ; col < 10 ; col++){
            numbers[row][col] = row * col;
        }
    }
    return ok(arrayInt.render(numbers));
}

app/views/book新增arrayInt.scala.html

@* 
   這是 Scala 二維陣列範例
   稍微跟Java表示方式不太一樣
   最內層代表陣列的基本型態是Int正整數
*@
@(number : Array[Array[Int]]) 

@* 
  這是迴圈的基本寫法 , 從1開始計數到10,總共9次
  兩層迴圈就是 9 * 9 , 總共會執行81次
*@
@for(row <-1 until 10){
    @for(col <-1 until 10){

        @* 這邊為了排版好看,而多新增一個空白 
           Play Scala本身能分辨出是變數
           所以變數就算跟符號或文字相鄰近
           也不會判斷錯誤 *@
        @col * @row = @number(row)(col) &nbsp;

        @* 
           為了輸出結果美觀,增加if判斷式
           若是相乘結果小於10,再多新增一個空白
        *@

        @if(number(row)(col)<10 ){
            &nbsp;
        }
    }
    @* 印出一行結果後,換行 *@
    <br>
}

conf/routes

# http://127.0.0.1:9000/scala/arrayInt     
GET     /scala/arrayInt               controllers.ScalaController.arrayInt()

Request

GET : http://127.0.0.1:9000/scala/arrayInt

Response


Play Scala with Java Object
我們一定會期望可以在頁面上操作我們要使用的物件等等。以下示範傳遞一個簡單的JAVA物件到頁面上,印出物件的內容,這邊我們用Book.java來做範例。

app/pojo/scalascalapackage需要自行新增後。再新增Book.java類別,裡面有兩個屬性。Name(書名)與ISBN

package pojo.scala;

public class Book {
    public String name;

    public String isbn;

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public String getIsbn() {
        return isbn;
    }

    public void setIsbn(String isbn) {
        this.isbn = isbn;
    }
}

app/views/book,新增oneBook.scala.html網頁檔案,簡單顯示書名。

@* 
  對應到pojo scala Book類別
  物件的路徑需要正確,才可以正確被呼叫到
*@

@(book : pojo.scala.Book) 

書名     : @book.getName()<br>
ISBN     : @book.getIsbn()

app/controller/ScalaController.java

import pojo.scala.Book;
import views.html.book.*;
...
public Result oneBook() {
    Book book = new Book(); 
    book.setName("被討厭的勇氣");
    book.setIsbn("9789861371955");
    return ok(oneBook.render(book));
}

conf/routes

# http://127.0.0.1:9000/scala/oneBook     
GET     /scala/oneBook                controllers.ScalaController.oneBook()

Request

GET : http://127.0.0.1:9000/scala/oneBook

Response

書名 : 被討厭的勇氣
ISBN : 9789861371955



Play Scala with List
接下來我們來練習有多本書的情況下,應該怎樣在畫面印出書本資料。首先在Controller寫好三本書名與ISBN內容後,回傳JAVA List到頁面上,而List裡面包含多個Book物件,再傳送到scala.html頁面,跟迴圈方式,印出List裡每書本的資料。

app/controller/ScalaController.java

// 多本書
public Result listBook() {
    ArrayList<Book> books = new ArrayList<Book>();

    Book book1     = new Book(); 
    Book book2     = new Book(); 
    Book book3     = new Book(); 

    book1.setName("Docker錦囊妙計");
    book1.setIsbn("9789864760800");

    book2.setName("Python 3.5 技術手冊");
    book2.setIsbn("9789864761265");

    book3.setName("使用者故事對照|User Story Mapping");
    book3.setIsbn("9789863479468");

    books.add(book1);
    books.add(book2);
    books.add(book3);

    return ok(listBook.render(books));
}

app/views/book新增listBook.scala.html

@* 
  ScalaJava不同的地方在於
  集合裡的符號表示不太一樣
  Java  : ArrayList<Book>() 
  Scala : ArrayList[pojo.scala.Book]
  差異在一個是用大於小於<xxx>,把指定的物件類型包起來
  Scala適用中括號[xxx],把指定物件包起來
  ---
  對應到pojp scala Book類別
*@
@(books : ArrayList[pojo.scala.Book]) 

@*
  這是 Scala 使用foreach迴圈的範例,意思是指
  把List裏的books指定給book變數
  我們再印出每本書的內容
*@
@for(book <- books) {
  書名     : @book.getName()<br>
  ISBN     : @book.getIsbn()<hr>
} 
<br><br>
@*
  這是 Scala 使用for迴圈的範例
  指定一個變數index,從0開始到總共幾本書的長度
  這邊可以寫 books.length
  或者也可以寫成 books.size() 都是可以的
*@
@for(index <- 0 until books.length){
  書名     : @books(index).getName()<br>
  ISBN     : @books(index).getIsbn()<hr>
}

conf/routes

# http://127.0.0.1:9000/scala/listBook     
GET     /scala/listBook               controllers.ScalaController.listBook()

Request

GET : http://127.0.0.1:9000/scala/listBook

Response


Play Scala with Map
Map物件類型,是很常見的集合物件,是以key-value方式儲存資訊。我們在controller寫好一個Mapput幾本書名與ISBN字串後,傳送到scala.html頁面,顯示每書本資料。

app/controller/ScalaController.java

// Map書
public Result mapBook(){
    HashMap <String , String> books = new HashMap<String , String>();
    books.put("one", "Docker錦囊妙計 - 9789864760800");
    books.put("two", "Python 3.5 技術手冊 - 9789864761265");
    books.put("three", "使用者故事對照|User Story Mapping - 9789863479468");
    return ok(mapBook.render(books));
}

app/views/book新增mapBook.scala.html

@** 傳遞Map物件 **@

@(mapBook : Map [String , String]) 

@*
  這是Scala使用foreach迴圈範例,
  利用key value方式取出mapBook裏的資料
  利用這樣的方式,取出Map裡的資料
*@

@*利用這樣的方式,取出Map裏的資料*@
@for((key, value) <- mapBook) { 
    <a>Key = @key , 書名 = @mapBook(key)</a><hr>
}

conf/routes

# http://127.0.0.1:9000/scala/mapBook     取得ScalaController.mapBook回傳結果
GET     /scala/mapBook                controllers.ScalaController.mapBook()

Request

GET : http://127.0.0.1:9000/scala/mapBook

Response


Play Scala with if else
寫到這邊,你一定還發現到,最常使用的if else條件判斷式,還沒有說明過,這邊簡單來嘗試看看吧!

app/controller/ScalaController.java

// 多本書,含有空資料
public Result listEmptyBook() {
    ArrayList<Book> books = new ArrayList<Book>();

    Book book1     = new Book(); 
    Book book2     = new Book(); 
    Book book3     = new Book(); 

    book1.setName("Docker錦囊妙計");
    book1.setIsbn("9789864760800");

    book3.setName("使用者故事對照|User Story Mapping");
    book3.setIsbn("9789863479468");

    books.add(book1);
    books.add(book2);
    books.add(book3);

    return ok(listEmptyBook.render(books));
}

app/views/book新增listEmptyBook.scala.html

@(books : ArrayList[pojo.scala.Book]) 

@for(book <- books) {
  @* 如果書本不是空的,且有書名與ISBN才顯示內容*@
  @if(book!=null && book.getName() !=null && book.getIsbn()!=null){
      書名     : @book.getName()<br>
      ISBN     : @book.getIsbn()<hr>
  } else {
      @* 沒有資料的話,會跑else其他類型狀況 *@
        查無書本資料<hr>
  }
}

conf/routes

# http://127.0.0.1:9000/scala/listEmptyBook     取得ScalaController.listEmptyBook回傳結果
GET     /scala/listEmptyBook          controllers.ScalaController.listEmptyBook()

Request

GET : http://127.0.0.1:9000/scala/listEmptyBook

Response


Play Scala with Html content
如果我們在controller回傳一段Html語法時,我們可以在Scala頁面使用@Html()方式,來呈現出網頁效果。
app/controller/ScalaController.java

// 傳送網頁文字
public Result showHtml() {
    // 使用H1的文字格式,指定內容是Html頁面,編碼使用UTF-8
    return ok("<h1>測試網頁文字!</h1>", "UTF-8").as("text/html; charset=UTF-8");
}

app/views/book新增showHtml.scala.html

@(htmlContent:String)

@* 這邊可以使用@Html方式
   把內容轉換成網頁語言顯示 *@

@Html(htmlContent:String)

conf/routes

# http://127.0.0.1:9000/scala/showHtml     
GET     /scala/showHtml                controllers.ScalaController.showHtml()

Request

http://127.0.0.1:9000/scala/showHtml

Response


Play Scala with Switch
Switch是常見的條件判斷式方法之一,用途是當符合判斷式時,指定執行ㄧ些需要執行的結果。以下會混合CSS語法來一起示範Scala怎樣寫Switch條件判斷式。

app/controller/ScalaController.java

// 在頁面上寫Switch
public Result testSwitch() {
    return ok(testSwitch.render());
}

app/views/book新增testSwitch.scala.html

@*
   把num數字轉換成字串
   定義color來接收numStr判斷後的結果
   依序回傳紅橙黃綠藍靛紫
   最後一個判斷是,當沒有符合條件時
   預設進入的判斷式
*@

@backgroundColor(num : Int) = @{

  var numStr = num.toString    

  var color = numStr  match {
      case "0" => "background:#FF0000"    
      case "1" => "background:#FF4500"    
      case "2" => "background:#FFFF00"    
      case "3" => "background:#008000"    
      case "4" => "background:#0000FF"    
      case "5" => "background:#1E90FF"    
      case "6" => "background:#800080"    
      case  _  => "background:white"    
  }
  color
}

@for(index <-0 until 8){
    <div style="@backgroundColor(index);">@index</div>
}

conf/routes

# http://127.0.0.1:9000/scala/testSwitch     
GET     /scala/testSwitch                controllers.ScalaController.testSwitch()

Request

http://127.0.0.1:9000/scala/testSwitch

Response


Play Scala include Static File
當我們有靜態圖片、網頁,或者一些JS等等,需要在頁面上使用到時,我們可以用Play assets的方式,去取得靜態網頁或圖片、JS等等資源。Play assets會去專案的public目錄下尋找相關的檔案。接下來會示範怎樣可以取得網頁、圖片,跟引用JS
app/controller/ScalaController.java

// 測試引用靜態檔案
public Result testStatic() {
    return ok(testStatic.render());
}

app/views/book新增testStatic.scala.html

<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>靜態引用測試頁面</title>
</head>

<body>

<h3>測試JS</h3>
<input id="testJS"></input>
<script src='@routes.Assets.versioned("javascripts/static.js")'></script> 

<hr>

<h3 class="testCss">測試CSS</h3>
<link rel="stylesheet" type="text/css" href='@routes.Assets.versioned("stylesheets/static.css")'>

<hr>

<h3>測試圖片</h3>
<img src='@routes.Assets.versioned("images/static.png")' > 

<hr>

<h3>被引用頁面</h3>
<a href='@routes.Assets.versioned("html/static.html")'>點我!</a>

</body>

</html>

public,依照目錄功能,新增了4個類型檔案。

public/html/static.html

<h2>測試靜態頁面</h2>

public/images/static.png。可隨意測試放入自己喜愛的圖片。
public/stylesheets/static.css

.testCss {
    background-color: red;
}

public/javascripts/static.js

document.getElementById('testJS').value = '呼叫到的JS';

conf/routes

# http://127.0.0.1:9000/scala/testStatic    
GET     /scala/testStatic                controllers.ScalaController.testStatic()

Request

http://127.0.0.1:9000/scala/testStatic

Response

我們檢視網頁原始碼後,就可以發現到Play可以幫我們主動轉換成相對路徑,就樣我們靜態相關檔案就可以使用,要特別注意就是,因為在public目錄下,代表外部可以看到需要的檔案,要注意檔案跟安全性問題,以免造成網站漏洞。

<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>靜態引用測試頁面</title>
</head>

<body>

<h3>測試JS</h3>
<input id="testJS"></input>
<script src='/assets/javascripts/static.js'></script> 

<hr>

<h3 class="testCss">測試CSS</h3>
<link rel="stylesheet" type="text/css" href='/assets/stylesheets/static.css'>

<hr>

<h3>測試圖片</h3>
<img src='/assets/images/static.png' > 

<hr>

<h3>被引用頁面</h3>
<a href='/assets/html/static.html'>點我!</a>

</body>

</html>

Include other html
Play Scala頁面本身可以包含其他需要被引用的頁面。我們可以傳遞相關物件到主頁面後,再從主頁面傳遞相關物件到子頁面上,呈現出我們想要表達的畫面。這種比較適和使用率高且重覆的頁面,建議可以變成公用檔案,隨時可以被引用到。像一般網站常見的Header或者是Footer,就很適合這樣的作法。

app/controller/ScalaController.java

// 測試傳送參數到子頁面
public Result includePage() {
    return ok(mainPage.render("我是子頁面"));
}

app/views/book新增mainPage.scala.html

@(passString:String)

<h3>我是主頁面</h3>
<hr>

@* 傳遞字串給子頁面 *@
@views.html.book.childPage(passString)

app/views/book新增childPage.scala.html

@(passString:String)

<h5>@passString</h5>

conf/routes

# http://127.0.0.1:9000/scala/includePage    
GET     /scala/includePage            controllers.ScalaController.includePage()

Request

http://127.0.0.1:9000/scala/includePage

Response


JavaScript call Play
基本上,JSJQuery都可以呼叫到Play的服務,也可以用來抓取Play的靜態網頁或圖片,都是做得到的。以下來練習,應該怎樣寫,才能去取得我們我們Play寫好的服務

app/controller/ScalaController.java

// JS主要呼叫頁面
public Result jsCaller() {
    return ok(jsCaller.render());
}

// JS呼叫Play的內容
public Result playCaller() {
    return ok("我被呼叫到了!!");
}

app/views/book新增jsCaller.scala.html

<input id = 'playCaller'></input>

<script>
    // 組出要被呼叫到的url ex : http://127.0.0.1:9000/scala/playCaller
    // request.host = 127.0.0.1:9000/
    // controllers.routes.ScalaController.playCaller.url = scala/playCaller    
    // 使用@符號與小括號包起來得範圍,代表都是scala語言作用範圍
    var content = httpGet('@("http://" + request.host + controllers.routes.ScalaController.playCaller.url)');

    document.getElementById('playCaller').value = content;

    // JS http GET 取得網頁內容
    function httpGet(theUrl){
      var xmlHttp = null;
      xmlHttp = new XMLHttpRequest();
      xmlHttp.open( "GET", theUrl, false );
      xmlHttp.send( null );
      return xmlHttp.responseText;
    }

</script>

conf/routes

# http://127.0.0.1:9000/scala/jsCaller    
GET     /scala/jsCaller               controllers.ScalaController.jsCaller()

# http://127.0.0.1:9000/scala/playCaller    
GET     /scala/playCaller             controllers.ScalaController.playCaller()

Request

http://127.0.0.1:9000/scala/jsCaller

Response


[Final]
以上的內容,就是基本網站上常用到的功能,可以應付9成以上的需求,因為這次範例較多,可以多加練習看看,你能是否能正確呼叫到自己寫好的服務!!下個章節簡單介紹Play內建的SessionFlash功能。


[Reference]
1.Scala vs Java:兩者間的差異與相似處
http://www.openfoundry.org/tw/tech-column/9131-scala-vs-java

2.how-to-define-variable-in-play-scala-template
http://stackoverflow.com/questions/33623985/how-to-define-variable-in-play-scala-template

3.play-framework-2-2-get-url-of-the-requesting-page
http://stackoverflow.com/questions/24179874/play-framework-2-2-get-url-of-the-requesting-page

4.return-html-content-as-a-string-given-url-javascript-function
http://stackoverflow.com/questions/10642289/return-html-content-as-a-string-given-url-javascript-function

results matching ""

    No results matching ""