2011年6月30日 星期四

Play Framework 實驗筆記 4- 使用jqGrid 作資料維護

這一部分是將前端表格顯示的部份作改善。
使用的元件是jqGrid。這是jqGrid的Demo,裡面有許多使用範例,有興趣的可以去看一下,這邊只會用到以下這些簡單的功能。

1. 使用AJAX 的方式,向Server取得 JSON 格式的資料
2. 支援常見的分頁方式
3. 編輯時使用 inline editor 的方式

檔案位置:

檔案要放對位置
*.css 放在 N:\PlayFramework\play-1.1\members\public\stylesheets
css中用到的圖檔放在 N:\PlayFramework\play-1.1\members\public\stylesheets\images


*.js 放在 N:\PlayFramework\play-1.1\members\public\javascripts

View
View 的部份要將相關的js 檔include進來。我決定直接加在main.html
N:\PlayFramework\play-1.1\members\app\views\main.html
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">

    <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="stylesheet" type="text/css" media="screen" href="@{'/public/stylesheets/jquery-ui-1.8.12.custom.css'}">
  <link rel="stylesheet" type="text/css" media="screen" href="@{'/public/stylesheets/ui.jqgrid.css'}">
  
        #{get 'moreStyles' /}
        <link rel="shortcut icon" type="image/png" href="@{'/public/images/favicon.png'}">
        <script src="@{'/public/javascripts/jquery-1.4.2.min.js'}" type="text/javascript" charset="utf-8"></script>
        <script src="@{'/public/javascripts/jquery-ui-1.7.2.custom.min.js'}" type="text/javascript"></script>
  <script src="@{'/public/javascripts/grid.locale-cn.js'}" type="text/javascript"></script>
  <script src="@{'/public/javascripts/jquery.jqGrid.min.js'}" type="text/javascript"></script>

  #{get 'moreScripts' /}
    </head>
    <body>
           #{if flash.error}
            <p class="error">${flash.error}</p>
        #{/if}
        #{if flash.success}
            <p class="success">${flash.success}</p>
        #{/if}
        
        <div id="main">
            #{doLayout /} 
        </div>
    </body>
</html>

和controller對應的View要加入jqGrid元件

N:\PlayFramework\play-1.1\members\app\views\EnhanceMemberOperation\index.html
#{extends 'main.html' /}
#{set title:'Home' /}

Member Data
<table id="scroll37"></table>
<div id="pscroll37" ></div>

<script type="text/javascript">
 
jQuery.fn.log = function(msg) {
    if (typeof console != "undefined")
        console.log("%s: %o", msg, this);
    return this;
};
 
 var lastsel2

jQuery("#scroll37").jqGrid({
    url:'@{memberList()}',
 datatype: "json",
 height: 400,
 width: 800,
 editable: true,
    colNames:['ID','firstName', 'lastName','gender', 'email'],

 colModel:[
     {
        name:'memberid',
        index:'memberid',
        align : "right",
        width : 50, 
        required : true,
  hidden: true, 
  editable: false, 
  editrules: { edithidden: false }, hidedlg: true
      },{
        name:'firstName',
        index:'firstName',
        width:100,
        editable : true,
        required : true,
  editrules:{required:true}
      },{
        name:'lastName',
        index:'lastName',
        width:100,
        editable : true,
        required : true
      },{
        name:'gender',
        index:'gender',
        width:50, 
        editable : true,
        edittype : 'select',
        editoptions:{value:"1:男;2:女"},
  formatter:function(cellvalue, options, row){
       if(cellvalue==1 || cellvalue=="男" ){
    return "男";
      }else{
          return "女";
     }
  },

        required : true
      },{
        name:'email',
        index:'email',
        width:150, 
        editable : true,
        required : true
      }
    ],
    rowNum:20,
 rowTotal: 100,
 rowList : [20,30,50],
 //scroll:2,
 headertitles: false,
 loadonce:false,
    mtype: "GET",
 editurl: '@{memberEdit()}', 
 rownumbers: true,
 rownumWidth: 40,
 gridview: true,
    pager: '#pscroll37',
    sortname: 'id',
    viewrecords: true,
    sortorder: "asc",
 caption: "Member Data",


 onSelectRow: function(id){
 
      if(id && id!==lastsel2){
 
        jQuery('#scroll37').saveRow(lastsel2);
  $("#scroll37").jqGrid('setGridParam', {editurl:'@{memberEdit()}?memberId='+$('#scroll37').jqGrid('getCell',id, 1)})
  jQuery('#scroll37').editRow(id,true);
        lastsel2=id;
      }
   
    }

});

