CUBAプラットフォームのホームアカウンティング。 パート2





これは、 CUBAプラットフォームで作成された小さなアプリケーションのデバイスに関する記事の続きです。 このアプリケーションは、家計簿の会計システムであり、2つの目的で作成されました。1つは、実際に私の会計を説明するため、もう1つは、プラットフォームの機能を簡単な例で説明するためです。



最初の部分では、アプリケーションの主要部分、つまりデータモデル、中間層ビジネスロジック、Generic UIプラットフォームのテクノロジで作成された画面について説明しました。 約束された第2部では、汎用UIのテーマを変更する方法、ビジュアルコンポーネントの動作を変更する方法、およびJavaScriptで記述されたモバイルデバイス用の追加UIのデバイスについて説明します。







まず、アプリケーションが解決するタスクを思い出させてください。

  1. どの時点でも、現金、カード、預金、債務など、あらゆる種類の現金の現在の残高が表示されます。
  2. 収入と支出のカテゴリごとにレポートを生成し、特定の期間にお金が何に使われたのか、どこから来たのかを調べることができます。


アプリケーションのソースコードはGitHubにあり、アセンブリと起動手順は前の記事にあります。 CUBAプラットフォームは無料ではありませんが、自宅で使用するには無料のライセンスで5つの同時接続で十分であるため、誰かがそれを使用したい場合はお願いします。



アプリケーションを調査および改良するには、CUBA Studio、IntelliJ IDEA、およびCUBAプラグインをダウンロードしてインストールすることをお勧めします



UIテーマ



簡単なものから始めましょう-メインUIアプリケーションデザインのテーマを変更およびカスタマイズする方法。



記事の最初の部分がリリースされてからかなりの時間が経過し、新しいバージョンのプラットフォーム5.3をリリースすることに成功しました。このバージョンでは、新しいHaloテーマが追加されました。 これはVaadinフレームワークのValoテーマに基づいており、見栄えがよく、拡張性に優れていることに加えて、カスタマイズも非常に簡単です。 特に、配色を完全に変更するには、SCSS変数をいくつか再定義するだけです。



そのため、アプリケーションをプラットフォームの新しいバージョンに転送するだけで十分でした。システムユーザーは、 [ヘルプ]> [設定]ウィンドウでテーマを選択できます:古いハバナまたは新しいHalo。



新しいトピックでは、アプリケーションは次のようになります。





これらはデフォルトのテーマ設定です。 私が追加した唯一のことは、左パネルの最終バランスの数字のスタイルです。 これを行うには、合計を表示するために使用されるLabelコンポーネントのLeftPanelクラスで、スタイル名が設定されます:totals。 次に、CUBA Studioを使用して、Haloテーマ拡張機能を作成しました。 同時に、テーマ/ HaloサブディレクトリがWebモジュールのプロジェクトに表示され、テーマ設定をオーバーライドできます。 halo-ext.scssファイルで合計スタイルを定義するCSSコードを追加しました:



.v-label-totals { font-weight: bold; }
      
      





セレクタのクラス名は、実行中のアプリケーションのブラウザを使用して決定するのが最も簡単です。



Haloテーマは好みに合わせて簡単に調整できます。 私には設計能力がないので、暗い配色の定義の例を示します。





このようなテーマの変更では、次の変数値がhalo-ext-defaults.scssファイルに追加されます。



 $v-background-color: #444D50; $v-support-inverse-menu: false;
      
      





同様に、テーマ変数を使用して、さまざまな要素、インデント、テーブル設定などのフォントサイズを変更できます。



Haloテーマの興味深い機能は、標準プラットフォームコンポーネントのアイコンにFont Awesomeグリフを使用することです。 スケーリングの際の明らかな利点に加えて、これにより、背景色に応じてアイコンの色を自動的に選択することもできます。上記の例では、暗い背景に新しいアイコンセットを定義する必要はありませんでした。



ビジュアルコンポーネントのカスタマイズ



