歡迎您光臨本站 註冊首頁
   Internet 應用程序(Rich Internet Applications,RIA)通過瀏覽器保證桌面應用程序的動態性和功能。RIA 的主要特徵之一就是將表示層移動到客戶機,並使用伺服器上健壯的 RESTful 服務層支持它。這種想法藉助 SOUI(Service Oriented User Interface)和 SOFEA(Service Oriented Front End Architecture)之類的熱門辭彙得到傳播。本文是包含兩個部分的系列文章的第一部分,它讓您看到使用 Groovy 的 Grails Web 應用程序框架創建 Web 服務後端多麼簡單。您將把這個後端與用 Adobe 的 Flex 框架開發的 RIA 連接起來。

關於本系列

這個系列探索一些應用程序架構,它們在後端使用由 Grails 框架實現的面向服務架構(SOA)。了解 Grails 如何大大簡化了 Web 應用程序的創建,尤其是 Web 服務的創建。這種後端可以輕鬆連接到任意純客戶端應用程序。在第 1 部分中,您將使用 Adobe Flex 創建一個可以使用 Flash Player 的應用程序。在第 2 部分中,您將通過 Google Web Toolkit 用純 JavaScript 創建前端。

先決條件

在這篇文章中,您將使用 Grails 和 Flex 構建 Web 應用程序。Grails 框架基於 Groovy 編程語言,這是針對 Java™ 平台的動態語言。熟悉 Groovy 會更好,但不是必要的。了解 Java 或其他動態語言(比如 Ruby 或 Python)會有很大幫助。本文使用 Grails 1.0.3(參見 參考資料)。Grails 可用於許多資料庫或應用伺服器,但本文不需要提供它們,因為它們已經隨 Grails 附帶。前端使用 Flex 構建。Flex 是一個使用 ActionScript 編程語言的應用程序框架,它在 Flash Player 上運行。再聲明一下,不熟悉 Flex 和 ActionScript 沒有關係。熟悉 Java 和 JavaScript 有利於學習 Flex。需要 Flex SDK 3.2 或更高版本來編譯本文的代碼(參見 參考資料)。要運行本文構建的應用程序,必須具有 Flash Player 10.0 或更高版本。

架構

許多企業都爭相採用 Service Oriented Architecture (SOA)。SOA 讓架構更加敏捷,方便業務的快速發展。當然,您的企業可能有另一個緊迫的計劃:將用戶界面實現為現代的富 Internet 應用程序。SOA 和 RIA 這兩種流行的技術並不容易結合在一起。但事實證明,它們可以很好地協同工作。您可以使用 SOA 設計將服務部署到應用伺服器。也可以將所有表示邏輯移動到客戶機,並利用強大的前端技術(比如 Flex)創建 RIA。這正是您在本系列中需要完成的事情,現在從使用 Grails 創建一個 Web 服務開始。

Web 服務

當許多開發人員聽見術語 Web 服務 時,他們就會想到 SOAP(Simple Object Access Protocol)。SOAP 在很多開發人員腦中具有消極的意義,因為他們認為 SOAP 是一種大型的複雜技術。不過 Web 服務不是這樣的。REST(Representational State Transfer)式的 Web 服務受到普遍歡迎,因為它們的語義很簡單。它們的創建和使用都很容易。它們可以像 SOAP 一樣使用 XML,但使用的是 Plain Old XML(POX),不像 SOAP 那樣帶有奇特的包裝器和報頭。Grails 框架使得創建這種 Web 服務非常簡單,所以我們現在從 Grails 領域模型開始。

Grails 域模型

Grails 是通用的 Web 開發框架。許多 Web 應用程序都使用關係資料庫存儲和獲取在某個應用程序中使用的數據,因此 Grails 採用了強大的 Object Relational Modeling (ORM) 技術,即 GORM。通過 GORM,您可以輕鬆地對域對象進行建模,並將它們持久化到任意關係資料庫中,但不再需要處理 SQL。GORM 使用流行的 Hibernate 庫生成特定於資料庫的經過優化的 SQL,以及管理域對象的生命周期。在使用 GORM 之前,我們先快速討論一下將要創建的應用程序,以及需要使用 GORM 進行建模的東西。