jQuery("#scroll37").jqGrid('navGrid','#pscroll37',{del:true,add:true,edit:true});

</script>


Controller
由於新增了ajax的功能,所以controller 的部份要使用 renderJSON() 作輸出
N:\PlayFramework\play-1.1\members\app\controllers\EnhanceMemberOperation.java

package controllers;

import java.util.ArrayList;
import java.util.Hashtable;
import java.util.List;

import models.Member;
import models.MemberCard;
import play.data.validation.Required;
import play.mvc.Controller;

public class EnhanceMemberOperation extends Controller {
    public static void index() { 
        render();
   }

    public static void memberEdit(@Required String oper, long memberId,String firstName,String lastName ,Integer gender, String email) {
     if(validation.hasErrors()) {
            params.flash(); // add http parameters to the flash scope
            validation.keep(); // keep the errors for the next request
            System.out.println("ERROR: "+ validation.errors());
            index();
        }
        if (oper.equalsIgnoreCase("add")){
         new Member(firstName,lastName,gender,email).insert();
        }else if (oper.equalsIgnoreCase("del")){
         Member member = Member.findById(memberId);
         member.delete();
        }else if (oper.equalsIgnoreCase("edit")){
         Member member = Member.findById(memberId);
         member.firstName = firstName;
         member.lastName = lastName;
         member.gender = gender;
         member.email = email;
         member.update();
        }
        index();
    }
    
    public static void memberList(int page, int rows ,int sidx ,int  sord ) {

      System.out.println("進入memberList表單" +  params.get("page")+":" + params.get("rows"));
      int totalRecords= Member.all().count();
     int totalPage = (int) java.lang.Math.ceil(new Double(totalRecords) / new Double(rows));
     System.out.println("totalPage=" +  totalPage +" totalRecords=" + totalRecords);
     Hashtable ht = new Hashtable();
  ArrayList al = new ArrayList();
  
  int start = (page - 1) * rows;
  List<Member> members = Member.all().fetch(rows,start);
  for (int i=0;i<members.size();i++){
   Hashtable rowdata = new Hashtable();
   ArrayList columns = new ArrayList();
   columns.add(members.get(i).id);
   columns.add(members.get(i).firstName);
   columns.add(members.get(i).lastName);
   columns.add(members.get(i).gender);
   columns.add(members.get(i).email);
   rowdata.put("id",i);
   rowdata.put("cell",columns);
   al.add(rowdata);
  }

  ht.put("page",page);
  ht.put("total",totalPage);
  ht.put("records",totalRecords);
  ht.put("rows",al);
        renderJSON(ht);
      
    }

    
    public static void memberDelete(Long id) {
     Member member = Member.findById(id);
        notFoundIfNull(member); 
        member.delete();
        flash.success("The member %s has been deleted", member.firstName  );
        index();
    }
}



執行結果如下圖:

2011年6月26日 星期日

Play Framework 實驗筆記 3- Basic Data Operation

這篇文章將實做第一個資料庫互動的範例。
主要是針對Member 作所謂的 Add, Delete, Inquiry, Update. 並將所有動作整合到同一個頁面。

1. Controller

新增BasicMemberOperation.java 在對應的目錄下,這個是要當Controller用的,網頁的每一個動作都對應到Controller裡面的某個method。 最後透過Index呼叫PlayFramework作render。

N:\PlayFramework\play-1.1\members\app\controllers\BasicMemberOperation.java
package controllers;

import java.util.List;

import models.Member;
import models.MemberCard;
import play.data.validation.Required;
import play.mvc.Controller;

public class BasicMemberOperation extends Controller {
    public static void index() { 
        List members = Member.getall(); // get all member data
        render(members);
   }
    
 
    //將選取的資料傳回表單
    public static void getDataForEdit(Long id) {
     Member member = Member.findById(id);
        notFoundIfNull(member);
        flash.put("id", member.id);
        flash.put("firstName", member.firstName);
        flash.put("lastName", member.lastName);
        flash.put("email", member.email);
        index();
    }
    
