2014年9月23日 星期二

AngularJS 教學 - Java RESTful 範例

AngularJS 教學 - $resource service 詳細介紹到如何使用 ngResource 的 $resource service 進行後端的 RESTful 溝通

所以我花點時間打算寫一些 AngularJS RESTful 一個完整的前後端範例

可能想到要用 Java 和 Node.js 作為範例後端吧,所以本篇先提供以 Java 為後端的範例程式吧


以下我用 Todo 作為範例,整個程式我並不會介紹太多,基本的可以先看 AngularJS 教學 - $resource service

先列出專案目錄結構
































1. 建立 Java RESTful 專案
   我是以 Maven 來建立我的範例專案,如果不想用 Maven 的你可以直接抓 JAR 也無所謂
   以下是 POM 檔內容(pom.xml)
   <dependencies>
        <dependency>
            <groupId>com.sun.jersey</groupId>
            <artifactId>jersey-server</artifactId>
            <version>1.17.1</version>
        </dependency>
        <dependency>
            <groupId>com.sun.jersey</groupId>
            <artifactId>jersey-servlet</artifactId>
            <version>1.17.1</version>
        </dependency>
        <dependency>
            <groupId>com.sun.jersey</groupId>
            <artifactId>jersey-json</artifactId>
            <version>1.17.1</version>
        </dependency>
     </dependencies>

    不想用 Maven 的,可以抓下列的 JAR 檔放到 WEB-INF/lib 目錄內..
    

    
2. 建立 Todo Model(Todo.java)
   
package todo.model;

import javax.xml.bind.annotation.XmlRootElement;

@XmlRootElement
public class Todo {
 private int id;
 private String name;
 private String owner;
 private String priority;
 public int getId() {
  return id;
 }
 public void setId(int id) {
  this.id = id;
 }
 public String getName() {
  return name;
 }
 public void setName(String name) {
  this.name = name;
 }
 public String getOwner() {
  return owner;
 }
 public void setOwner(String owner) {
  this.owner = owner;
 }
 public String getPriority() {
  return priority;
 }
 public void setPriority(String priority) {
  this.priority = priority;
 }
}

3. 建立 Todo Dao(TodoDao.java)
   為了範例的方便以及重點在於 AngularJS 的 RESTful
   所以沒有用任何資料庫,資料都是 Hard code 的...

package todo.dao;
import java.util.ArrayList;
import java.util.List;
import todo.model.Todo;
public class TodoDao {
 private static int idSeq = 1;
 private static List todos;
 
 static{
  final Todo todo = new Todo(){{
   setId(idSeq++);
   setName("Complete a Todo list");
   setOwner("Allen");
   setPriority("High");
  }};
  todos = new ArrayList(){{
   add(todo);
  }};
 }
 
 public static List queryAll(){
  return todos;
 }
 
 public static Todo getTodo(int id){
  Todo result = null;
  for(Todo todo: todos){
   if(todo.getId() == Integer.valueOf(id)){
    result = todo;
    break;
   }
  }
  return result;
 }
 
 public static Todo addTodo(Todo todo){
  todo.setId(idSeq++);
  todos.add(todo);
  return todo;
 }

 public static void updateTodo(Todo todo) {
  Todo target = getTodo(todo.getId());
  target.setName(todo.getName());
  target.setOwner(todo.getOwner());
  target.setPriority(todo.getPriority());
 }

 public static void deleteTodo(Integer id) {
  int index = 0;
  for(int i=0;i<todos.size();i++){
   if(todos.get(i).getId() == id) index = i;
  }
  todos.remove(index);
 }
 
}
4. 建立 Todo RESTful(TodoResource.java)


package todo.resource;
import java.util.List;
import javax.ws.rs.DELETE;
import javax.ws.rs.GET;
import javax.ws.rs.POST;
import javax.ws.rs.PUT;
import javax.ws.rs.Path;
import javax.ws.rs.PathParam;
import javax.ws.rs.Produces;
import javax.ws.rs.core.MediaType;
import todo.dao.TodoDao;
import todo.model.Todo;

@Path("/todos")
public class TodoResource {
 