在示例應用程序中,您將創建一個模仿流行站點 Digg 的功能的 Web 應用程序(見 參考資料)。在 Digg 上,用戶可以提交新聞鏈接(Web 頁面)。然後其他用戶可以閱讀這些新聞,並投票支持或反對它們。您的應用程序將具有這些基本功能。它允許人們匿名地提交新聞並對其進行投票,因此不需要對用戶進行建模,對新聞建模即可。下面是針對示例應用程序中的新聞的 GORM 模型,如清單 1 所示。


清單 1. 新聞模型
				  class Story {      String link      String title      String description      String tags      String category      int votesFor      int votesAgainst  }  

這就是對域對象進行建模所需的所有代碼。您要聲明它的屬性和這些屬性的類型。這將允許 Grails 為您創建表,並且為從該表讀寫數據動態創建方法。這是 Grails 提供的主要好處之一。您僅需將數據建模代碼放在某個地方,而不需要為簡單的讀寫編寫任何模板代碼。現在已經準備好域模型,您可以創建一些使用該域模型的業務服務了。

業務服務

SOA 的好處之一就是它允許您以非常自然的方式對系統進行建模。您希望執行的一些操作是什麼?您要以此為依據來定義應用程序的業務服務。例如,您需要瀏覽和搜索新聞,所以要為此創建一個服務,如清單 2 所示。


清單 2. 搜索服務
				  class SearchService {        boolean transactional = false        def list() {          Story.list()      }        def listCategory(catName){          Story.findAllWhere(category:catName)      }            def searchTag(tag){          Story.findAllByTagsIlike("%"+tag+"%")      }  }  

您首先注意到的可能是布爾標誌 transactional。Grails 構建在大量成熟的技術之上,包括 Spring。它使用 Spring 的聲明性事務來修飾帶有事務的服務。如果您想執行數據更新,或希望其他事務服務能夠使用這個服務,那麼就要啟用這個功能。您希望這個服務是只讀的。它不會取代數據訪問,因為您已經擁有處理這方面內容的域模型。

第一個服務操作獲取所有新聞。第二個操作獲取特定類別的新聞。在這裡用到 GORM 的 where-樣式的查找程序。它允許您傳入一個名稱/值對映射,以在 SQL 查詢中構造一個 WHERE 子句。第三個操作讓您可以使用特定標記搜索新聞。在這裡使用的是 GORM 的動態查找程序。這樣,您就可以將查詢子句作為查找程序方法的名稱的一部分。所以使用 findAllByTags 在標記列上執行查詢。此外,還有注意 like 後綴;這是區分大小寫的 SQL LIKE 子句。它允許您查找所有標記參數作為標記屬性的子字元串的新聞。例如,如果一個新聞被標記為 “BarackObama”,它將在搜索 “obama” 時顯示出來。

您創建了 3 種簡單但強大的新聞搜索方式。注意,它們的語法都很簡潔。如果您是一位 Java 程序員,就知道正常情況下要編寫多少代碼才能實現這些功能。搜索服務是很強大的,但現在還沒有可供搜索的內容。您還需要一個用於管理每則新聞的服務,我稱之為 Story 服務,如清單 3 所示。


清單 3. Story 服務
				  class StoryService {        boolean transactional = true        def create(story) {          story.votesFor = 0          story.votesAgainst = 0          log.info("Creating story="+story.title)          if(!story.save(flush:true) ) {              story.errors.each {                  log.error(it)              }          }          log.info("Saved story="+story.title + " id=" + story.id)          story      }      def voteFor(storyId){          log.info("Getting story for id="+storyId)          def story = Story.get(storyId)          log.info("Story found title="+story.title + "   votesFor="+story.votesFor)          story.votesFor += 1          if(!story.save(flush:true) ) {              story.errors.each {                  log.error(it)              }          }          story      }      def voteAgainst(storyId){          log.info("Getting story for id="+storyId)          def story = Story.get(storyId)          log.info("Story found title="+story.title + "   votesAgainst="+story.votesAgainst)          story.votesAgainst += 1          if(!story.save(flush:true) ) {              story.errors.each {                  log.error(it)              }          }          story              }  }  