    //delete member 
    public static void delete(Long id) {
     Member member = Member.findById(id);
        notFoundIfNull(member); 
        member.delete();
        flash.success("The member %s has been deleted", member.firstName  );
        index();
    }
   
    //add or edit member 
   public static void addOrEdit(long id,@Required String firstName,@Required String lastName , @Required String email) {
       if(validation.hasErrors()) {
           params.flash();       // add http parameters to the flash scope
           validation.keep(); // keep the errors for the next request
           index();
       }
       
       if (id == 0){ //no id, add member
        new Member(firstName,lastName,0,email).insert();
       }else{ // have id, edit member
        Member member = Member.findById(id);
         member.firstName = firstName;
         member.lastName = lastName;
         member.email = email;
         member.update();
       }
       index();
   }
   
   //產生測試資料
   public static void genData() {
    int count = 10;
       for (int i=1;i<=count;i++){
        Member theMember = new Member("FirstName"+i,"lastName"+i,0,"email"+i);
        theMember.insert();
        for (int j=1;j<=20;j++){
         MemberCard theCard = new MemberCard("NO:"+i+j,theMember);
         theCard.startDate = new java.util.Date();
         theCard.insert();
         System.out.println("NO:"+i+j);
        }
       }
       flash.success("The member has been deleted, row=%s", count);
       index();
   }
   
   //清空測試資料
   public static void removeAllData() {
    int count = Member.all().delete();
    for (Member m: Member.all().fetch()) m.delete();
       flash.success("The member has been deleted, row=%s", count);
       index();
   }
   
}

2.View 

N:\PlayFramework\play-1.1\members\app\views\BasicMemberOperation\index.html
#{extends 'main.html' /}
#{set title:'Home' /}
<h2>
#{a @BasicMemberOperation.genData()}<b>Create Test Data</b>#{/a}
<BR>
#{a @BasicMemberOperation.removeAllData()}<b>Remove all Data</b>#{/a}
</h2>
<hr>

#{form @BasicMemberOperation.addOrEdit()}
    <p>
     <input type="hidden" name="id" id="id" size="60" value="${flash.id}"/>
  
        Member First Name <input type="text" name="firstName" id="firstName" size="20" value="${flash.firstName}"/>
  <span class="error">#{error 'firstName' /}</span>
  <BR>
  
  Member Last Name <input type="text" name="lastName" id="lastName" size="20" value="${flash.lastName}"/>
  <span class="error">#{error 'lastName' /}</span>
  <BR>
  
  Member Email <input type="text" name="email" id="email" size="40" value="${flash.email}"/>
  <span class="error">#{error 'email' /}</span>
    </p>  
    <p>
     #{if flash.id == null}
        <input type="submit" value="ADD Data"> 
  #{/if}
  #{else}
  <input type="submit" value="SAVE"> 
  #{/else}
    </p>

#{/form}
<hr>
<h2>All Member List</h2>
#{if members.size() > 0}

<table BORDER=1>
<tr >
 <td><b>Member First Name</b></td>
 <td><b>Member Last Name</b></td>
 <td><b>Member Email</b></td>
 <td><b>Action</b></td>
 </tr> 
#{list items:members, as:'members'}
 <tr>
 <td>${members.firstName} </td>
 <td>${members.lastName}</td>
 <td>${members.email}</td>
 <td>
  #{a @BasicMemberOperation.getDataForEdit(members.id)}Edit#{/a}
  #{a @BasicMemberOperation.delete(members.id)}Delete#{/a}
 </td>
 </tr>
#{/list} 
</table>
#{/if}
 
 
#{else}
    <div class="empty">
        There is currently nothing to show here.
    </div>
#{/else}


3. Model
Model 的部份請參照第一篇,將資料物件相關的檔案放到N:\PlayFramework\play-1.1\members\app\models下 

4. 執行專案 
執行以下命令 N:\PlayFramework\play-1.1>play run members 之後直接用瀏覽器開啟 http://127.0.0.1:9000/basicmemberoperation/index 便可以進行操作

2011年6月24日 星期五

