この記事の目的は、小さな便利なアプリケーションを作成する例を使用して、 CUBAプラットフォームの機能について説明することです。
CUBAはJavaでのビジネスアプリケーションの高速開発を目的としていますが、Habré に関するいくつかの記事についてはすでに書いています。
通常、プラットフォームは、実在するが大きすぎる閉鎖的な情報システム、または「Hello World」スタイルのアプリケーション、またはWebサイトの「Libraries」などの人工的な例を構築します。 そのため、しばらく前に、私は2羽の鳥を一度に1石で殺そうとすることにしました-私たちのプラットフォームの使用例として、自分にとって便利なアプリケーションを書いてパブリックドメインに置きます。
結果として何が起こったのか
要するに、アプリケーションは2つの主なタスクを解決します。
- どの時点でも、現金、カード、預金、債務など、あらゆる種類の現金の現在の残高が表示されます。
- 収入と支出のカテゴリごとにレポートを生成し、特定の期間にお金が何に使われたのか、どこから来たのかを調べることができます。
もう少し詳しく:
- さまざまな種類の現金が口座から提示されます 。
- 口座への到着、口座からの出費、口座間での資金移動時に操作が可能です。
- 収入または費用のトランザクションでは、 カテゴリを指定して、お金の出所または使用目的を指定できます。
- 現在の日付のすべてのアカウントの残高は常に表示され、各トランザクションの後に再計算されます。
- 収入と支出のカテゴリ別のレポートには、視覚的な比較をすばやく行うために、任意の2つの期間の概要が同時に表示されます。 どのカテゴリも比較から除外できます。 レポートの各行について、操作で「失敗」して、レポートの構成を確認できます。
- システムは、1つのTomcatにデプロイされた3つのWebアプリケーションで構成されています。
- ミドルウェア
- CUBAのフル機能UI
- Backbone.js + BootstrapのレスポンシブUIは、モバイルデバイスでの操作の入力を容易にします。
このような単純なタスクを解決するのはいくぶん冗長に見えますが、第一に、アプリケーションは実用的というよりも教育目的のために作成され、第二に、多くのリソースを必要としません-私自身のコピーはAmazon EC2マイクロインスタンスで簡単に実行できます。
いくつかのスクリーンショット
メインUI:操作のリスト
メインUI:収入/費用のカテゴリ別レポート
レスポンシブUI:操作のリスト
レスポンシブUI:現在のバランス
実行方法
プロジェクトのソースコードは次のとおりです。github.com / knstvk / akkount (KK-これらは私のイニシャルであり、私の頭に浮かんだものはありません)。
プラットフォーム自体は無料ではありませんが、無料ライセンスでの5つの同時接続は家庭での使用には十分であるため、誰かがそれを使用したい場合はお願いします。
JDK 7+とset JAVA_HOME環境変数のみが必要です。 ビルドするには、プロジェクトのルートでコマンドプロンプトを開き、実行します
gradlew setupTomcat deploy
Gradleがダウンロードされ、
その後、HSQLサーバーを起動し、プロジェクトデータサブディレクトリにデータベースを作成する必要があります。
gradlew startDb
gradlew createDb
Gradleコマンドを使用してTomcatを起動できます。
gradlew start
または
startup.*
build/tomcat/bin
サブディレクトリ内のスクリプト。
アプリケーションのメインWebインターフェースは
localhost:8080/app
localhost:8080/app
、レスポンシブUI-オン
localhost:8080/app-portal
localhost:8080/app-portal
ユーザーはadmin、パスワードはadminです。
データベースは最初は空ですが、テストデータを入力するためのジェネレーターがあります。 管理メニュー-> JMXコンソール-> app-core.akkount-> app-core.akkount:type = SampleDataGeneratorから利用できます。 以下に
generateSampleData()
メソッドを示します。このメソッドは、整数を入力として受け取ります。これは、操作を作成する必要がある現在の日付からの日数です。 たとえば、200と入力し、[実行]をクリックします。 操作が完了するのを待ってからログアウトし(右上隅のアイコン)、再度ログインします。 私のスクリーンショットとほぼ同じものが表示されます。
内部の見方
アプリケーションを調査および改良するには、CUBA Studio、IntelliJ IDEA、およびCUBAプラグインをダウンロードしてインストールすることをお勧めします 。
さらに、スタジオでどのように、何が行われているかについては触れません。 すべてが視覚的であり、コンテキストヘルプがあり、プラットフォーム上にビデオ資料とドキュメントがあります。 HSQLデータベースを使用する唯一のニュアンスについて説明します。HSQLDBを使用してプロジェクトを開くと、スタジオは独自のサーバーをポート9001で起動し、データベースを
~/.haulmont/studio/hsqldb
保存します。 つまり、Gradleコマンドを使用してStudioとは別にHSQLサーバーを起動した場合は、停止する必要があります。 必要に応じて、データベースファイルを
data/akk
から
~/.haulmont/studio/hsqldb/akk
簡単に転送できます。
一般に、アプリケーションは、より深刻なデータベース(PostgreSQL、Microsoft SQL Server、またはOracle)でも実行できます。 これを行うには、Studioでプロジェクトプロパティで必要なデータベースのタイプを選択し、[ エンティティ]-> [DBスクリプトの生成 ]を実行し、メインメニューの[ 実行 ] -> [データベースの作成 ]をクリックします 。
この記事の主な目的は、Studioのインターフェースには表示されず、何を探すべきか事前にわからない場合はドキュメントで見つけるのが難しいプラットフォームでの開発手法を示すことです。 したがって、プロジェクトの説明は断片的であり、非自明で非標準的なものに重点が置かれます。
データモデル
エンティティクラスは
global
モジュールにあり、中間層とWebクライアントの両方からアクセスできます。
これらは基本的に通常のJPAエンティティであり、適切に注釈が付けられ、
persistence.xml
登録されます。 それらのほとんどには、「インスタンス名」を設定するCUBA固有のアノテーション
@NamePattern
もあります。これは、
toString()
ようなUIで特定のエンティティインスタンスを表示する方法です。 そのような注釈が指定されていない場合、インスタンス名として
toString()
使用され、クラス名とオブジェクト識別子が返されます。 別の特定のアノテーション
@Listeners
、オブジェクトを作成/変更するためのリスナーオブジェクトのクラスを定義します。 エンティティリスナーについては、以下で詳しく説明します。
JPAエンティティに加えて、プロジェクトには非永続的な
CategoryAmount
エンティティがあります。 非永続エンティティのインスタンスはデータベースに保存されませんが、アプリケーションレイヤー間でデータを転送し、標準UIコンポーネントで表示するためにのみ使用されます。 この場合、そのようなエンティティを使用して、
CategoryAmount
ごとにレポートを生成します:中間層では、データが抽出され、
CategoryAmount
インスタンスが作成および入力され、Webクライアントでは、これらのインスタンスがデータソース(データソース)に配置され、テーブルに表示されます。 標準の
Table
コンポーネントは、エンティティの起源については何も知りません-それらにとって、これらはアプリケーションのメタデータで既知のオブジェクトにすぎません。 また、メタデータに非永続エンティティを含めるには、クラスに
@MetaClass
アノテーションを追加し、
@MetaClass
アノテーションを追加して、
metadata.xml
ファイルにクラスを登録する必要があります。 もちろん、永続エンティティもメタデータで説明されています。このため、アプリケーションの開始時にメタデータローダーが
persistence.xml
ファイルも解析します。
Enumは、
OperationType
などのエンティティの隣にもあります。 エンティティ属性のデータモデルで使用される列挙体はあまり一般的ではありません。それらは
EnumClass
インターフェイスを実装し、
id
フィールドを持っています。 したがって、データベースに格納されている値はJava値から分離されています。 これにより、アプリケーションコードの任意のリファクタリング中に、運用データベースのデータとの互換性を確保できます。
エンティティパッケージの
messages.properties
および
messages_ru.properties
ファイルには、エンティティのローカライズされた名前とその属性が含まれています。 これらの名前は、ビジュアルコンポーネントがそれらのレベルで再定義しない場合にUIで使用されます。 メッセージファイルは、一般的なUTF-8エンコードのキーと値のセットです。 特定のロケールのメッセージの検索は、
PropertyResourceBundle
ルールに似ています-最初に、ロケールに対応するサフィックスを持つファイルで、見つからない場合、サフィックスのないファイルでキーが検索されます。
モデルの本質を考慮してください。
-
Currency
-通貨。 一意のコードと任意の名前があります。 通貨コードの一意性は、@Column
アノテーションにunique = true
プロパティが含まれる場合、Studioがデータベース作成スクリプトに含める一意のインデックスによってサポートされます。 プラットフォームには、データベースで一意性が侵害されたときにスローされる例外ハンドラが含まれています。 このハンドラーは、ユーザーに標準メッセージを提供します。 ハンドラーはプロジェクトで置き換えることができます。 -
Account
-アカウント。 一意の名前と任意の説明があります。 また、通貨へのリンクと個別の通貨コードフィールドも含まれています。 このフィールドは、パフォーマンスを改善するための非正規化の例です。 通常、アカウントのリストは通貨コードと一緒に表示されるため、通貨コードをアカウント自体に追加することで、データベースへのクエリの結合を取り除くのが理にかなっています。 アカウントの通貨を変更するときに、エンティティリスナにアカウントの通貨コードを更新するよう強制します(実際にはこれは非常にまれですが)。これについては後で詳しく説明します。 アカウントには、active
属性(新しい操作で使用できるという兆候)とincludeInTotal
属性(この口座の残高を合計残高に含める必要があるという兆候)も含まれています。 -
Category
-収入または支出のカテゴリ。 一意の名前と任意の説明があります。catType
属性は、CategoryType
列挙によって定義されるカテゴリタイプです。 既に上記で説明したように、列挙識別子によって定義された値(この場合、文字列「E」または「I」)はクラスフィールドとデータベースに格納され、ゲッターとセッター、したがってアプリケーションコード全体がCategoryType.INCOME
値で動作しますおよびCategoryType.EXPENSE
。 -
Operation
-操作。 操作属性:タイプ(OperationType
列挙)、日付、費用およびacc1
勘定(acc1
、acc2
)および対応する金額(amount1
、amount2
)、カテゴリおよびコメント。 -
Balance
-特定の日付の口座残高。 一般に、ホームアカウンティングの場合、この本質がなくても、「時間の初めから」常に動的に残高を計算することが完全に可能です。 しかし、楽しみのために、私は多数の操作の場合に実装を複雑にすることにしました-各月の初めの口座残高は、各操作を記録するときにBalance
コピーに保存され、翌月の初め(および、もしあれば)の残高が再計算されます。 ただし、現在の日付の残高を計算するには、月の初めに残高を取得し、現在の月の操作の売上高を計算するだけです。 このアプローチは、長期にわたってパフォーマンスの問題を引き起こすことはありません。 -
UserData
は、一部のユーザー関連データのキーと値のストアです。 たとえば、最後に使用したアカウント、カテゴリ別のレポートパラメーター。 つまり、繰り返し行われるユーザーアクション中に「記憶」する必要があるものを保存します。 可能なキーは、UserDataKeys
クラスの定数によって定義されます。
エンティティリスナー
JPAを使用している場合は、おそらくエンティティリスナーも使用したでしょう。 これは、データベースのエンティティへの変更を保存するときにアクションを実行するための便利なメカニズムです。 最も重要なことは、リスナーによって行われたすべての変更は、データベーストリガーと同様に同じトランザクションで行われます。 したがって、リスナーのデータモデルの一貫性を維持するためのロジックを編成すると便利です。
CUBAのエンティティリスナーは、実装がJPAとわずかに異なります。 リスナークラスは、1つ以上の特別なインターフェイス(
BeforeInsertEntityListener
、
BeforeUpdateEntityListener
など)を実装する必要があります。 リスナーは、文字列の配列にクラス名をリストすること
@Listeners
、
@Listeners
アノテーションのエンティティクラスに登録されます。 エンティティは中間層とクライアントの両方にアクセス可能なグローバルオブジェクトであり、リスナーはクライアントにアクセスできない中間層のみのオブジェクトであるため、リスナークラスのリテラルをエンティティクラスで直接使用することはできません。 リスナーは、
EntityManager
およびデータベースを操作する他の手段にアクセスする必要があるため、中間層にのみ存在します。
このアプリケーションでは、エンティティリスナーは2つの機能を実行します。1つ目は非正規化フィールドを更新し、2つ目は月の初めに口座残高を再計算することです。
最初のタスクは簡単です
onBeforeInsert()
メソッドのAccountEntityListener
AccountEntityListener
、
onBeforeInsert()
は通貨コードの値を更新します。 これを行うには、
Currency
関連インスタンスを参照するだけで十分です。
2番目のタスクは、基本的にアプリケーションのビジネスロジックの主要なタスクの1つです。
OperationEntityListener
は、この
onBeforeInsert()
、
onBeforeUpdate()
、
onBeforeDelete()
ます。 バランスの再計算に加えて、このリスナーは
UserData
オブジェクトで最後に使用されたアカウントも記憶します。
Before-listenerでは、
EntityManager
の使用に制限がなく、エンティティのインスタンスをロードおよび変更することに注意してください。 たとえば、
addOperation()
では、
Query
を使用して
Query
Balance
インスタンスがロードおよび変更されます。 これらは、1つのトランザクションの操作と同時にデータベースに保存されます。
リスナーでは、永続コンテキストにあるオブジェクトの「前の」状態、つまりデータベースにある状態を取得する必要がある場合があります。 たとえば、この場合、
onBeforeUpdate()
場合、最初に残高から取引金額の前の値を差し引いてから、新しい値を追加する必要があります。 これを行うには、
getOldOperation()
を使用して
getOldOperation()
メソッドで新しいトランザクションが開始され、そのコンテキストで
EntityManager
別のインスタンス
EntityManager
取得され、同じ識別子を持つ以前の操作状態がデータベースからロードされます。 その後、リスナーが動作している現在のトランザクションに影響を与えることなく、新しいトランザクションが完了します。
中間層コンポーネント
クライアントレベルへのデータのロードとデータベースへのユーザーによる変更の保存に関する主な作業は、プラットフォームに実装されている標準のDataServiceによって実行されます。 これにより、視覚コンポーネントのデータソースが機能します。 これはアプリケーションでは十分ではないため、いくつかの特定のサービスが作成されています。
まず、
UserDataService
であり、これにより
UserDataService
のキーと値のストレージを操作し、エンティティ識別子の読み取りと書き込みのための型付きインターフェイスを提供できます。 サービスインターフェイスは、クライアントレベルからアクセスできる必要があるため、
global
モジュールにあります。 サービスの実装は、
UserDataServiceBean
クラスのコアモジュールにあります。
UserDataWorker
への呼び出しを委任します。この
UserDataWorker
には、有用な作業を行うコードが集中しています。 これが行われるのは、
OperationEntityListener
もこの機能が必要なためです。つまり、中間層の「内側から」です。 このサービスは「ミドルウェア境界」を形成し、クライアントブロックからの呼び出しのみを目的としています。 中間層コンポーネント内から呼び出すことはできません。これにより、インターセプターの操作が繰り返され、認証を確認して特別な方法で例外を処理します。 また、順序を復元するために、ミドルウェアの外部から呼び出されるサービスを、内部から呼び出される残りのBeanから分離する価値があります。 少なくとも、トランザクションの外部から呼び出す場合は常に存在せず、ミドルウェアコードから呼び出す場合は、トランザクションを既に開くことができるためです。
次のサービスは
BalanceService
です。 任意の日に口座残高の値を取得できます。 この機能はUIの顧客と中間層(テストデータジェネレーター)の両方に必要なので、別の
BalanceWorker
も配置され
BalanceWorker
。
最後のサービスは
ReportService
。 カテゴリごとにレポートのデータを取得し、非永続的な
CategoryAmount
エンティティのインスタンスのリストとして返します。
中間層には、テストデータを生成するために設計された
SampleDataGenerator
ビンも実装されています。 この種の機能では、通常、複雑なUIは必要ありません-単純なパラメーターの転送を呼び出しに提供するだけで十分な場合があり、属性のセットの形式で状態を表示する必要がある場合があります。 さらに、システムのユーザーではなく、管理者のみがこれを操作します。 この場合、BeanにJMXインターフェースを与え、Webクライアントに組み込まれたJMXコンソールから、または任意の外部JMXツールに接続して、そのメソッドを呼び出すと便利です。 この場合、Beanには
SampleDataGeneratorMBean
インターフェースがあり、コアモジュールの
spring.xml
登録されています。
Beanの
generateSampleData()
メソッドには
@Authenticated
として注釈が付けられていることに注意してください。 つまり、このメソッドが呼び出されると、特別なシステムログインが実行され、実行スレッドにユーザーセッションが存在します。 この場合、メソッドは
EntityManager
を介してエンティティを作成および変更するため、このメソッドが必要です。これらのエンティティは、保存時に属性
createdBy
、
updatedBy
createdBy
必要とします。 一方、
removeAllData()
メソッドもJMXインターフェース経由で呼び出されますが、
QueryRunner
を介したSQLクエリを使用してデータを削除し、ユーザーセッションにどこからもアクセスしないため、認証は必要ありません。
一般に、ユーザーセッションの存在の必須チェックは、クライアントレベルから中間層への入り口(サービスインターセプター)でのみ実行されます。 ミドルウェアレベルでセッションの存在とユーザー権限を確認するかどうか-アプリケーション開発者が決定しますが、エンティティ監査の属性にユーザー名を入れる必要があるため、セッションの存在が必要な場合があります。 さらに、
DataService
がエンティティを使用したCRUD操作の実行を委任する
DataWorker
である
DataWorker
でユーザー権限が常にチェックされます。
メインアプリケーションウィンドウ
CUBA Webクライアントの標準機能は、アプリケーションウィンドウの左側にある非表示のパネルで、通常はいわゆる「アプリケーションフォルダー」と「検索フォルダー」が表示されます。これらのフォルダーは、情報にすばやくアクセスするために使用されます。フォルダーをクリックすると、エンティティのリストとフィルターが適用された特定の画面が開きます。
メインウィンドウの左側に現在の残高に関する情報を表示するのは論理的に思えました。そこで、フォルダパネルの上部にバランスパネルを埋め込みました。
これは次のように行われます。
- プラットフォームから
FoldersPane
クラスを派生しLeftPanel
、メソッドをオーバーライドinit()
し、refreshFolders()
どの方法が呼び出されますcreateBalancePanel()
。新しいコンテナがその中に作成され、から取得したデータで満たされBalanceService
、親コンテナの最上部に配置されます。 -
LeftPanel
FoldersPane
,AppWindow
AkkAppWindow
createFoldersPane()
. -
AkkAppWindow
AppWindow
,createAppWindow()
App
. ,getLeftPanel()
— .
画面記述子はファイルにあります
operation-browse.xml
。ここではすべてが標準です。ただし、操作テーブルで日付と金額を表すためのフォーマッタクラスの使用は除きます。
日付を表示するには
DateFormatter
、ローカライズされたメッセージのパッケージからキーによってフォーマットが送信されるプラットフォームが使用されます。したがって、フォーマット文字列は言語によって異なります。ロシア語の場合、日付はドットで区切られ、英語の場合は/で区切られます。
金額が小数部なしで表示され、0がまったく表示されないようにするために、プロジェクトでクラスが作成されました
DecimalFormatter
-金額の列で使用されます。
操作エディター
ここでより興味深いのは、操作が3つのタイプ(収入、費用、振替)のいずれかであり、編集画面が異なるように見えることです。
一見すると、最初の2つの画面は同じように見えますが、実際はそうではありません。ビジュアルコンポーネントは、エンティティのさまざまな属性
Operation
-との費用、
acc1
-
amount1
との収入、
acc2
およびとの収入
amount2
です。この可変性は、コントローラーコードで完全に実装できますが、画面のさまざまな部分を別々のフレームに分割することで、より宣言的に行うことにしました。
3つのフレーム-操作の種類の数。それらはすべて、操作編集画面自体と同じパッケージにあります。ほとんどの場合、フレームは静的に接続されます-コンポーネントを使用して
iframe
XML画面記述子内。操作のタイプに応じて目的のフレームを選択する必要があるため、これは私たちには適していません。したがって、画面のXML記述子では
operation-edit.xml
、フレームのコンテナのみが定義されます
groupBox
。識別子を持つコンポーネント
frameContainer
、および画面へのフレームの実際の作成と挿入は、コントローラーで実行されます
OperationEdit
。
@Inject private GroupBoxLayout frameContainer; private OperationFrame operationFrame; @Override public void init(Map<String, Object> params) { ... String frameId = operation.getOpType().name().toLowerCase() + "-frame"; operationFrame = openFrame(frameContainer, frameId, params);
ここで
OperationFrame
-フレームコントローラ操作の種類によって実装されるインタフェース。それを介して、3つのフレームすべてを均一に管理すること、つまりそれらを初期化および検証することが便利です。コントローラー
メソッドには別の興味深いポイントがあります。操作がコミットされた後に起動するリスナーが登録されます。
init()
OperationEdit
@Override public void init(Map<String, Object> params) { ... getDsContext().addListener(new DsContext.CommitListenerAdapter() { @Override public void afterCommit(CommitContext context, Set<Entity> result) { LeftPanel leftPanel = App.getLeftPanel(); if (leftPanel != null) leftPanel.refreshBalance(); } }); }
このリスナーは、現在のバランスを表示する左パネルのコンテンツを更新します。
操作タイプフレームには、次の共通機能があります。合計で機能するテキストフィールドは、データソースに添付されません。これは、フィールドに算術式を入力できるように行われ、システムが金額を計算します。
検討してください
expense-frame.xml
。
textField
識別子を使用してコンポーネントを宣言します
amountField
。コントローラー
ExpenseFrame
は
AmountCalculator
、合計計算ロジックがカプセル化されたビンを使用します。
@Inject private TextField amountField; @Inject private AmountCalculator amountCalculator; @Override public void postInit(Operation item) { amountCalculator.initAmount(amountField, item.getAmount1()); … } @Override public void postValidate(ValidationErrors errors) { BigDecimal value = amountCalculator.calculateAmount(amountField, errors); … }
Webクライアント層で定義された同じBeanは、他の2つのフレームコントローラーでも使用されます。
initAmount()
Bean メソッドは、テキストボックスのデータ型でフォーマットされた現在の量を設定します
BigDecimal
。
datatype = decimal
コンポーネントに指定することは単純に不可能です。この場合、数字のみを入力することが可能であり、算術式を入力できる必要があるためです。メソッド
calculateAmount()
は、正規表現を使用して式の正確性をチェックし、インターフェイスを介してGroovyの式として実行します
Scripting
。結果は数値になり、操作を設定するためにスクリーンコントローラーに返されます。
カテゴリーレポート
この対話型レポートは画面によって実装されます
categories-report.xml
。このタイプの2つのカスタムデータソースが含まれているため、主に興味深いもの
CategoryAmountDatasource
です。データソースクラスは
datasourceClass
element 属性で指定されます
collectionDatasource
。これらのデータソースにはJPQL演算子も指定されますが、使用されず、指定されていない場合はStudioがクエリテキストを自動的に生成するためにのみ存在します。実際、データソース
CategoryAmountDatasource
はメソッド
loadData()
をオーバーライドし
DataService
、JPQLクエリを介してデータをロードする代わりに、serviceを呼び出し
ReportService
、必要なパラメーターを渡します。
public class CategoryAmountDatasource extends CollectionDatasourceImpl<CategoryAmount, UUID> { private ReportService service = AppBeans.get(ReportService.NAME); @Override protected void loadData(Map<String, Object> params) { ... Date fromDate = (Date) params.get("from"); Date toDate = (Date) params.get("to"); ... List<CategoryAmount> list = service.getTurnoverByCategories(fromDate, toDate, categoryType, currency.getCode(), ids); for (CategoryAmount categoryAmount : list) { data.put(categoryAmount.getId(), categoryAmount); } ... }
パラメータは、
refresh()
データソースメソッドのスクリーンコントローラーによって設定されます。メソッド
refreshDs1()
、
refreshDs2()
クラスを参照してください
CategoriesReport
。サービスは非永続エンティティインスタンスのリストを返し
CategoryAmount
、データソースはそれらをデータコレクションに保存します。したがって、これらのデータソースに関連付けられたテーブル
CategoryAmount
は、通常の方法でデータベースからロードされた他のエンティティとしてインスタンスを表示します。[除外
]ボタンの機能は興味深いように設計されており、選択したカテゴリを検討対象から除外できます。このような2つのボタンは
、記述子で
categories-report.xml
宣言されています-左右のテーブル用です。各ボタンはアクションに関連付けられています。
excludeCategory
あなたのテーブル。ただし、XML記述子のテーブルに対してアクションは宣言されていません。どのように機能しますか?実際、この場合のテーブルのアクションは
init()
スクリーンコントローラーメソッドに追加されます
initExcludedCategories()
。メソッドを参照してください。このメソッドは、サービスを使用して記憶されていた以前に除外されたカテゴリのリストも「リコール」します
UserDataService
。
型アクションは
ExcludeCategoryAction
、トリガーされると、除外されたカテゴリに対応するリンクボタンを持つコンテナと碑文を作成し、ハンドルで事前に宣言されたコンテナ内に新しいコンテナを配置するメソッド
excludeCategory()
を呼び出し
ComponentsFactory
ます
excludedBox
。ボタンごとにリスナーが作成され、トリガーされると、ボタンがラベルとともに配置されているコンテナ全体が親コンテナから削除されます。さらに、データソースはカテゴリリストを再編成することにより更新されます。
一般に、カテゴリレポート画面はプラットフォームを使用するためのかなり非標準のオプションであるため、多くの手動で記述されたロジックがあり、通常はコンポーネントの相互作用の標準オプション内に隠されています。
謝辞
しばらく使っていた素晴らしいzenmoney.ruサービスからアイデアを得ました。プラットフォームに含まれるすべてのオープンソースライブラリとフレームワークは、[ヘルプ]-> [バージョン情報]-> [クレジット]ウィンドウにリストされています。
続く
同じアプリケーションに関する次の記事では、Backbone.js + Bootstrapで記述され、REST APIを介して中間層と対話するデバイスブロックレスポンシブUIについて説明する予定です。さらに、メインUIのテーマをわずかに変更し、新しいUIコンポーネントを追加して、プロジェクトでインターフェイスをカスタマイズする可能性を示します。