Story 服務有 3 個操作。首先,您可以創建一則新的新聞。其次,有一個通過新聞 ID 對其好壞進行投票的操作。無論那種情況,您都記錄了一些日誌。Grails 使得日誌記錄非常容易 — 僅需使用隱式日誌對象和普通的 log4j 樣式的方法:log.debug、log.info、log.error 等等。使用 Story 實例上的 save 方法保存(或插入和刪除)這則新聞。您要注意如何通過檢測新聞實例的屬性錯誤來檢查錯誤。例如,如果新聞所需的欄位的值缺失,它將作為 story.errors 的一部分顯示出來。最後需要注意的是,這個服務是事務性的。這將告訴 Grails(和 Spring)使用現有的事務(如果已經存在的話),或在調用這些操作時創建一個新的事務。這對投票操作十分重要,因為在這些操作中您必須先從資料庫讀取新聞,然後再更新一個列。現在已經創建了基本的業務服務,您可以圍繞它們創建一個 Web 服務,以將它們公開為 API,如下所示。

公開 API

作為開發人員,我們通常認為 API 是為供其他開發人員調用而編寫的代碼。在面向服務架構中,這仍然是正確的。不過這個 API 不是 Java 介面或類似的東西;它是一個 Web 服務簽名。Web 服務公開 API,讓他人可以調用它。Grails 讓 Web 服務的創建變得非常簡單 — 它不過是 Grails 應用程序中的另一個控制器。清單 4 給出了應用程序的 API。