次に、ビジュアルコンポーネントの動作、より具体的には、ブラウザで実行されるクライアントコンポーネントコードを変更する可能性に移りましょう。 WebクライアントのCUBAコンポーネントはVaadinフレームワーク技術を使用して実装されているため、コンポーネントのクライアント部分を開発する主な方法は、Javaを使用し、GWTを使用してJavaScriptでコンパイルすることです。 これは広範なトピックであり、別の記事が必要なので、ここでは、JavaScriptに追加のロジックを実装してコンポーネントを拡張する、より簡単なオプションを検討します。



問題を定式化します。 現在、操作量フィールドを使用して、操作を保存するときにAmountCalculatorクラスを使用してスクリーンコントローラーで計算される算術式を入力できます。 つまり、これはすべてサーバー側で行われます。 タスクは次のとおりです。金額フィールドで「=」キーを押して、クライアント(ブラウザ内)で式を計算し、すぐにフィールドに結果を表示します。



JavaScriptで算術式を計算するのは簡単です。正規表現を使用して式の有効性を確認し、eval()で実行します。 主な質問は、JavaScriptをアプリケーションのクライアントコードの適切な場所に接続する方法です。 バージョン7以降、Vaadinには、このコンポーネント、具体的にはAbstractJavaScriptExtensionクラスを拡張するための特別なメカニズムがあります。 私はそれを利用しました。



Webモジュールは、AbstractJavaScriptExtensionから継承したJavaクラスCalcExtensionを作成しました。 彼がすることは、コンストラクタでTextFieldインスタンスを取得し、それを継承されたextend()メソッドに渡すことです。つまり、独自の方法で「拡張」します。 さらに、このクラスには、クライアントロジックがJavaScriptで記述されているファイルの名前を持つJavaScriptアノテーションがあります。



 // CalcExtension.java @JavaScript("textfieldcalc.js") public class CalcExtension extends AbstractJavaScriptExtension { public CalcExtension(TextField textField) { super.extend(textField); } }
      
      





拡張機能は、AmountCalculatorクラスのinitAmount()メソッドでTextField CUBAコンポーネントから取得したcom.vaadin.ui.TextFieldコンポーネントに接続されます。



 // AmountCalculator.java public void initAmount(TextField amountField, BigDecimal value) { com.vaadin.ui.TextField vTextField = WebComponentsHelper.unwrap(amountField); new CalcExtension(vTextField); ...
      
      





textfieldcalc.jsファイルは、webモジュールのweb / VAADINディレクトリにあります。 アプリケーションをビルドすると、WebアプリケーションのVAADINディレクトリに自動的にコピーされます-GWTウィジェットセット、テーマ、その他のリソースもあります。 グローバル関数は、Java拡張機能クラスのフルネームに対応する名前でファイルに定義されますが、ピリオドはアンダースコアに置き換えられます。



 // textfieldcalc.js window.akkount_web_operation_CalcExtension = function() { var connectorId = this.getParentId(); var input = $(this.getElement(connectorId)); input.on("keypress", function(event) { if (event.which == 61) { event.preventDefault(); var x = event.target.value; if (x.match(/([-+]?[0-9]*\.?[0-9]+[\-\+\*\/])+([-+]?[0-9]*\.?[0-9]+)/)) { event.target.value = eval(x); } } }); }
      
      





この関数は、コンポーネントが初期化されるときに呼び出されます。この関数は、フレームワークとの対話が実行される特別なオブジェクトを指します。 このオブジェクトのメソッドは、JavaDocsのAbstractJavaScriptExtensionクラスによって説明されており、その助けを借りて、コンポーネント(この場合は入力)を実装するDOM要素を取得できます。 次に、必要なアクションを実行するjQueryを使用して、イベントリスナーが要素にハングアップします。 結果が得られます-ユーザーが「=」キー(コード61)を押すと、フィールドの式が計算され、結果がフィールドに戻されます。



オプションのJavaScript UI



