Delphi Labs: DataSnap XE - jQueryMobile Web フロントエンド

By: Chikako Yonezawa

Abstract: この記事では、スタンドアロンの Delphi DataSnap サーバーから取得した InterBase XE の “Employees” データベースデータを表示するための jQueryMobile Web フロントエンド アプリケーションを構築するために Delphi XE を使用します

    はじめに

このラボでは、jQuery Mobile JavaScript ライブラリを使用し、Delphi XE で、DanaSnap サーバーから取得したデータベースのデータを表示する Web アプリケーションを構築します。この記事を執筆している時点での jQuery Mobile は、開発中のアルファ版のため、現時点 (2011年3月) で利用可能な機能が、最終的なリリース版と異なる部分があるかもしれません。

この記事の中で、http://jquerymobile.com/demos/1.0a3/ から利用可能な jQuery Mobile Alpha 3 リリースを使用しています。

このチュートリアルの最終目的は、基となる InterBase XE のサンプル “Employee” データベースのデータを提供する Delphi XE DataSnap スタンドアロン データサーバーと、データを表示するために jQuery Mobile を使用する WebBroker Delphi XE スタンドアロン Web アプリケーションのクライアントから構成される、多層で、スケーラブルな DataSnap システムのサンプルの作成です。

このチュートリアルの前に、以前の Delphi Labs の記事で説明した “WebBroker jQueryMobile ボイラープレート” (http://edn.embarcadero.com/article/41325) があります。

Hide image
Click to see full-sized image

expand view>>

    jQueryMobile を理解する

ピュアな JavaScript でコーディングすることは、とても退屈で、この作業を簡単にするためのたくさんのフレームワークや、ライブラリがあります。最も人気のある JavaScript ライブラリの一つに「迅速で簡潔」で「迅速な Web 開発のための、HTML ドキュメントの走査、イベント処理、アニメーション、Ajax処理を簡素化」する、jQuery (http://jquery.com) があります。

jQuery の世界の中で、最も最近のプロジェクトの一つに、iPhone 、Android やその他のスマートフォン上で利用可能なWebブラウザ向けに最適化された、この人気のライブラリの “mobile” バージョンがあります。それは、“mobile” なルックアンドフィールを持った Web ページを構築するためのもので、 HTML5 や、CSS3 のような最新の Web 標準を使っている jQuery コアの上に位置するレイヤーです。

出発点となる “WebBroker jQueryMobile ボイラープレート” プロジェクトは、Embarcadero の Code Central (http://cc.embarcadero.com/item/28254) より使用することができます。jQueryMobile で動的に表示されるようにクライアントデータを提供する DataSnap サーバーアプリケーションを構築する時です。

    DataSnap サーバーアプリケーションを構築する

InterBase XE のサンプル “Employees” データベースの “Customers” テーブルからデータを読み込む、スタンドアロンの Delphi DataSnap VCL フォームアプリケーションサーバーを作成します。顧客のレコードが “会社名” でソートされるようにしますので、クライアント側でソートを実行する必要はありません。

プロジェクトマネージャのプロジェクトグループノード上でマウスの右ボタンをクリックし、“新規プロジェクトを追加” を選択し、「新規作成」ダイアログの “Delphi プロジェクト – DataSnap Server” から “DataSnap Server” アイコンをクリックします。

Hide image
Click to see full-sized image

expand view>>

ウィザードの最初の画面はデフォルトの “VCL フォームアプリケーション” のままとします。

2番目の画面では、サンプルメソッドを使用しないので、サンプルメソッドの生成のためのオプションのチェックは外します。

Hide image
Click to see full-sized image

expand view>>

次の画面は、デフォルトのポート番号のままにします。

最後に選択する内容はとても重要です。サーバーメソッドクラスのデフォルトの上位型ではなく、一番下の“TDSServerModule”を選択します。

Hide image
Click to see full-sized image

expand view>>

[完了] ボタンをクリックします。

メニューの [ファイル|すべて保存] をクリックします。メインフォームのユニットは “FormServerUnit” と命名し、サーバーコンテナユニットと、サーバーメソッドユニットはデフォルトの名前のままで保存します。また、プロジェクトは、”DataServerApp”、プロジェクトグループは “jQueryScalableSystem” と命名して保存します。

InterBase のサンプルデータベースに接続するための、接続設定が必要です。

「データエクスプローラ」内の “INTERBASE” ノードを選択します。そして、マウスの右ボタンをクリックして表示されるポップアップメニューより [新規接続を追加] を選択して名前を “IBEMPLOYEE” とします。

作成した “IBEMPLOYEE” を選び、マウスの右ボタンを押して表示されるポップアップメニューの中から [接続の変更] を選択します。

データベースファイルへのパスを入力します。InterBase XE をデフォルトのままインストールした場合は、“C:\Embarcadero\InterBase\examples\database\employee.ib” となります。

[テスト接続] のボタンをクリックし、データベースサーバーが起動しており、接続が確立できることを確認します。

データサーバーの実装は、コーディングを必要としません。選択した RDBMS インスタンスへのアクセスを提供する “TSQLConnection” コンポーネント、データベースから会社名でソートされた顧客情報を返却するクエリーが設定された “TSQLQuery” コンポーネント、クライアントアプリケーションから接続される “TDataSetProvider” コンポーネントを使用します。

サーバーメソッドモジュールに “TSQLConnection” コンポーネントを素早く追加するには、データエクスプローラから、サーバーメソッドユニット上へ、それをドラックします。これが、すべての必要なプロパティが自動的に設定される最速の方法です。代わりに、手作業で “TSQLConnection” コンポーネントを追加し、それに応じたプロパティを設定することもできます。

実際のデータベース接続情報は、データベース接続オブジェクトの “Params” プロパティに組み込まれています。

F6 キーを押し、”IDE インサイド” を表示します。そして、サーバーメソッドユニットへ ”TSQLQuery” コンポーネントを追加します。

すでにデータモジュール上にある接続オブジェクトを指すように、その “SQLConnection” プロパティを設定します。

“CUSTOMERS” テーブルから会社名でソートされた顧客情報を取得するための SQL 文を提供する必要があります。

データエクスプローラの “IBEMPLOYEE” データベース上で、マウスの右ボタンをクリックし、SQL 文を対話形式で作成するために「SQL ウィンドウ」を選択します。

右側にあるリストから “CUSTOMERS” テーブルを真ん中のメインウィンドウ上にドラッグします。

クエリーにすべてのフィールドを含ませるため、テーブル内のすべてのフィールドにチェックを付けます。

会社名を含んでいる “CUSTOMER” フィールドのソート順序として “Ascending” を選択します。

クエリーのテキストを選択し、マウスの右ボタンを押して表示されるポップアップメニューから、[コピー]を選択し、クリップボードにコピーします。

「SQL ウィンドウ」を閉じます。

Hide image
Click to see full-sized image

expand view>>

クエリー (TSQLQuery) コンポーネントの “SQL” プロパティを開き、そこにこの SQL 文を貼り付けます。

SQL 文が正しく動作するかを確認するために “Active” プロパティをクリックして “true” に設定できることを確認した後、このプロパティのチェックマークを外します(Falseにします)。

クエリーコンポーネントを “sqlqCustomersByCompanyName” とリネームします。

“TDataSetProvider” コンポーネントをモジュールへ追加します。

プロバイダ (TDataSetProvider) コンポーネントを “dspCustomersByCompanyName” とリネームします。

その “DataSet” プロパティに “sqlqCustomersByCompanyName” クエリーコンポーネントを設定します。

Hide image
Click to see full-sized image

expand view>>

これだけです! Delphi の DataSnap データサーバーの準備が整いました。

サーバーアプリケーションを実行させるために、緑色の三角アイコンをクリックします。

このチュートリアルが終わるまで、つまり、クライアントアプリケーションを開発し、その動作確認が終わるまで、サーバーは起動したままでなければなりません。

    DataSnap クライアントデータアクセスを追加する

今、コードエディタで 顧客データモジュールのコードを確認するのであれば、F12 キーを押して、データモジュールに非ビジュアルコンポーネントを追加することができるフォームデザイナから切り替えます。VCLフォームとは異なり、データモジュールは、それらのコンテンツを表示するようにデザインされていません。データアクセス機能のためのさまざまな非ビジュアルコンポーネントを配置することを可能にする設計時用の画面を持っています。

F6 キーを押して、「IDE インサイト」を表示します。 “TSQLConnection” とタイプしてそれを見つけたら、従業員データ用に接続を追加します。

わかりやすいように、この新しい接続を “DSSQLConnectionEmployees” と命名します。

この時、常に “LoginPrompt” プロパティのチェックを外すことは良い考えです。これにより “Login” ダイアログの表示は行われません。

“Driver” プロパティを “DataSnap”に変更します。これにより、 “Driver” プロパティの中のDataSnap接続のための固有のサブプロパティにアクセスできるようになります。同じコンピュータ上で、両方のアプリケーションを動作させているので “HostName” プロパティには “localhost” を指定します。実際のシナリオでは、動作しているマシンの IP アドレス、または、DNS名となります。

では、どのようにリモートデータサーバーへ接続するのでしょうか?

サーバー側では、VCL の “TClientDataSet” コンポーネントからのクライアントリクエストを受信する準備が整った “TDataSetProvider” コンポーネントがフォーム上にあります。

ウィザードで選んだサーバー側の実装の基底クラスは、MIDAS の時代からある “IAppServer” インターフェースを実装している “TDSServerModule” でした。

このインターフェースは、データプロバイダとクライアントデータセットのコンポーネントが、ネットワークを介してデータパケットを送受信するのに使用するメソッドを含んでいます。

このシナリオ内で、サーバーへアクセスするクライアントプロキシコードを生成する必要はありません。なぜなら、サーバー側の機能が “IAppServer” インターフェースによって、すでに定義されているからです。

  IAppServer = interface(IDispatch)
    ['{1AEFCC20-7A24-11D2-98B0-C69BEB4B5B6D}']
    function  AS_ApplyUpdates(const ProviderName: WideString; Delta: OleVariant;
                              MaxErrors: Integer; out ErrorCount: Integer; var OwnerData: OleVariant): OleVariant; safecall;
    function  AS_GetRecords(const ProviderName: WideString; Count: Integer; out RecsOut: Integer;
                            Options: Integer; const CommandText: WideString;
                            var Params: OleVariant; var OwnerData: OleVariant): OleVariant; safecall;
    function  AS_DataRequest(const ProviderName: WideString; Data: OleVariant): OleVariant; safecall;
    function  AS_GetProviderNames: OleVariant; safecall;
    function  AS_GetParams(const ProviderName: WideString; var OwnerData: OleVariant): OleVariant; safecall;
    function  AS_RowRequest(const ProviderName: WideString; Row: OleVariant; RequestType: Integer;
                            var OwnerData: OleVariant): OleVariant; safecall;
    procedure AS_Execute(const ProviderName: WideString; const CommandText: WideString;
                         var Params: OleVariant; var OwnerData: OleVariant); safecall;
  end;

データモジュールへ “TDSProviderConnection” コンポーネントを追加します。

私は、それを “DSProviderConnectionEmployees” とリネームしました。そして、その “SQLConnection” プロパティに、接続コンポーネント “DSSQLConnectionEmployees” を設定します。

ドロップダウンリストから直接、可能なサーバークラスの名前を選択することができたら良かったのですが、接続したいサーバー上のサーバークラスの実際の名前を入力する必要があります。この場合は “TServerMethodsIBEmployees” です。

データモジュールへ “TClientDataSet” コンポーネントを追加します。

その “RemoteServer” プロパティを “DSProviderConnectionEmployees” に設定し、すべてが正しく設定され、接続されているのであれば、 “ProviderName” プロパティのドロップダウンリストで、利用可能なデータセットプロバイダ名が表示されます。

サーバー上に複数のプロバイダ、および、サーバーへの同じ接続を共有する複数のプロバイダ接続コンポーネントを持つことができることに注意してください。プロバイダ名として、 “dspCustomersByCompanyName” を選択します。

クライアントデータセットコンポーネントを “cdsIBCustomers” にリネームし、 “Active” プロパティを “true” に設定し、すべてが正しく接続されるかを試し、その後 “false” に設定してください。設計時には “Active” のままにしたくはありません。データが必要になったときに有効 (“true”) にし、その後再び無効 (“false”) にします。

データアクセスコードを簡素化するために、クライアントデータセットフィールドを永続化するのは良い方法です。 “cdsIBCustomers” クライアントデータセットをダブルクリックして、 “項目の設定” ダイアログを表示します。ダイアログ内で、マウスの右ボタンをクリックし、 “すべてのフィールドを追加する” を選択します。

クライアントデータセットをオープンし、クライアントから顧客データを取得し、jQueryMobile 固有の機能を使用して HTML として整える “GetHtmbody” メソッドの実際の実装を提供する必要があります。

    Delphi で jQueryMobile コードを動的に生成する

このチュートリアルの主な焦点は、Delphi と DataSnap ですので、jQueryMobile の詳細については解説しません。

Web 上には興味深く有益な jQueryMobile リソースがたくさんあります。雑誌 “Practical Web Design” のFeb2011号に含まれていた http://net.tutsplus.com から、とても有益な “Simple development with jQuery Mobile (jQuery Mobile での簡単な開発)” マルチメディアチュートリアルを見つけました。jQueryMobile の基本情報を提供する他のリソースについては、このチュートリアル: http://miamicoder.com/2011/creating-a-website-using-jquery-mobile-part1/ で見つけました。

HTML ページの動的な作成を行うデータモジュールの最終バージョンのソースコードが以下にありますのでご覧ください。

以下のすべてのコードを選択し、Delphi のエディタ内にコピー&ペーストしてください。

unit DataModuleEmployeesUnit;

interface

uses
  SysUtils, Classes, DBXDataSnap, DBXCommon, DB, SqlExpr, DBClient, DSConnect;

type
  TCustomerData = record
    CUST_NO: Integer;
    CUSTOMER: String;
    CONTACT_FIRST: String;
    CONTACT_LAST: String;
    PHONE_NO: String;
    ADDRESS_LINE1: String;
    ADDRESS_LINE2: String;
    CITY: String;
    STATE_PROVINCE: String;
    COUNTRY: String;
    POSTAL_CODE: String;
    ON_HOLD: String;
  end;

  TDataModuleEmployees = class(TDataModule)
    DSSQLConnectionEmployees: TSQLConnection;
    DSProviderConnectionEmployees: TDSProviderConnection;
    cdsIBCustomers: TClientDataSet;
    cdsIBCustomersCUST_NO: TIntegerField;
    cdsIBCustomersCUSTOMER: TStringField;
    cdsIBCustomersPHONE_NO: TStringField;
    cdsIBCustomersCONTACT_LAST: TStringField;
    cdsIBCustomersCONTACT_FIRST: TStringField;
    cdsIBCustomersADDRESS_LINE1: TStringField;
    cdsIBCustomersADDRESS_LINE2: TStringField;
    cdsIBCustomersCITY: TStringField;
    cdsIBCustomersSTATE_PROVINCE: TStringField;
    cdsIBCustomersCOUNTRY: TStringField;
    cdsIBCustomersPOSTAL_CODE: TStringField;
    cdsIBCustomersON_HOLD: TStringField;
  private
    FCustDetailPages: string;
    function GetCustList: string;
    function GetCustDetailPages: string;
    procedure OutputCustomerDetailPage(const c: TCustomerData);
  public
    function GetHtmlBody: string;
  end;

//var
//  DataModuleEmployees: TDataModuleEmployees;

implementation

{$R *.dfm}

{ TDataModuleEmployees }

function TDataModuleEmployees.GetHtmlBody: string;
begin
  Result :=
 '<!-- Start of main page -->'
+'<div data-role="page" id="main">'

+'  <div data-role="header">'
+'       <h1>Delphi in the Cloud</h1>'
+'  </div><!-- /header -->'

+'  <div data-role="content">'

+'<h2>Welcome to DelphiLabs!</h2>'
+'<p>Press on the button below to find information'
+' about customers of a fictional company.</p>'

+'        <ul data-role="listview" data-theme="c" data-inset="true">'
+'          <li><a href="#customersMain"></a>Customers</li>'
+'          <li><a href="#about"></a>About</li>'
+'        </ul>'
+'  </div><!-- /content -->'

+'  <div data-role="footer">'
+'       <h6>DelphiLabs DataSnap Tutorial</h6>'
+'  </div><!-- /footer -->'
+'</div><!-- /page -->'

+'<!-- Start of about page -->'
+'<div data-role="page" id="about">'

+'  <div data-role="header">'
+'       <h1>About</h1>'
+'  </div><!-- /header -->'

+'  <div data-role="content">'

+'<h2>Welcome to DelphiLabs!</h2>'
+'<p>The page you just see has been created as a demo'
+' project for DelphiLabs "DataSnap" jQueryMobile tutorial'
+' and deployed to a virtual machine running in <a href="http://aws.amazon.com/">Amazon EC2</a>.</p>'
+'<p>Sample "Customers" data comes from <a href="http://www.embarcadero.com/products/interbase">InterBase XE</a> demo database'
+' that is accessed through <a href="http://www.embarcadero.com/products/delphi">Delphi XE</a> standalone DataSnap server application.</p>'
+'<p>The HTML markup that you just see right now has been generated dynamically in Delphi code'
+' using <a href="http://jquerymobile.com/2011/02/jquery-mobile-alpha-3-released/">jQueryMobile (alpha3)</a>.'
+'<hr/>'
+'<p>Visit <a href="http://www.embarcadero.com/rad-in-action/delphi-labs">'
+'Embarcadero RAD-in-Action Delphi-Labs page</a> for more details!</p>'
+'  </div><!-- /content -->'
+'  <div data-role="footer">'
+'       <h6>DelphiLabs DataSnap Tutorial</h6>'
+'  </div><!-- /footer -->'
+'</div><!-- /page -->'


+'<!-- Start of customers summary page -->'
+'<div data-role="page" id="customersMain">'

+'  <div data-role="header">'
+'       <h1>Customers</h1>'
+'  </div><!-- /header -->'

+'  <div data-role="content" data-theme="b">'

+ GetCustList

+'  </div><!-- /content -->'
+'  <div data-role="footer">'
+'       <h6>DelphiLabs DataSnap Tutorial</h6>'
+'  </div><!-- /footer -->'

+'</div><!-- /page -->'

+ GetCustDetailPages

end;

function TDataModuleEmployees.GetCustList: string;
var s: string; ch: char; c: TCustomerData;
begin
  s := '<ul data-role="listview" data-inset="true" data-theme="c" data-filter="true">';

  ch := ' ';

  cdsIBCustomers.Active := true;
  try
    while not cdsIBCustomers.Eof do
    begin
      c.CUST_NO := cdsIBCustomersCUST_NO.AsInteger;
      c.CUSTOMER := cdsIBCustomersCUSTOMER.AsString;
      c.CONTACT_FIRST := cdsIBCustomersCONTACT_FIRST.AsString;
      c.CONTACT_LAST := cdsIBCustomersCONTACT_LAST.AsString;
      c.PHONE_NO := cdsIBCustomersPHONE_NO.AsString;
      c.ADDRESS_LINE1 := cdsIBCustomersADDRESS_LINE1.AsString;
      c.ADDRESS_LINE2 := cdsIBCustomersADDRESS_LINE2.AsString;
      c.CITY := cdsIBCustomersCITY.AsString;
      c.STATE_PROVINCE := cdsIBCustomersSTATE_PROVINCE.AsString;
      c.COUNTRY := cdsIBCustomersCOUNTRY.AsString;
      c.POSTAL_CODE := cdsIBCustomersPOSTAL_CODE.AsString;
      c.ON_HOLD := cdsIBCustomersON_HOLD.AsString;


      if ch <> c.CUSTOMER[1] then
      begin
        ch := c.CUSTOMER[1];
        s := s + '<li data-role="list-divider" data-theme="b">' + ch + '</li>';
      end;

      s := s +
      '<li><a href="#cust' + IntToStr(c.CUST_NO) + '">' + cdsIBCustomersCUSTOMER.AsString + '</a></li>';

      OutputCustomerDetailPage(c);

      cdsIBCustomers.Next;
    end;
  finally
    cdsIBCustomers.Active := false;
  end;

  s := s + '</ul>';
  Result := s;

end;

function TDataModuleEmployees.GetCustDetailPages: string;
begin
  Result := FCustDetailPages;
end;

procedure TDataModuleEmployees.OutputCustomerDetailPage(const c: TCustomerData);
var s: string; aStatus: string;
begin
  if c.ON_HOLD <> '' then
    aStatus := '<B> (ON HOLD)</B>'
  else
    aStatus := '';

  s :=
 '<!-- Start of customer detail page -->'
+'<div data-role="page" id="cust' + IntToStr(c.CUST_NO) + '">'

+'  <div data-role="header">'
+'       <h1>Customer Details</h1>'
+'  </div><!-- /header -->'
+'  <div data-role="content">'

+'<p><h1>' + c.CUSTOMER + aStatus + '</h1></p>'

+'<div data-role="collapsible" data-theme="b">'
+'<h3>Contact</h3>'
+'<p><h2>' + c.CONTACT_FIRST + ' ' + c.CONTACT_LAST + '</h2></p>'
+'<p>' + c.PHONE_NO + '</p>'
+'</div>'

+'<div data-role="collapsible" data-theme="b">'
+'<h3>Address</h3>'
+'<p>' + c.ADDRESS_LINE1 + '</p>'
+'<p>' + c.ADDRESS_LINE2 + '</p>'
+'<p>' + c.CITY + '</p>'
+'<p>' + c.POSTAL_CODE + ' ' + c.CITY +'</p>'
+'<p>' + c.STATE_PROVINCE + '</p>'
+'<p><b>' + c.COUNTRY + '</b></p>'
+'</div>'

+'  </div><!-- /content -->'
+'  <div data-role="footer">'
+'       <h6>DelphiLabs DataSnap Tutorial</h6>'
+'  </div><!-- /footer -->'

+'</div><!-- /page -->';

  FCustDetailPages := FCustDetailPages + s;
end;

end.

この Web アプリケーションの 実行バージョンは、この URL: http://79.125.25.31:8080/ を通してアクセスすることができます。

私のChrome Webブラウザではこのように表示されます:

Hide image
Click to see full-sized image

expand view>>

“Customers” ボタンをクリックすると、会社名でソートされた顧客一覧リストへ遷移します。上にあるフィルタフィールドを確認してください。

Hide image
Click to see full-sized image

expand view>>

顧客名のところをクリックすると、その顧客の詳細を見ることができるでしょう。

Hide image
Click to see full-sized image

expand view>>

これだけ! Delphi XE で、モバイルデバイス用のあなた独自の見栄えのよい Web サイトを構築するために、ここで説明したテクノロジーを自由にご利用ください!

    まとめ

この Delphi Labs DataSnap チュートリアルの中で、モバイルデバイス上でデータベースデータを表示するための、多層でスケーラブルなシステムを構築する実用的な手順を紹介しました。データのソースとして InterBase XE データベース、データアクセスのための中間のスタンドアロン DataSnap サーバー、モバイル Webブラウザのために最適化されたモダンな jQueryMobile JavaScript ライブラリを使用し、データを表示するために WebBroker Web アプリケーションを使用しました。

jQueryMobile ライブラリのプレリリースバージョンを使用していることに注意してください。最終的な機能は、ここで説明したものと異なる可能性があります。

DataSnap は、Delphi, C++Builder, RAD Studio の Enterprise および Architect エディションの機能です。Starter および Professional エディションには含まれていません。

    参考資料

    若干の免責事項

“Delphi Labs” では、Delphi での開発にフォーカスしていますが、ここで説明されているアプリケーションのすべては、RAD Studio の一部である C++Builder でも構築可能です。

Delphi と C++Builder は、とても密接に統合されています。双方は、同一の基盤である統合開発環境 (IDE: Integrated Development Environment) 上の異なる「パーソナリティ」であり、VCL (Visual Component Library) を共有しています。

Delphi Pascal プログラミング言語を使用するのは、私個人の好みです。C++ をより好む方は、C++Builder XE (http://www.embarcadero.com/jp/products/cbuilder) でお試し下さい!

Server Response from: ETNASC03