    @GET
    @Produces(MediaType.APPLICATION_JSON)
    public List getTodos() {
  return TodoDao.queryAll();
    }
 
    @GET
    @Path("{id}")
    @Produces(MediaType.APPLICATION_JSON)
    public Todo getTodo(@PathParam("id") String id) {
  return TodoDao.getTodo(Integer.valueOf(id));
    }
 
    @POST
    @Produces(MediaType.APPLICATION_JSON)
    public Todo getTodos(Todo todo) {
  return TodoDao.addTodo(todo);
    }
 
    @PUT
    @Path("{id}")
    @Produces(MediaType.APPLICATION_JSON)
    public void updateTodos(Todo todo) {
  TodoDao.updateTodo(todo);
    }
 
    @DELETE
    @Path("{id}")
    @Produces(MediaType.APPLICATION_JSON)
    public void updateTodos(@PathParam("id") String id) {
  TodoDao.deleteTodo(Integer.valueOf(id));
    }
}
5. 設定 web.xml
    <servlet>
        <servlet-name>jersey-serlvet</servlet-name>

        <servlet-class>
            com.sun.jersey.spi.container.servlet.ServletContainer
        </servlet-class>

        <init-param>
            <param-name>com.sun.jersey.config.property.packages</param-name>
            <param-value>todo.resource</param-value>
        </init-param>

        <init-param>
            <param-name>com.sun.jersey.api.json.POJOMappingFeature</param-name>
            <param-value>true</param-value>
        </init-param>

        <load-on-startup>1</load-on-startup>
     </servlet>
    
     <servlet-mapping>
         <servlet-name>jersey-serlvet</servlet-name>
         <url-pattern>/rest/*</url-pattern>
     </servlet-mapping>

6. Java RESTful 後端總結
   撰寫 Java RESTful 的重點在於 RESTful 的URL以及請求方法(上面第四點)
   
   因為這會關係到前端 AngularJS 使用 $resource 時所要呼叫的後端 URL

   例如目前這個 Todo 範例

   查詢所有 Todo        --> GET /rest/todos
   查詢 id 為 1 的 Todo --> GET /rest/todos/1
   修改 id 為 1 的 Todo --> PUT /rest/todos/1
   刪除 id 為 1 的 Todo --> DELETE /rest/todos/1
   新增 Todo           --> POST /rest/todos


接下來是這次範例的畫面,為了稍為美觀所以加了 Bootstrap 3.0




7. index.html & app.js
   需要引入 bootstrap.min.css,並且下載以下這三個 AngularJS JS 檔

  angular.min.js、angular-resource.min.js、angular-route.min.js

   以下是 index.html 
       <html ng-app="todoApp">
    <head>
        <meta charset="UTF-8">
        <title>Todo App Demo</title>
        <link rel="stylesheet" type="text/css" href="css/bootstrap.min.css">
        <script type="text/javascript" src="js/lib/angular.min.js"></script>
        <script type="text/javascript" src="js/lib/angular-resource.min.js"></script>
        <script type="text/javascript" src="js/lib/angular-route.min.js"></script>
        <script type="text/javascript" src="js/app.js"></script>
        <script type="text/javascript" src="js/controller.js"></script>
        <script type="text/javascript" src="js/service.js"></script>
    </head>
    <body>
        <div ng-view></div>
    </body>
    </html>

        建立 AngularJS 的 main 程式 (app.js)

angular.module('todoApp', [
    'ngRoute',
    'todoApp.services',
    'todoApp.controllers'
]).config(function($routeProvider) {
    $routeProvider.when('/', {
        templateUrl: 'partial/list.html', 
        controller: 'TodoListCtrl'
    });
    $routeProvider.when('/todoDetail/:todoId', {
        templateUrl: 'partial/detail.html', 
        controller: 'TodoDetailCtrl'
    });
    $routeProvider.when('/todoUpdate/:todoId', {
        templateUrl: 'partial/update.html', 
        controller: 'TodoUpdateCtrl'
    });
    $routeProvider.otherwise({redirectTo: '/'});
});

8. 建立 Todo Service (service.js)


angular.module("todoApp.services", ["ngResource"])
    .factory("Todo", function($resource){
     return $resource('rest/todos/:id', {id:'@id'}, {
      update: {
                    method: 'PUT'
                }
     });
    });
9. 建立 Todo Controllers (controller.js)
          要多留意是怎麼使用 Todo service 去打 RESTful 的,這在上一篇都有提到了

'use strict';

angular.module("todoApp.controllers", [])
    .controller("TodoListCtrl", function($scope, Todo){
     $scope.name = "Allen";
     $scope.todos = Todo.query();
     $scope.$on("updateTodos", function (event, todo) {
      $scope.todos.push(todo);
        });
     
     $scope.remove = function(idx){
             $scope.todos[idx].$remove(function(res){
                 $scope.todos.forEach(function(p, index) {
                     if (index == idx) $scope.todos.splice(index, 1);
                 });
             });
        };
    })
    .controller("TodoDetailCtrl", function($scope, $routeParams, Todo){
        $scope.todo = Todo.get({id: $routeParams.todoId});
    })
    .controller("TodoAddCtrl", function($rootScope, $scope, Todo){
     $scope.name = "";
     $scope.owner = "";
     $scope.priority = "Low";
     
     $scope.add = function(){
      var todo = new Todo();
      todo.name = $scope.name;
      todo.owner = $scope.owner;
      todo.priority = $scope.priority;
      todo.$save(function(){
       $rootScope.$broadcast("updateTodos", todo);
      });
     };
    })
    .controller("TodoUpdateCtrl", function($scope, $routeParams, $location, Todo){
     $scope.todo = Todo.get({id: $routeParams.todoId});
     $scope.update = function(){
      $scope.todo.$update(function(){
                $location.path("/todoDetail/"+$scope.todo.id);
            });
     };
    });
10. 建立 HTML Page (list.html、detail.html、update.html)
    因為 HTML 程式碼的篇幅比較多,所以我只列出中點的地方
    
    首先是 list.html,頁面包含了列出所有的 Todo 項目,並且底下有個區塊可以新增 Todo
    
    <tr ng-repeat="todo in todos">
      <td><a href="#/todoDetail/{{todo.id}}">{{todo.name}}</a></td>
          <td>
              <a href="#/todoUpdate/{{todo.id}}">Update</a>
              &nbsp;&nbsp;
              <button type="button" class="btn btn-warning" ng-click="remove($index)">Delete</button>
          </td>
     </tr>
     //略...
     <div class="panel-body form-horizontal"  role="form" ng-controller="TodoAddCtrl">
         //略...
         <input type="text" name="name" ng-model="name" class="form-control"/>
         //略...
         <input type="text" name="owner" ng-model="owner" class="form-control"/>
         //略...
         <select name="priority" ng-model="priority" class="form-control">
             <option value='High'>High</option>
             <option value='Middle'>Middle</option>
             <option value='Low'>Low</option>
         </select>
       <button type="button" class="btn btn-default" ng-click="add()">Submit</button>
      </div>

    接下來是 detail.html,負責呈現一個 Todo 項目的內容
    <div class="panel panel-info">
          <div class="panel-heading">{{todo.name}}</div>
        <div class="panel-body">
            <p id='todoOwner'>Owner : {{todo.owner}}</p>
            <p id='todoPriority'>Priority : {{todo.priority}}</p>
            <a id='goBackLink' href='/AngularJSRestful/#/'>Back to list</a>
        </div>
     </div>
    
    最後是 update.html,負責更新一個 Todo 項目

          同樣為了節省篇幅,只列出重要的部分

     //略...
     <input type="text" name="name" ng-model="todo.name" class="form-control"/>
     //略...
     <input type="text" name="owner" ng-model="todo.owner" class="form-control">
     //略...
     <select name="priority" ng-model="todo.priority" class="form-control">
           <option value='High'>High</option>
           <option value='Middle'>Middle</option>
           <option value='Low'>Low</option>
      </select>
     //略...
      <button type="button" class="btn btn-default" ng-click="update()">Submit</button> 

最後附上完整的範例程式 點我下載(Download)

因為範例程式寫得很隨性,如果有錯請告知^0^

沒有留言:

張貼留言