Play Framework 實驗筆記 2- Play! Framework 環境設定

1. 安裝
這個部份比較簡單,大部分資訊網路上都有。Google 一下就有了~~
Source由官網 http://www.playframework.org/download 下載 play-1.1.zip 直接解壓後就可以使用

2. 建立專案
N:\PlayFramework\play-1.1>play new members

N:\PlayFramework\play-1.1>"N:\PlayFramework\play-1.1\python\python.exe" "N:\Play
Framework\play-1.1\play" new members
~        _            _
~  _ __ | | __ _ _  _| |
~ | '_ \| |/ _' | || |_|
~ |  __/|_|\____|\__ (_)
~ |_|            |__/
~
~ play! 1.1, http://www.playframework.org
~
~ The new application will be created in N:\PlayFramework\play-1.1\members
~ What is the application name? [members]
~
~ OK, the application is created.
~ Start it with : play run members
~ Have fun!
~

N:\PlayFramework\play-1.1>

3. 安裝Plugin for PlayFramework

3.1. SIENA
package models;
N:\PlayFramework\play-1.1>play install siena

N:\PlayFramework\play-1.1>"N:\PlayFramework\play-1.1\python\python.exe" "N:\Play
Framework\play-1.1\play" install siena
~        _            _
~  _ __ | | __ _ _  _| |
~ | '_ \| |/ _' | || |_|
~ |  __/|_|\____|\__ (_)
~ |_|            |__/
~
~ play! 1.1, http://www.playframework.org
~
~ Will install siena-1.5
~ This module is compatible with: 1.1
~ Do you want to install this version (y/n)? y
~ Installing module siena-1.5...
~
~ Fetching http://www.playframework.org/modules/siena-1.5.zip
~ [--------------------------100%-------------------------] 32.9 KiB/s
~ Unzipping...
~
~ Module siena-1.5 is installed!
~ You can now use it by add adding this line to application.conf file:
~
~ module.siena=${play.path}/modules/siena-1.5
~

3.2. GAE
package models;
N:\PlayFramework\play-1.1>play install gae

N:\PlayFramework\play-1.1>"N:\PlayFramework\play-1.1\python\python.exe" "N:\Play
Framework\play-1.1\play" install gae
~        _            _
~  _ __ | | __ _ _  _| |
~ | '_ \| |/ _' | || |_|
~ |  __/|_|\____|\__ (_)
~ |_|            |__/
~
~ play! 1.1, http://www.playframework.org
~
~ Will install gae-1.4
~ This module is compatible with: 1.1, and GAE 1.4.0
~ Do you want to install this version (y/n)? y
~ Installing module gae-1.4...
~
~ Fetching http://www.playframework.org/modules/gae-1.4.zip
~ [--------------------------100%-------------------------] 32.9 KiB/s
~ Unzipping...
~
~ Module gae-1.4 is installed!
~ You can now use it by add adding this line to application.conf file:
~
~ module.siena=${play.path}/modules/gae-1.4
~

記得加入這兩行,到 N:\PlayFramework\play-1.1\member1\conf\application.conf

module.gae=${play.path}/modules/gae-1.4
module.siena=${play.path}/modules/siena-1.5


4. 將專案匯出到eclipse
這個三不五時可以作一次,例如你新增module 到你的application.conf 後,記得重新匯出到eclipse,這樣相關的lib才會載入到eclipse project
package models;
N:\PlayFramework\play-1.1>play eclipsify members

N:\PlayFramework\play-1.1>"N:\PlayFramework\play-1.1\python\python.exe" "N:\Play
Framework\play-1.1\play" eclipsify members
~        _            _
~  _ __ | | __ _ _  _| |
~ | '_ \| |/ _' | || |_|
~ |  __/|_|\____|\__ (_)
~ |_|            |__/
~
~ play! 1.1, http://www.playframework.org
~
~ OK, the application is ready for eclipse
~ Use File/Import/General/Existing project to import N:\PlayFramework\play-1.1\m
embers into eclipse
~
~ Use eclipsify again when you want to update eclipse configuration files.
~ However, it's often better to delete and re-import the project into your works
pace since eclipse keeps dirty caches...
~
然後開啟Eclipse 之後,用File下面的 Import,選擇 General/Existing project 後到指定路徑就可以Import 專案。


