はじめに
企業向けシステムのWeb化の需要は絶えませんが、Web技術の進歩により、Webアプリケーションでできることも増え、同時に期待される機能も増していると感じます。ブラウザだからこの程度、といった妥協は許されず、JavaScriptなどを駆使した、結構なプログラムになっているのが現実だったりします。
そうした状況下でJavaScriptベースのフレームワークが広まり、Webアプリケーション開発者を楽にしてくれているわけですが、その中でもSencha Ext JSは、「ビジネス向けアプリケーション開発に向いている」とか「データハンドリングに長けている」「データ駆動型Webアプリだ」などと評価されています。
そこで今回、このSencha Ext JSを使って、典型的な従来型C/SアプリケーションをWeb化してみます。開発には、Ext JSアプリケーションをビジュアルに作成できるというSencha Architectを使い、コードだらけの記事にならないようにしたいと思います。
対象とするC/Sアプリケーション
対象とするアプリケーションは、顧客情報の管理を行うC/Sアプリケーションです。条件を指定して顧客一覧を表示し、詳細データを編集できます。
アプリケーションのUI要素としては、グリッドが用いられています。大量のデータを一覧表示し、スクロールバーを操作してすばやく閲覧できます。
また、詳細表示には、ポップアップウィンドウを用いており、顧客の写真データがあれば、それを表示します。詳細表示画面は、詳細データの編集を兼ねており、データを編集して[更新]ボタンをクリックすると、データが更新されます。
アプリケーションは、RDBMSに接続し、検索条件に基づいてクエリーを投げます。詳細表示では、グリッドで現在選択されているレコードのデータを表示します。[更新]ボタンをクリックすると、編集内容に基づいてデータベースを更新します。
アプリケーションとしてはやや小ぶりで、実装されている機能もわずかですが、C/SアプリケーションのWeb化という命題に、Sencha Ext JSがどのように対応できるかを検証するには十分でしょう。
Sencha Ext JSとは
Ext JSの特長
作業を始める前に、Sencha Ext JSの概要を理解しておきましょう。
Senchaは、米国のツールベンダーで、現在ではDelphi/C++Builderなどを提供するエンバカデロと同じアイデラ・グループに属しています。アイデラは、現在積極的にいろいろなツールベンダーを傘下に収めており、AssemblaやLANSA、Travis CIなども現在では同じグループに属しています。
Ext JSは、そのSenchaが提供するJavaScriptベースのフレームワークで、 豊富なコンポーネント、設計、開発、テストまでをカバーするツール群、オープンな方式で任意のデータソースにアクセスできるデータパッケージなどを特長としています。コンポーネントは100種類以上用意されており、ボタンやパネル、リストといった基本的なものはもちろん、グリッドやツリー、チャート、カレンダーといった高度なUI要素も含まれています。
データベースや他システムからデータを取得して、豊富なコンポーネントを駆使して表示する。企業向けWebシステム構築に向いていると言われるゆえんです。
Ext JSの開発スタイル
Ext JSは、JavaScriptフレームワークですので、基本的にJavaScriptを記述することになります。しかし、基本的にはコンポーネントを組み合わせてUIを構築することになるので、直接的にHTMLを記述することはほとんどありません。
コンポーネントに対する設定(コンフィグ)を行い、イベントハンドラを記述するようなスタイルで、DelphiやVisual Basic、あるいはC#のUI設計に似ています。
Senchaでは、こうした作業をビジュアルに行えるツールSencha Architectも用意しているので、JavaScriptのコード(特に設定のコード)をエディタでガリガリ記述するという苦労も必要ありません。
テストの自動化もサポート
Webアプリケーションのテストは、さまざまなブラウザで同じように動作するか、また、修正に対してデグレードなどが発生していないかを網羅的にチェックしなければならないため、結構煩雑です。Sencha Ext JSには、Sencha Testと呼ばれるテストツールが用意されており、この作業を自動化してくれます。
データアクセス機能を用意する
Web APIを作る
それでは、具体的な作業に取り掛かりましょう。最初に用意するのは、データアクセス機能です。C/Sアプリケーションでは、クライアントアプリケーションが直接リモートのRDBMSに接続していました。
Ext JSによるWebアプリケーションはWebブラウザ上で動作するわけですから、ここから直接RDBMSに接続するわけにはいきません。Ext JSからは、任意のWeb APIにアクセスすることができるので、クエリーの結果をJSONデータで返すWeb APIと、特定のレコードを更新するWeb APIを実装しておきます。
Ext JS側での処理は後述しますが、データパッケージと呼ばれる機能を使うため、Web APIを扱うためのコーディングは実質不要です。
実装方法は自由
Web APIの実装は本記事の中心的な話題ではないので、あまり詳細は説明しません。Ext JSからすれば、REST/JSONのようなオープンなプロトコル/データ形式であれば、APIの実装方法は問いません。従って、以前から使用している言語を用いたり、サーバーサイドアプリケーションの構築に最適な言語を選択したりするなど、自由です。
今回は、元のデータアクセスがC/Sアプリケーションであったということもあるので、C/Sアプリケーションを実装していたDelphiをそのまま使うことにします。
Delphiにも、現在ではWeb APIを実装するためのテクノロジーが用意されており、コンポーネントを用いた効率的な開発で、主要なRDBMS、クラウドサービス、既存のシステムなどと連携するWeb APIを簡単に構築することができます。
Delphiで実装したWeb API
以下は、Delphiで実装したWeb APIの開発画面です。パネルのようなところにいくつかのアイコンが並んでいますが、これが、データアクセスやJSONデータ処理を行うためのコンポーネントです。UIはないのですが、ロジックもビジュアル操作で構築できるのです。
Web APIは以下のようなコードによって実装されています。DelphiにはREST API実装やJSONデータの受け渡しに特化したコンポーネントTEMSDataSetResourceが用意されており、データベースのCRUD操作を行うAPIを、コーディングなしに実装できます。TEMSDataSetResourceよりも細かい制御が必要な場合は、応答するprocedure内のコードで実装できます。
type [ResourceName('WebAPI')] // API のリソース名定義 TWebAPIResource1 = class(TDataModule) // TEMSDataSetResource は操作とサフィックスを定義するだけでAPIとして動作する [ResourceSuffix('get' , 'customers/{id}')] [ResourceSuffix('put' , 'customers/{id}')] EMSDataSetResource1: TEMSDataSetResource; published // データの一覧取得を TEMSDataSetResource よりも細かく制御する場合の定義 [ResourceSuffix('customers/')] procedure Get(const AContext: TEndpointContext; const ARequest: TEndpointRequest; const AResponse: TEndpointResponse); end; implementation procedure TWebAPIResource1.Get(const AContext: TEndpointContext; const ARequest: TEndpointRequest; const AResponse: TEndpointResponse); var CustomerJSONResponse: TJSONObject; begin // ここで検索の実行やJSONデータの生成などを実装する AResponse.Body.SetValue(CustomerJSONResponse,True); end;
このAPIには http://localhost:8080/WebAPI/customers/ としてアクセスでき、データの取得はGETメソッドに対して、次のJSONデータを返します。またデータの更新をPUTで受け付けます。
{ “result”: [ { "CUSTOMER_ID": 1, "NAME": "神野 天音", "NAMEPHONETIC": "かみの あまね", "EMAIL_ADDRESS": "kamino_amane@example.com", "BIRTHDAY": "1995-02-14T00:00:00.000+09:00", "PREFECTURE": "東京都", "PHONE_NUMBER": "03-4540-4148", "MOBILEPHONE": "03-4540-4148" }, … … … ], “total”: 50000 }
“total”は、このデータセット全体のレコード数を示しています。
Sencha ArchitectでUIを設計する
Senchaバージョンのイメージ
では、いよいよSencha Ext JSを使い、次の仕様でWebアプリケーションのユーザーインターフェイスを実装します。
- ツールキットは「Modern」を使用し、表示の切り替えはカードレイアウトで行う
- データの一覧表示画面と詳細表示画面を実装する
- 一覧表示画面では顧客ID、氏名、ふりがな、電話番号を表示し、顧客名で絞り込み検索を行う
- 詳細表示画面では顧客1名分の情報を単票で表示し、情報の編集が行える
Ext JSのツールキットには、ModernとClassicの2種類があります。従来型のWebブラウザをターゲットとしたClassicに対し、Modernはスマートフォンやタブレットでも最適なUIを提供するマルチデバイス対応になっています。また、これらのデバイスではポップアップウィンドウによる表示よりも画面全体を遷移されるレイアウトがよく用いられるため、その一つのやり方としてカードレイアウトを使用します。
先に完成イメージをご覧いただきましょう。一覧表示の項目を選択すると詳細表示に切り替わり、ここでデータの編集も行えます。
Sencha ArchitectでUIを設計
Sencha Architectで最初に行う作業は、 Panelの配置です。PanelはExt JSでよく用いられるコンテナの一つで、ヘッダを持つ表示コンポーネントです。Panelには別のコンポーネントを配置して表示させたり、HTMLを直接記述して表示させたりすることができます。ここでは、GridやFormを並べて表示させるために用います。
Sencha Architectの開発画面の画面右上にあるToolboxから、Panelコンポーネントを選んで設計画面または画面左側のProject InspectorのViewsにドロップします。
このPanelの上に、GridとForm Panelを配置します。Toolbox上部の検索窓からコンポーネントを絞り込み検索すれば必要なコンポーネントがすぐに見つかります。
コンポーネントのプロパティ設定
配置したコンポーネントの外観や振る舞いを変更するには、プロパティを設定します。今回、Panelはカードレイアウトで使用します。この設定を行うには、layoutプロパティをcardに変更します。
画面右下のConfigペインからlayoutプロパティを探し、選択肢からcardを選びます。また、Gridに検索用のSearchFieldを追加したり、カラムの名前を変えたりするなどの調整を行います。
詳細表示用のForm PanelにはText FieldやButtonを配置して、ラベルの名前やボタンの表示を調整します。
このようにコンポーネントのドラッグ&ドロップとプロパティの設定で画面が作成されました。この画面をブラウザで表示するためのJavaScriptコードはSencha Architectが自動的に生成します。実際に生成されたコードはキャンバスのセレクタを「Code」に切り替えると確認できます。
データパッケージを使う
Model、Store、Proxy
Sencha Ext JSに搭載されたデータパッケージは、Web APIによって供給されるデータの定義、取得、保管といった操作を担当します。データパッケージを用いることで、JavaScriptコードを記述することなく、任意のデータをグリッドをはじめとするUIコントロールやグラフに表示させることができます。データの読み込みやキャッシュもデータパッケージが担当するので、大量のデータであっても、ユーザーの操作を妨げることなく、ハイパフォーマンスで読み込めます。
-
Model
データのフィールド定義やレコード単位での操作が行えます。各フィールドでは型や値の検証機能などを定義でき、データを安全に取り扱えます。 -
Store
データの保管場所です。Web APIから取得したデータはStoreに保存され、Gridなどの表示に利用できます。アプリケーションによるStoreの変更は必要に応じてWeb API側に同期したり、変更結果を破棄したりできます。 -
Proxy
Ajax、JSONP、RESTなどのAPIやWeb Storageに対するアクセスを提供するコンポーネントです。APIから取得するデータは JSON、XML、ARRAYのためのReader/Writerがサポートされます。
Modelの定義
今回のデータのModel定義をAPIが返すJSONの内容にあわせて作成します。画面左側のProject InspectorでModelを追加し、画面右側のConfigペインでフィールド名などのプロパティを設定します。
StoreとProxyの設定
Ext JSのStoreは、利用するAPIの仕様によっていくつかのバリエーションがありますが、基本的な動作を確認するために、今回は基本クラスのExt.data.Storeを用います。Project InspectorにStoreを追加すると、アプリケーション全体で利用できるグローバルなストアとして利用できます。作成したStoreにはProxyとReaderを追加します。
設定が完了したら、MyStoreの横に表示されているプレビューボタン(目玉アイコン)をクリックすると、APIから取得できるデータを確認できます。
このように作成したStoreをGridのStoreプロパティで指定し、GridのそれぞれのColumnで表示するフィールドをdataIndexプロパティ指定すると、APIから取得したデータは設計画面のGridで表示できることが確認できます。
データの絞り込み検索
一覧表示に絞り込み検索の機能をつけるには、SearchFieldにEvent Bindingを行います。また入力パラメータをサーバに送信するには、Proxy.extraParamを設定してStore.reload()を実行します。これでAPIからは絞り込み検索の結果が取得でき、Gridに表示されます。
データの編集
データの編集は、Grid選択時に別のFormを表示して行います。Gridで選択された行のデータをビューモデルにセットしてFormから利用できるようにします。
onGridSelect: function(dataview, selected, eOpts) { this.getViewModel().set('record', selected[0]); this.getView().animateActiveItem(1,'slide'); }
Formの側ではText FieldのValueに {record.NAME} のように記述してマグネット型のアイコンをオンにすれば、ビューモデルのレコードが表示されるようになります。同様の作業を他のText Fieldにも実施します。
[戻る]ボタンや[保存]ボタンはFormにToolbarを置いた上に配置します。textプロパティやiconClsプロパティでボタンの表記やアイコンを設定できます。
また、それぞれのボタンを押したときの処理は、Gridの場合と同様にEvent Bindingで設定します。[戻る]ボタンでは、Storeへの変更状態を破棄してグリッド表示に戻る処理を実装します。
onCancelButtonTap: function(button, e, eOpts) { let store = this.getViewModel().getStore('MyViewModelStore'); store.rejectChanges(); this.getView().animateActiveItem(0,{type:'slide', direction: 'right'}); }
[保存]ボタンでは、storeへの変更状態を WebAPI側に反映させるために、sync()を行ってグリッド表示に戻るようにします。
onSaveButtonTap: function(button, e, eOpts) { let store = this.getViewModel().getStore('MyViewModelStore'); store.sync(); this.getView().animateActiveItem(0,{type:'slide', direction: 'right'}); }
これでデータの一覧表示、詳細表示、保存、キャンセルの機能が一通り実装できました。さらにGridにPaging Toolbar Pluginを追加すると、ページング操作のためのツールバーが表示され、データ全件の閲覧や詳細表示、編集が行えるようになります。
アプリケーションの動作を確認する
動作するアプリケーションがいったんできあがりました。Sencha Ext JSに用意されたコンポーネントやデータパッケージを用いることで、C/Sアプリケーションで提供してきた機能を容易に実装できることが理解できたと思います。
作成したアプリケーションをSencha Architectから実行するには、プレビューボタンをクリックします。ブラウザでアプリの動作を確認できます。
一覧表示の行を選択すると、URLの遷移なしに検索の実行や詳細画面への切り替わりが行え、デスクトップアプリケーションと同様に操作できることが確認できます。
ところが、この状態はC/Sアプリケーションとは異なり、画面下部のツールバーでのページング操作が必要です。このツールバーはスクロール操作が可能ですが、元のC/Sアプリケーションと比べると操作性で若干劣るように見えます。C/Sアプリケーションと同様の操作性を求めるユーザーの声には応えたいですよね。
Ext JSでは、このようにスクロールバーだけで操作させたい場合には、VirtualStoreというストアを利用します。
VirtualStoreの基本的な設定はStoreと同様でProxy、Readerを組み合わせます。設定すべきプロパティ(autoLoad、store.model、proxy.url、reader.rootProperty)も同じです。
ただし、VirtualStoreはRead Onlyでの利用となるため、通常のStoreとは制御方法が少し異なります。そのため検索やGridの選択、[保存]ボタン、[戻る]ボタンに関するイベントハンドラの実装を修正します。
onSearchfieldKeyup: function(textfield, e, eOpts) { if (e.getKey() === e.ENTER) { let store = Ext.getStore('MyVirtualStore'); store.proxy.extraParams = { 'name':textfield.getValue() }; store.reload(); } } onGridSelect: function(dataview, selected, eOpts) { let store = dataview.getStore('MyVirtualStore'); let record = store.getAt(selected); this.getViewModel().set('record', record); this.getView().animateActiveItem(1,'slide'); } onCancelButtonTap: function(button, e, eOpts) { let record = this.getView().getViewModel().getData().record; let store = Ext.getStore('MyVirtualStore'); store.reload(); this.getView().animateActiveItem(0,{type:'slide', direction: 'right'}); } onSaveButtonTap: function(button, e, eOpts) { let record = this.getView().getViewModel().getData().record; let store = this.getStore('MyVirtualStore'); store.sync(); record.save(); // VirtualStore 自体は ReadOnly なので、かわりに Model の record を操作する store.reload(); this.getView().animateActiveItem(0,{type:'slide', direction: 'right'}); }
これでC/Sアプリケーションと同様の操作性が実現でき、満足のいくWebアプリケーションを提供できるようになりました。
まとめ
従来型のC/Sアプリケーションでは、大量のデータを一度に表示し、その画面上でデータを効率よく処理していくことで生産性を高めていたと言えます。一方、Webアプリケーションとして実装した場合、Sencha Ext JSのような技術を用いないと、ページ遷移を伴うものとなり、操作性では数段階劣るものと映ります。
Sencha Ext JSを使って実装したWebアプリケーションは、古いWebアプリケーションの操作とは異なり、単一のページで処理を継続できる構成になっています。このような構成は、シングルページアプリケーション(SPA)と呼ばれ、Webアプリケーションをあたかもデスクトップアプリケーションのように操作できるようにします。
C/SアプリケーションをWeb化したいといった要求に応える場合、個々のUI要素の検討も重要ですが、操作性に一番影響を与えるものとして、ページ遷移を伴うべきか否か、言い換えれば部分的ないしは全体的なSPAの導入を検討するべきでしょう。
Sencha Ext JSによるWeb化の試みはうまくいったように見えます。ただ、Webアプリケーションとしての特性や能力の限界、また一方でその利点といったところを熟考することで、より確実かつ最適な実装が可能になるのだと思います。
セミナー情報
今回、企業向けWebアプリケーション構築にフォーカスしたセミナーを開催。ビジネスアプリケーションで必要となる各種ユーザーインターフェイスを、データハンドリングに長けたSencha Ext JSを用いて構築し、どこまでユーザーの要求に耐えられるかをビジネスケースを例に、検証します。同時に、Webアプリケーション構築の開発サイクル全体で、どのような点に注意すべきか、陥りがちな落とし穴とその対策についても紹介します。
開催概要
- 主催 エンバカデロ・テクノロジーズ
- 日時 2019年7月30日(火)14:00~17:00(13:40受付開始)
- 会場 エンバカデロ飯田橋オフィス セミナールーム(東京・飯田橋)地図
- 参加費 無料(事前登録制)