CUBAでのアプリケーション開発の実践では、通常、「外部」ユーザー用に追加のフロントエンドが作成されます:クライアント、モバイル従業員など。 これらは、低レベルのネイティブテクノロジーの使用を規定する、特定のデザインと使いやすさの要件を持つWebサイトまたはモバイルアプリケーションです。 このような追加のUIには、CUBA汎用UIよりもはるかに多くの開発とサポートの努力が必要です。 幸いなことに、追加のUIに必要な機能は、原則として、エンタープライズ情報システム全体の機能よりもはるかに少ないです。



説明したアプリケーションでは、Backbone.js + Bootstrapの追加のレスポンシブUIがモバイルデバイスでの作業の利便性に役立ちます。 操作を入力して、現在の残高のみを表示できます。 カテゴリおよびディレクトリ管理によるレポートは、フィルタリングおよびソート操作も実装されていません。







最初にサーバー側を検討してください。



CUBAにはポータルモジュールが含まれています。これには、データモデルを操作するためのユニバーサルREST APIなどが含まれます。 これにより、クライアントJavaScriptアプリケーションと中間層との主なやり取りが行われます。 プロジェクトでは、現在の残高の取得と最後に使用したアカウントの取得という2つの追加APIメソッドのみが作成されました。 それ以外はすべてユニバーサルAPIによって実装されます。



プロジェクトでREST APIを使用するには、その中にポータルモジュールを作成する必要があります。 CUBA Studioでこれを行う最も簡単な方法は、[ プロジェクトプロパティ]セクションの[ ポータルモジュール作成]コマンドを使用することです。 同時に、Spring MVC上にWebアプリケーションスタブも作成されます。これは、認証の例として機能し、中間層で動作します。 この準備はアプリケーションでは使用されません。そのため、srcrootのPortalControllerクラスと構成ファイル以外はすべて削除されています。



PortalControllerクラスはSpring MVCコントローラーであり、HTTP GET経由で呼び出されるgetBalance()およびgetLastAccount()の2つのメソッドが含まれています。 これらはプラットフォームの標準REST APIを補完するメソッドであり、同様に実装されます。最初に認証、次に中間層サービスの呼び出しやJSONでの結果の生成を含むメソッドのロジックが実装されます。 したがって、メソッドのメインロジックはユーザーセッションのコンテキストで実行され、そのキーはメソッドパラメーターで渡されます。



 // PortalController.java @Inject protected Authentication authentication; @RequestMapping(value = "/api/balance", method = RequestMethod.GET) public void getBalance(@RequestParam(value = "s") String sessionId, HttpServletRequest request, HttpServletResponse response) throws IOException { if (!authentication.begin(sessionId)) { response.sendError(HttpServletResponse.SC_UNAUTHORIZED); return; } try { JSONObject result = new JSONObject(); ... response.setContentType("application/json;charset=UTF-8"); PrintWriter writer = response.getWriter(); writer.write(result.toString()); writer.flush(); } catch (Throwable e) { response.sendError(HttpServletResponse.SC_INTERNAL_SERVER_ERROR); } finally { authentication.end(); } }
      
      







プラットフォームの標準REST APIには、ログインおよびログアウトメソッド、IDおよびJPQLリクエストによるエンティティの取得、変更されたインスタンスのコミットが含まれます。一般に、すべてがCRUD操作用です。 PortalControllerクラスの2つの特定のメソッドと合わせて、クライアントJavaScriptアプリケーションが機能するにはこれで十分です。



追加のUIのクライアント側に移りましょう。



クライアントコードは、ポータルモジュールのweb / akkountディレクトリにあります。 PortalDispatcherServletサーブレットを使用してブラウザにロードされ、その構成はportal-dispatcher-spring.xmlファイルにあります。



実際、アプリケーションは1つのホストHTMLファイルと、Backbone.jsのモデルとビューのセットで構成されています。 レイアウトと設計はBootstrapで行われます。いくつかの特定のCSSはcss / akkount.cssファイルにあります。 main.jsスクリプトは、アプリケーションへのエントリポイントです。これは、router.jsファイルにあるBackboneルーターを初期化します。 さらに、main.jsには、いくつかの一般的な関数、変数、および定数が含まれています。