5. 啟動專案
package models;

N:\PlayFramework\play-1.1>play run members

N:\PlayFramework\play-1.1>"N:\PlayFramework\play-1.1\python\python.exe" "N:\Play
Framework\play-1.1\play" run members
~        _            _
~  _ __ | | __ _ _  _| |
~ | '_ \| |/ _' | || |_|
~ |  __/|_|\____|\__ (_)
~ |_|            |__/
~
~ play! 1.1, http://www.playframework.org
~
~ Ctrl+C to stop
~
Listening for transport dt_socket at address: 8000
23:09:07,000 INFO  ~ Starting N:\PlayFramework\play-1.1\members
23:09:08,515 WARN  ~ You're running Play! in DEV mode
23:09:09,468 INFO  ~ Listening for HTTP on port 9000 (Waiting a first request to
 start) ...
6. 開啟瀏覽器,並連線到 http://127.0.0.1:9000/
看到以下的圖片,代表專案已經正常執行

2011年6月14日 星期二

Play Framework 實驗筆記 1- Data persitence API

首先先從資料物件開始介紹~

關於 Data persitence API,由於考慮到使用雲端服務(ex.GAE)所以找了一下支援的API,發現Siena還不錯,所以先拿來試試看。

目前觀察到Siena 有以下特性:

1. 容易上手,設計也很直覺
2. 同時支援兩大雲端服務
3. 支援目前所有熱門的關聯式資料庫 (採用Apache DdlUtils)

以下是關於Siena Project 的說明
Siena is a single API with many implementations. You can use siena with relational databases, with the Google App Engine's datastore or with Amazon's SimpleDB. There is also an implementation called siena-remote very useful if you want to use the Google App Engine's datastore remotely. Other implmenetations are planned such as: HBase, DBSLayer,...

Siena is Open Source and is released under the Apache License 2.0.

這個範例中會用到兩個資料物件,分別是Member 和 MemberCard,兩者的關係是一個Member會有多張MemberCard(也就是一對多的關係)。下面貼出範例的程式碼供參考:

Member.java
package models;

import java.util.*;
import siena.*;

public class Member extends Model{
     @Id(Generator.AUTO_INCREMENT)
     public Long id;

     @Column("first_name")
     public String firstName;

     @Column("last_name")
     public String lastName;

     @Column("gender")
     public Integer  gender;
     
     @Column("email")
     public String email;
     

     public Member(String firstName, String lastName, int gender, String email) {
         this.firstName = firstName;
         this.lastName = lastName;
         this.gender = gender;
         this.email = email;
     }
     
     public static Query<Member> all() {
         return Model.all(Member.class);
     }
     
     public static List<Member> getall() {
         return all().fetch();
     }
     
     public static Member findById(Long id) {
         return all().filter("id", id).get();
     }
     
     public static Query<MemberCard> findByMember(Long id) {
      Member theMember= all().filter("id", id).get();
      Query<MemberCard> cards = MemberCard.all().filter("member", theMember);
         return cards;
     }
 
}

MemberCard.java
package models;

import java.util.Date;
import java.util.List;

import javax.persistence.CascadeType;
import javax.persistence.OneToMany;

import siena.Column;
import siena.Generator;
import siena.Id;
import siena.Model;
import siena.Query;

public class MemberCard extends Model {

    @Id
    @Column("cardID")
    public String cardID;
    
    @Column("StartDate")
    public Date startDate;
    
    @Column("endDate")
    public Date endDate;
    
    @Column("member")
    public Member member;

    public MemberCard(String cardID) {
        this.cardID = cardID;
    }
  
    public MemberCard(String cardID, Member member) {
        this.cardID = cardID;
        this.member = member;
    }
    
    public static Query<MemberCard> all() {
        return Model.all(MemberCard.class);
    }
    
    public static List<MemberCard> getall() {
        return all().fetch();
    }
    
    public static MemberCard findById(String cardID) {
        return all().filter("cardID", cardID).get();
    }
    
    
}

2011年6月9日 星期四

Regarding the select editor for jqGrid

http://www.ok-soft-gmbh.com/jqGrid/DependendSelects2.htm
這個範例是做Grid 中兩個Select Editor 的互動