Ch6 : Play Scala & html
[Scala in html]
Scala本身是可以編譯於JVM的程式語言,寫起來風格相似JAVA與JavaScript,另外它還擁有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,新增book的package,再來新增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)
@*
為了輸出結果美觀,增加if判斷式
若是相乘結果小於10,再多新增一個空白
*@
@if(number(row)(col)<10 ){
}
}
@* 印出一行結果後,換行 *@
<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/scala,scala的package需要自行新增後。再新增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
@*
Scala跟Java不同的地方在於
集合裡的符號表示不太一樣
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寫好一個Map,put幾本書名與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
基本上,JS或JQuery都可以呼叫到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內建的Session與Flash功能。
[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