認証は次のように機能します。 ログイン後、サーバーから受信したセッションキーは、スクリプト変数main.jsおよびsessionStorageに保存されます。 ページをリロードすると、キーはsessionStorageから取得され、Cookieは使用されません。 操作のロードの実行中にエラー401を受信した場合(サーバーでのセッションの有効期限のためにセッションキーが無効です)、ログインは表示にリダイレクトされます。 ユーザー名を永続的に保存するには(ログインウィンドウでチェックボックスがオンになっている場合)、localStorageが使用されます。



興味深いのは、標準のCUBA REST APIを介してBackbone.jsモデルとアプリケーションの中間層との間でデータを交換する方法です。 これを行うために、Backbone.sync()関数はmain.jsスクリプトで再定義され、cuba-api.jsファイルにあるcubaAPIオブジェクトに同期を委任します。



 // main.js Backbone.sync = function(method, model, options) { options || (options = {}); switch (method) { case 'create': app.cubaAPI.create(model, options); break; case 'update': app.cubaAPI.update(model, options); break; case 'delete': app.cubaAPI.remove(model, options); break; case 'read': if (model.id) app.cubaAPI.load(model, options); else app.cubaAPI.loadList(model, options); break; } };
      
      





cubaAPIオブジェクトメソッドが機能するには、Backboneモデルにいくつかの追加フィールド(entityName、jpqlQuery、maxResults、view)が含まれている必要があります。 これらは、loadList()メソッドで対応する要求パラメーターを生成するために使用されます。 update()およびremove()メソッドでは、JSONはそれぞれcommitInstancesまたはremoveInstancesプロパティで単純に形成されます。



 // cuba-api.js (function() { app.cubaAPI = { loadList: function(collection, options) { var url = "api/query.json?s=" + app.session.id + "&e=" + collection.model.entityName + "&q=" + encodeURIComponent(collection.jpqlQuery); if (collection.maxResults) url = url + "&max=" + collection.maxResults; if (collection.view) url = url + "&view=" + collection.view; $.ajax({url: url, type: "GET", success: function(json) { options.success(json); }, error: function(xhr, status) { options.error(xhr, status); } }); }, update: function(model, options) { var json = { "commitInstances": [_.clone(model.attributes)] }; var url = "api/commit?s=" + app.session.id; $.ajax({url: url, type: "POST", contentType: "application/json", data: JSON.stringify(json), success: function(json, status, xhr) { options.success(json); }, error: function(xhr, status) { options.error(xhr, status); } }); }, remove: function(model, options) { var json = { "removeInstances": [_.clone(model.attributes)] }; var url = "api/commit?s=" + app.session.id; $.ajax({ ... }); }, ...
      
      





CUBA REST APIは、JSONで循環参照を持つオブジェクトグラフを返すことができます。 これを行うには、ルートからのパスに複数回存在するオブジェクトが、繰り返しオブジェクトの識別子に等しいref属性を持つオブジェクトに置き換えられます。 このようなrefオブジェクトを実際のオブジェクトに置き換えるために、cubaAPIはparse()メソッドを作成しました。このメソッドは、Backbone.jsのモデルとコレクションのparse()メソッドをオーバーライドします。



 // main.js Backbone.Model.prototype.parse = function(resp) { return app.cubaAPI.parse(resp); }; Backbone.Collection.prototype.parse = function(resp) { return app.cubaAPI.parse(resp); };
      
      





モデルのinitialize()関数も再定義され、CUBA REST APIへの転送の準備ができた形式でエンティティ識別子を生成します。



 // main.js Backbone.Model.prototype.initialize = function() { if (!this.get("id")) this.set("id", this.constructor.entityName + "-" + app.guid()); };
      
      





それ以外の場合、追加のUIはBackbone.jsの非常に標準的な方法で構築されます。



ご清聴ありがとうございました!

当面の計画には、GWTおよびHaulmontが開発したPostgreSQLサーバー復元力サポートツールでのCUBAのビジュアルコンポーネントの作成に関する記事の公開が含まれます。



All Articles