清單 4. API 控制器
				  import grails.converters.*    class ApiController {      // injected services      def searchService      def storyService            // set the default action      def defaultAction = "stories"        def stories = {          stories = searchService.list()          render stories as XML      }            def submit = {          def story = new Story(params)          story = storyService.create(story)          log.info("Story saved story="+story)          render story as XML      }            def digg = {          def story = storyService.voteFor(params.id)          render story as XML      }            def bury = {          def story = storyService.voteAgainst(params.id)          render story as XML      }  }  

在這裡,您將看到在前面小節創建的業務服務。這些服務由 Grails 自動注入。實際上,它們是由 Spring 框架(Grails 構建在這種技術之上)注入的。您僅需遵循命名約定(searchService 的實例變數,用於引用 SearchService 類的實例等等),其他事情由 Grails 完成。

API 包含四個可能調用的操作(行為):stories、submit、digg 和 bury。在每種情況下,您將委託給一個業務服務,然後獲取調用結果並將其序列化到發送給客戶機的 XML。Grails 使呈現變得很簡單,僅使用呈現函數和 XML 轉換器。最後需要注意,submit、digg 和 bury 操作都使用 params 對象。這是 HTTP 請求參數及其值的散列表。對於 digg 和 bury,僅需獲取 id 參數。對於 submit 操作,需要將整個 params 對象傳遞給 Story 類的構造器。這用到 Grails 數據綁定 — 只要參數名與該類的屬性名匹配,Grails 將替您設置它們。這是 Grails 使開發更加容易的另一個例子。您僅編寫了很少的代碼,但這就是創建服務並將其公開為 Web 服務所需的所有代碼。現在您可以創建一個使用這些服務的富表示層。





表示

您已經在應用程序的後端使用了 SOA。這將允許您在其上創建許多不同類型的表示層。首先要使用 Flex 框架構建一個基於 Flash 的用戶界面。不過這像使用其他客戶端表示技術一樣簡單。您甚至還可以創建一個 “胖” 桌面客戶端。不過這是不必要的,因為您可以從 Web 客戶端上獲得豐富的用戶體驗。看看下面提供的一些簡單用例,並使用 Flex 通過它們構建用戶界面。現在,讓我們列出所有新聞。

列出新聞

要列出新聞,您必須知道應該從後端調用哪個 API。您知道了嗎?在創建後端時,您是否將特定的 URL 映射到 API 控制器及其方法?很明顯您還沒有這樣做,但 Grails 能幫助您輕鬆完成。它使用約定優於配置的原則完成此操作。因此,要在 API 控制器上為 digg 應用程序調用新聞操作,URL 應該為 http://<root>/digg/api/stories。因為這是一個 RESTful Web 服務,所以它應該是一個 HTTP GET。您可以在瀏覽器上直接執行這個操作,並獲得類似於清單 5 的 XML。


清單 5. 示例 XML 響應
				  <?xml version="1.0" encoding="UTF-8"?><list>    <story id="12">      <category>technology</category>      <description>This session discusses approaches and techniques to   implementing Data Visualization and Dashboards within Flex 3.</description>      <link>http://onflash.org/ted/2008/10/360flex-sj-  2008-data-visualization-and.php</link>      <tags>flash, flex</tags>      <title>Data Visualization and Dashboards</title>      <votesAgainst>0</votesAgainst>      <votesFor>0</votesFor>    </story>    <story id="13">      <category>technology</category>      <description>Make your code snippets like really nice when you blog about   programming.</description>      <link>http://bc-squared.blogspot.com/2008/07/  syntax-highlighting-and-code-snippets.html</link>      <tags>programming, blogging, javascript</tags>      <title>Syntax Highlighting and Code Snippets in a blog</title>      <votesAgainst>0</votesAgainst>      <votesFor>0</votesFor>    </story>    <story id="14">      <category>miscellaneous</category>      <description>You need a get notebook if you are going to take   good notes.</description>      <link>http://www.randsinrepose.com/archives/2008/06/01/  sweet_decay.html</link>      <tags>notebooks</tags>      <title>Sweet Decay</title>      <votesAgainst>0</votesAgainst>      <votesFor>0</votesFor>    </story>    <story id="16">      <category>technology</category>      <description>If there was one thing I could teach every engineer, it   would be how to market. </description>      <link>http://www.codinghorror.com/blog/archives/001177.html</link>      <tags>programming, funny</tags>      <title>The One Thing Every Software Engineer Should Know</title>      <votesAgainst>0</votesAgainst>      <votesFor>0</votesFor>    </story>  </list>  

很明顯,具體內容取決於您在資料庫中保存什麼樣的數據。重要的是 Grails 以什麼樣的結構將 Groovy 對象序列化為 XML。現在,可以為服務編寫一些 ActionScript 代碼了。

數據訪問

將表示層移動到客戶端的最大好處就是讓架構更加乾淨。現在可以輕鬆採用傳統的模型-視圖-控制器(MVC)模式,因為伺服器和客戶機之間很乾凈。所以,首先構建一個表示數據結構的模型,然後封裝對數據的訪問。這如清單 6 中的 Story 類所示。


清單 6. Story 類
				  public class Story extends EventDispatcher  {      private static const LIST_URL:String =       "http://localhost:8080/digg/api/stories";                                [Bindable] public var id:Number;              [Bindable] public var title:String;      [Bindable] public var link:String;      [Bindable] public var category:String;      [Bindable] public var description:String;      [Bindable] public var tags:String;      [Bindable] public var votesFor:int;      [Bindable] public var votesAgainst:int;                private static var listStoriesLoader:URLLoader;      private static var dispatcher:Story = new Story();                    public function Story(data:XML=null)          {              if (data)              {                  id = data.@id;                  title = data.title;                  link = data.link;                  category = data.category;                  description = data.description;                  tags = data.tags;                  votesFor = Number(data.votesFor);                  votesAgainst = Number(data.votesAgainst);              }          }    }  

清單 6 給出了 Story 類的基礎內容。它具有幾個與您將從服務獲取的數據結構對應的欄位。這個構造器採用一個可選的 XML 對象,並通過這個對象填充欄位。您可能已經看到,訪問 XML 數據的語法非常簡單。ActionScript 將 E4X 標準實現為處理 XML 的簡化方式。這類似於 XPath,但使用對面向對象編程語言更加自然的語法。此外,每個屬性都使用 [Bindable] 修飾。這是一個 ActionScript 註釋。它允許將 UI 組件綁定到欄位,以在欄位發生變更時自動更新 UI。最後需要注意的是靜態變數 listStoriesLoader。這是 URLLoader 的一個實例,即用於發送 HTTP 請求的 ActionScript 類。Story 類中的一個靜態方法使用它通過 API 載入所有新聞。如清單 7 所示。


清單 7. 列出 Stories 方法
				  public class Story extends EventDispatcher  {      public static function list(loadHandler:Function, errHandler:Function=null):void      {          var req:URLRequest = new URLRequest(LIST_URL);          listStoriesLoader = new URLLoader(req);          dispatcher.addEventListener(DiggEvent.ON_LIST_SUCCESS, loadHandler);          if (errHandler != null)          {              dispatcher.addEventListener(DiggEvent.ON_LIST_FAILURE, errHandler);          }          listStoriesLoader.addEventListener(Event.COMPLETE, listHandler);          listStoriesLoader.addEventListener(IOErrorEvent.IO_ERROR, listErrorHandler);          listStoriesLoader.load(req);      }      private static function listHandler(e:Event):void      {          var event:DiggEvent = new DiggEvent(DiggEvent.ON_LIST_SUCCESS);          var data:Array = [];          var storiesXml:XML = XML(listStoriesLoader.data);          for (var i:int=0;i<storiesXml.children().length();i++)          {              var storyXml:XML = storiesXml.story[i];              var story:Story = new Story(storyXml);              data[data.length] = story;          }          event.data = data;          dispatcher.dispatchEvent(event);      }  }  

list 是控制器將要調用的方法。它發出一個 HTTP 請求,並為該請求的結束註冊一個事件監聽器。這是必要的,因為 Flash 中的所有 HTTP 請求都是非同步的。當請求完成時,將調用 listHandler 方法。在這裡將再次使用 E4X 解析來自服務的 XML 數據。它創建一個 Story 實例數組,並將該數組附加到一個將要發出的定製事件。看看清單 8 中的定製事件。


清單 8. DiggEvent
				  public class DiggEvent extends Event  {      public static const ON_STORY_SUBMIT_SUCCESS:String = "onStorySubmitSuccess";      public static const ON_STORY_SUBMIT_FAILURE:String = "onStorySubmitFailure";      public static const ON_LIST_SUCCESS:String = "onListSuccess";      public static const ON_LIST_FAILURE:String = "onListFailure";      public static const ON_STORY_VOTE_SUCCESS:String = "onStoryVoteSuccess";      public static const ON_STORY_VOTE_FAILURE:String = "onStoryVoteFailure";                public var data:Object = {};      public function DiggEvent(type:String, bubbles:Boolean=false,   cancelable:Boolean=false)      {          super(type, bubbles, cancelable);      }        }  

在 ActionScript 開發中經常用到定製事件類,因為所有伺服器交互都必須是非同步的。控制器可以在模型類上調用該方法,並且註冊用於查找定製事件的事件處理程序。您可以使用額外的欄位修飾定製事件。在這個例子中,您僅添加了一個通用的可重用的數據欄位。了解模型的表示層之後,我們看看控制器如何使用它。

應用程序控制器

該控制器負責協調模型的調用,將模型提供給視圖,然後通過視圖響應事件。我們看看控制器的代碼,如清單 9 所示。


清單 9. DiggController
				  public class DiggController extends Application  {      [Bindable]      public var stories:ArrayCollection;      [Bindable]      public var subBtnLabel:String = 'Submit a new Story';            public function DiggController()      {          super();          init();      }            private function init():void      {          Story.list(function(e:DiggEvent):void{                stories = new ArrayCollection(e.data as Array);});                  }  }  

控制器擴展了核心 Flex 類 Application。這是 Flex 的常見用法,它使您可以通過簡單的方式將控制器與視圖關聯起來,如下一小節所示。控制器具有一組可以綁定的新聞。因此可以將這組新聞綁定到 UI 控制項。應用程序載入之後將自動調用 Story.list 方法。它將一個匿名函數或 lambda 作為處理程序傳遞給 Story.list 方法。lambda 表達式僅將來自定製事件的數據轉儲到新聞集合中。現在看看如何在視圖中使用它。

視圖

我在前面提到,控制器擴展了 Flex Application 類。它是所有 Flex 應用程序的基類。Flex 允許使用 MXML(一種 XML 變體)聲明式地創建 UI。控制器可以利用這種語法,如清單 10 所示。


清單 10. 用戶界面
				  <?xml version="1.0" encoding="utf-8"?>  <ctrl:DiggController xmlns:mx="http://www.adobe.com/2006/mxml" layout="vertical"       xmlns:works="components.*" xmlns:ctrl="controllers.*">      <mx:Script>          <![CDATA[              import org.developerworks.digg.Story;                private function digg():void              {                  this.diggStory(results.selectedItem as Story);              }              private function bury():void              {                  this.buryStory(results.selectedItem as Story);              }          ]]>      </mx:Script>      <ctrl:states>          <mx:State name="SubmitStory">              <mx:AddChild relativeTo="{buttons}" position="after">                  <works:StoryEditor successHandler="{this.submissionHandler}"/>              </mx:AddChild>          </mx:State>      </ctrl:states>      <mx:DataGrid id="results" dataProvider="{stories}" doubleClickEnabled="true"           doubleClick="openStory(results.selectedItem as Story)"/>      <mx:HBox id="buttons">          <mx:Button label="Digg the Story!" click="digg()"/>          <mx:Button label="Bury the Story!" click="bury()"/>          <mx:Button label="{this.subBtnLabel}" click="toggleSubmitStory()"/>      </mx:HBox>  </ctrl:DiggController>  

注意,根文檔使用 “ctr1” 名稱空間。這被聲明為指向 “controller” 文件夾,即存儲 controller 類的位置。因此任何來自 controller 類的內容在 UI 代碼中都是可用的。例如,一個 dataProvider 屬性設置為 stories 的數據網格。這是來自 controller 類的 stories 變數。它直接綁定到 DataGrid 組件。現在可以將 Flex 代碼編譯成 SWF 文件。您僅需使用靜態 HTML 文件來嵌套 SWF。它的運行效果如圖 1 所示。


圖 1. Digg 應用程序

這是 Flex 應用程序的默認外觀。您可以使用標準的 CSS 改變它的顏色,就像在 HTML 應用程序中一樣。此外,還可以定製 DataGrid 組件,指定它的列、順序等等。“Digg the Story!” 和 “Bury the Story!” 按鈕都調用 controller 類上的函數。注意,您已經為 DataGrid 的雙擊事件添加一個 controller 函數。所有 controller 方法都使用這個模型,就像在 MVC 架構中一樣。最後一個按鈕 “Submit a new Story” 使用了 Flex 的幾個關鍵特性。看看它是如何工作的。





提交新聞

如您在清單 10 所見,單擊 “Submit a new Story” 按鈕將調用 controller 類上的 toggleSubmitStory 方法。代碼如清單 11 所示。


清單 11. toggleSubmitStory 方法
				  public class DiggController extends Application  {        public function toggleSubmitStory():void      {          if (this.currentState != 'SubmitStory')          {              this.currentState = 'SubmitStory';              subBtnLabel = 'Nevermind';          }          else          {              this.currentState = '';              subBtnLabel = 'Submit a new Story';          }      }          }  

這個函數將應用程序的 currentState 屬性更改為 SubmitStory。現在回頭看看 清單 10,找到這個狀態是在什麼位置定義的。狀態允許您添加或刪除組件,或在現有組件上設置屬性。在這個例子中,您為 UI 添加一個新組件。這個組件正是提交新聞所需的定製組件。如清單 12 所示。


清單 12. StoryEditor 組件
				  <?xml version="1.0" encoding="utf-8"?>  <mx:VBox xmlns:mx="http://www.adobe.com/2006/mxml" width="100%" height="100%">      <mx:Form>          <mx:FormHeading label="Submit a New Story"/>          <mx:FormItem label="What's the URL?" required="true">              <mx:TextInput id="linkBox" toolTip="Keep it Short and Sweet"                   text="{story.link}"/>          </mx:FormItem>          <mx:FormItem label="Give it a Title" required="true">              <mx:TextInput id="titleBox" text="{story.title}"/>          </mx:FormItem>          <mx:FormItem label="Pick a Category" required="true">              <mx:ComboBox dataProvider="{Story.CATEGORIES}" id="categoryBox"   selectedIndex="0"/>          </mx:FormItem>          <mx:FormItem label="Give a Short Description">              <mx:TextArea height="60" width="320" id="descripBox"                   text="{story.description}"/>          </mx:FormItem>          <mx:FormItem label="Tag It">              <mx:TextInput id="tagBox" text="{story.tags}"/>          </mx:FormItem>          <mx:Button label="Submit It!" click="submitStory()"/>      </mx:Form>      <mx:Binding source="linkBox.text" destination="story.link"/>      <mx:Binding source="titleBox.text" destination="story.title"/>      <mx:Binding source="categoryBox.selectedItem.data"   destination="story.category"/>      <mx:Binding source="descripBox.text" destination="story.description"/>      <mx:Binding source="tagBox.text" destination="story.tags"/>      </mx:VBox>  

清單 12 僅給出了定製組件的 UI 元素。這僅是一個簡單的表單,但要注意其中的綁定聲明。它允許您直接將 UI 表單元素綁定到 Story 實例。這已在一個腳本塊中聲明,如清單 13 所示。


清單 13. StoryEditor 腳本
				  <?xml version="1.0" encoding="utf-8"?>  <mx:VBox xmlns:mx="http://www.adobe.com/2006/mxml" width="100%" height="100%">      <mx:Script>          <![CDATA[              import mx.controls.Alert;              import org.developerworks.digg.*;                            [Bindable]              private var story:Story = new Story();              public var successHandler:Function;                            private function submitStory():void              {                  story.addEventListener(DiggEvent.ON_STORY_SUBMIT_SUCCESS,   successHandler);                  story.addEventListener(DiggEvent.ON_STORY_SUBMIT_FAILURE,   errorHandler);                  story.save();                  // reset                  story = new Story();              }                                        private function errorHandler(e:Event):void              {                  Alert.show("Fail! : " + e.toString());                              }          ]]>      </mx:Script>  </mx:VBox>  

該腳本塊用作組件的控制器代碼。您可以輕鬆地將它們放到該組件的獨立 controller 類中,因為這兩種樣式在 Flex 中都是很常見的。回頭看看 圖 1,單擊 “Submit new Story” 按鈕將顯示這個定製組件,如圖 2 所示。


圖 2. 顯示定製組件

您可以使用這個組件添加一則新的新聞。它將調用後端服務,該服務返回新聞所需的 XML。反過來,這將被轉換回 Story 實例,並添加到綁定到 DataGrid 的新聞集合中,使其能夠自動顯示在 UI 中。這樣,表示代碼就完成了,並連接到後端服務。





結束語

在這篇文章中,您看到了使用 Grails 創建 Web 服務有多麼簡單!您不需要編寫任何 SQL 以創建資料庫,或對其進行讀寫。通過使用 Grails,將 URL 映射到 Groovy 代碼、調用服務和為 Web 服務創建 XML 都變得非常簡單。Flex 前端能夠輕鬆使用 XML。您學會了如何在前端創建乾淨的 MVC 架構,以及如何使用許多複雜的 Flex 特性,比如 E4X、數據綁定、狀態和定製組件。

本系列的第 2 部分將探索在使用 Google Web Toolkit 的服務上使用基於 JavaScript 的 UI。(責任編輯:A6)



[火星人 ] 使用 Grails 構建富 Internet 應用程序,第 1 部分: 使用 Grails 和 Flex 構建 Web 應用程序已經有3281次圍觀

http://coctec.com/docs/linux/show-post-68923.html