GWTおよびVaadinのコンポーネントの作成とデバッグ

画像








Google Web ToolkitとVaadinフレームワークは、使用するだけで非常にうまく機能します。 突然その機能を拡張する必要がある場合は、真剣に汗をかかなければなりません。 この記事では、GWTで単純なコンポーネントを作成し、Vaadinサーバーパーツを追加して、アプリケーションで使用する方法を説明します。 完全に空のGWT / Vaadinプロジェクトを作成して最初からGWTコンパイルを構成するのではなく、代わりにCUBA.Platformに基づいてアプリケーションを実行し、コンポーネント自体を実装してから試してみます。 これは、すべてがこのアプリケーションにどれだけうまく適合するかを示します。 コンポーネントのデバッグには特別な注意を払いたいと思います。コンポーネントは簡単ではなく、開発者にとって常に困難を引き起こすからです。



この記事で説明されているすべてがGWTとVaadinに適用されるわけではなく、一部の手順と手法はCUBA.Platformを使用する場合にのみ適用できることを警告したいと思います。



環境の準備



実験には、 CUBA Studioで作成された空のプロジェクトを使用します。 CUBA-Javaでビジネスアプリケーションを開発するためのプラットフォーム。データモデルとアプリケーションインターフェイスをすばやく作成し、データを操作するロジックを決定し、ユーザー権限を管理できます。 UIプラットフォームの中心で、Vaadin Webフレームワークが積極的に使用されているため、多くの興味深いアイデアを実装できます。



最初に、JavaScriptでコンパイルするGWTモジュールを作成します。 Studioで「Webツールキットモジュールの作成」アクションを実行します。 これは単純な補助操作であり、手動で実行しても意味がありません。 Studioは、AppWidgetSet.gwt.xmlモジュールのGWT記述子、モジュールのディレクトリ、空のパッケージを生成し、必要なタスクをbuild.gradleアセンブリの説明に追加します。



次のステップでは、アクション「IDEプロジェクトファイルの作成または更新」を実行してIntelliJ IDEAプロジェクトファイルを生成し、IDEにコンポーネントコードを記述します。



コンポーネント自体をプログラムするには、Javaコードを強調表示する以外に特別なIDE機能は必要ありません。したがって、IntelliJ Ideaを使用する必要はありません。EclipseまたはNetbeansも同様に使用できます。 Google Web Toolkitのおかげで、使い慣れたJavaツールを使用できます。これは、大規模プロジェクトを開発する際の大きな利点です。



コンポーネントを書く



画像






コンポーネント自体は非常に単純です-5つ星の形の評価フィールド。 これは、ユーザーがマウスで評価を選択する入力フィールドです。 サーバー上に状態があり、変更されると表示が変わるはずです。



画像






これが、Ideaプロジェクトウィンドウでの新しいWebツールキットモジュールの外観です。 ルートパッケージにはGWTモジュール記述子が含まれています。

AppWidgetSet.gwt.xml
<?xml version="1.0" encoding="UTF-8"?> <!DOCTYPE module PUBLIC "-//Google Inc.//DTD Google Web Toolkit 1.7.0//EN" "http://google-web-toolkit.googlecode.com/svn/tags/1.7.0/distro-source/core/src/gwt-module.dtd"> <module> <inherits name="com.haulmont.cuba.web.toolkit.ui.WidgetSet" /> </module>
      
      





ベースモジュールCUBA.Platformを継承し、アプリケーション(ブラウザで実行される)のクライアント部分全体の開始点です。 デフォルトでは、GWTコンポーネントの実装は「クライアント」サブパッケージに配置する必要があります。 クライアントパッケージとその中にratingfieldサブパッケージを作成しましょう。



コンポーネントの最初の部分はGWTウィジェットです。 RatingFieldWidgetクラス。web-toolkitモジュールに配置します。

RatingFieldWidget.java
 package com.haulmont.ratingsample.web.toolkit.ui.client.ratingfield; import com.google.gwt.dom.client.DivElement; import com.google.gwt.dom.client.SpanElement; import com.google.gwt.dom.client.Style.Display; import com.google.gwt.user.client.DOM; import com.google.gwt.user.client.Event; import com.google.gwt.user.client.ui.FocusWidget; import java.util.ArrayList; import java.util.List; //  GWT  public class RatingFieldWidget extends FocusWidget { private static final String CLASSNAME = "ratingfield"; // API     public interface StarClickListener { void starClicked(int value); } protected List<SpanElement> stars = new ArrayList<SpanElement>(5); protected StarClickListener listener; protected int value = 0; public RatingFieldWidget() { DivElement container = DOM.createDiv().cast(); container.getStyle().setDisplay(Display.INLINE_BLOCK); for (int i = 0; i < 5; i++) { SpanElement star = DOM.createSpan().cast(); //      DOM.insertChild(container, star, i); //    ONCLICK DOM.sinkEvents(star, Event.ONCLICK); stars.add(star); } setElement(container); setStylePrimaryName(CLASSNAME); } //       GWT @Override public void onBrowserEvent(Event event) { super.onBrowserEvent(event); switch (event.getTypeInt()) { //    ONCLICK case Event.ONCLICK: SpanElement element = event.getEventTarget().cast(); //      int index = stars.indexOf(element); if (index >= 0) { int value = index + 1; //    setValue(value); //   if (listener != null) { listener.starClicked(value); } } break; } } //       primaryStyleName //        @Override public void setStylePrimaryName(String style) { super.setStylePrimaryName(style); for (SpanElement star : stars) { star.setClassName(style + "-star"); } updateStarsStyle(this.value); } //      public void setValue(int value) { this.value = value; updateStarsStyle(value); } //    private void updateStarsStyle(int value) { for (SpanElement star : stars) { star.removeClassName(getStylePrimaryName() + "-star-selected"); } for (int i = 0; i < value; i++) { stars.get(i).addClassName(getStylePrimaryName() + "-star-selected"); } } }
      
      





ウィジェットは、イベントの表示と応答を担当する独立したクラスです。 彼はサーバー側について知る必要はなく、それを操作するためのインターフェースを定義するだけです。 私たちの場合、これはsetValueメソッドとStarClickListenerインターフェイスです。



ウィジェットのコード全体に単一のJavaScript行がないことは注目に値します。これは、大きく複雑なコンポーネントに適しています。 ただし、このJavaコードはJavaScriptでコンパイルされ、リフレクションやI / Oなどの標準Javaライブラリの多くの部分が利用できない場合があることを忘れないでください(完全な互換性情報はこちらをご覧ください: www.gwtproject.org/doc/latest/RefJreEmulation.html



外観を決定します



お気づきかもしれませんが、キー要素にスタイル名を割り当てることを除いて、ウィジェットコードには外観への参照はありません。 この手法は、コンポーネントの開発で常に使用されており、スタイルの組み合わせに基づいてCSSの外観を決定できます。



コンポーネントの外観を決定するには、最初にスタイルファイルを作成します。 これを行うには、テーマ「halo」に対してアクション「Create theme extension」を使用できます。 このテーマでは、使用するアイコンの代わりにFontAwesomeフォントグリフを使用します。 Studioは、Webモジュールのテーマディレクトリに、実験用の空のSCSSファイルを作成します。



各コンポーネントのスタイルを、SCSS混合形式のcomponents / componentnameディレクトリ内の個別のファイルcomponentname.scssに分けるのが慣例です。

ratingfield.scss
 @mixin ratingfield($primary-stylename: ratingfield) { .#{$primary-stylename}-star { font-family: FontAwesome; font-size: $v-font-size--h2; padding-right: round($v-unit-size/4); cursor: pointer; &:after { content: '\f006'; // 'fa-star-o' } } .#{$primary-stylename}-star-selected { &:after { content: '\f005'; // 'fa-star' } } .#{$primary-stylename} .#{$primary-stylename}-star:last-child { padding-right: 0; } .#{$primary-stylename}.v-disabled .#{$primary-stylename}-star { cursor: default; } }
      
      





次に、そのようなファイルはメインテーマファイルに含まれます

halo-ext.scss
 @import "../halo/halo"; @import "components/ratingfield/ratingfield"; /* Define your theme modifications inside next mixin */ @mixin halo-ext { @include halo; @include ratingfield; }
      
      





SCSSについて一言。 これは、変数、不純物、および計算値を使用するためのCSSスタイルの記述形式です。 多くのWebフレームワークで積極的に使用されています。Vaadin7では、アプリケーションテーマの基本形式です。 CUBA.Platformアプリケーションでは、この形式を単純に使用できます。これは、StudioがSCSSテーマのアセンブリを整理するという汚い仕事を引き受けるからです。



不純物の形式の説明の形式は、コンポーネントに異なるプライマリスタイル名を持つ相続人がいる場合に役立ちます。 SCSS includeを使用して先祖スタイルを有効にします



星には、2つのFontAwesomeグリフ-「fa-star」と「fa-star-o」を使用します。 CSS自体は非常にシンプルで、2つの状態の星印とそれらのマウスカーソルのみが含まれています。



アプリケーションのテーマを切り替えるには、CUBA.Studioの[プロジェクトプロパティ]ページで[ハロ]を選択します。



サーバー側を追加する



ここまでは、GWTアプリケーションで作成したウィジェットを使用できました。これは、サーバーにまったく依存していないためです。 次に、Vaadinフレームワークとそのサーバー指向モデルについて説明します。 いくつかの機能があります。



-コンポーネントと入力フィールドの状態全体がサーバーに保存され、ページ全体を更新したり、サーバーとの接続が失われた後でも復元できます。

-クライアント部分を除くすべての有用なアプリケーションコードは、サーバーで実行されます



つまり、Vaadinコンポーネントは開発者からブラウザでの動作を隠し、不注意なJava開発者のHTML / CSSはそれを認識しません(まあ、またはほとんど認識しません。そうしないと、コンポーネントを記述するのにも役立ちます)。



Webモジュールにcom.haulmont.ratingsample.web.toolkit.uiパッケージを作成します。 その中に、コンポーネントRatingFieldのコードを配置します。 入力フィールドの基本ロジックを定義するVaadin AbstractFieldクラスからクラスを継承します。



Vaadinコンポーネントの主要なサーバーコンポーネント:



1)RatingFieldコンポーネントクラスは、サーバーコードのAPI、作業用のさまざまなget / setメソッド、イベントリスナー、およびデータソースの接続を定義します。 アプリケーション開発者は、常にこのクラスのメソッドをコードで使用します。

RatingField.java
 package com.haulmont.ratingsample.web.toolkit.ui; import com.haulmont.ratingsample.web.toolkit.ui.client.RatingFieldServerRpc; import com.haulmont.ratingsample.web.toolkit.ui.client.RatingFieldState; import com.vaadin.ui.AbstractField; //       Integer public class RatingField extends AbstractField<Integer> { public RatingField() { //    ,        registerRpc(new RatingFieldServerRpc() { @Override public void starClicked(int value) { setValue(value, true); } }); } //    @Override public Class<? extends Integer> getType() { return Integer.class; } //      @Override protected RatingFieldState getState() { return (RatingFieldState) super.getState(); } @Override protected RatingFieldState getState(boolean markAsDirty) { return (RatingFieldState) super.getState(markAsDirty); } //   setValue       @Override protected void setInternalValue(Integer newValue) { super.setInternalValue(newValue); if (newValue == null) { newValue = 0; } getState().value = newValue; } }
      
      





2)RatingFieldStateステータスクラスは、クライアントとサーバー間で送信されるデータを管理します。 サーバーで自動的にシリアル化され、クライアントで逆シリアル化されるパブリックフィールドを定義します。

RatingFieldState.java
 package com.haulmont.ratingsample.web.toolkit.ui.client; import com.vaadin.shared.AbstractFieldState; public class RatingFieldState extends AbstractFieldState { { //      primaryStyleName = "ratingfield"; } //      public int value = 0; }
      
      





3)RatingFieldServerRpcインターフェース-クライアント部分のサーバーAPIを定義し、そのメソッドはVaadinに組み込まれたリモートプロシージャコールメカニズムを使用してクライアントから呼び出すことができます。 コンポーネント自体にこのインターフェイスを実装します。この場合、フィールドのsetValueメソッドを呼び出します。

RatingFieldServerRpc.java
 package com.haulmont.ratingsample.web.toolkit.ui.client; import com.vaadin.shared.communication.ServerRpc; public interface RatingFieldServerRpc extends ServerRpc { //       void starClicked(int value); }
      
      





重要な点は、状態クラスとrpcを「クライアント」サブパッケージに配置する必要があるため、GWTコンパイラの粘り強いレッグがクライアントコードのJavaScript表現を作成するために問題なくそれらに到達することです。 さらに、クラスはGWTでコンパイルできないコードを使用しないでください。



クライアントコードをサーバー側に関連付けるときが来ました。 コネクタクラスはVaadinでこの役割を果たします。 これらはウィジェットクラスの隣に配置されます。 コネクタクラスにはConnectによって注釈が付けられ(ComponentName.class)、サーバーのクライアント部分の対応が設定されます。

RatingFieldConnector.java
 package com.haulmont.ratingsample.web.toolkit.ui.client.ratingfield; import com.haulmont.ratingsample.web.toolkit.ui.RatingField; import com.haulmont.ratingsample.web.toolkit.ui.client.RatingFieldServerRpc; import com.haulmont.ratingsample.web.toolkit.ui.client.RatingFieldState; import com.vaadin.client.communication.StateChangeEvent; import com.vaadin.client.ui.AbstractFieldConnector; import com.vaadin.shared.ui.Connect; //       RatingField //    AbstractField @Connect(RatingField.class) public class RatingFieldConnector extends AbstractFieldConnector { //     RatingFieldWidget @Override public RatingFieldWidget getWidget() { RatingFieldWidget widget = (RatingFieldWidget) super.getWidget(); if (widget.listener == null) { widget.listener = new RatingFieldWidget.StarClickListener() { @Override public void starClicked(int value) { getRpcProxy(RatingFieldServerRpc.class).starClicked(value); } }; } return widget; } //    - RatingFieldState @Override public RatingFieldState getState() { return (RatingFieldState) super.getState(); } //       @Override public void onStateChanged(StateChangeEvent stateChangeEvent) { super.onStateChanged(stateChangeEvent); //     ,   if (stateChangeEvent.hasPropertyChanged("value")) { getWidget().setValue(getState().value); } } }
      
      





試運転



これらすべてを実際に試すために、いくつかの準備手順を実行します。



1)Studioメニューからアプリケーションのデータベースを作成します。実行-データベースの作成

2)Webモジュールにコンポーネントを配置するための画面を作成します。



画像






3)アプリケーションメニューに画面を追加します。メインメニュー-編集



画像






4)次に、IDEでの画面の編集に移りましょう。

コンポーネントのコンテナが必要です。画面のXMLで宣言しましょう。

rating-screen.xml
 <?xml version="1.0" encoding="UTF-8" standalone="no"?> <window xmlns="http://schemas.haulmont.com/cuba/5.3/window.xsd" caption="msg://caption" class="com.haulmont.ratingsample.web.RatingScreen" messagesPack="com.haulmont.ratingsample.web"> <layout expand="container"> <vbox id="container"> <!--       Vaadin --> </vbox> </layout> </window>
      
      





評価コントローラークラスRatingScreen.java画面クラスを開き、画面にコンポーネントを配置するためのコードを追加しましょう。

RatingScreen.java
 package com.haulmont.ratingsample.web; import com.haulmont.ratingsample.web.toolkit.ui.RatingField; import com.haulmont.cuba.gui.components.AbstractWindow; import com.haulmont.cuba.gui.components.BoxLayout; import com.haulmont.cuba.web.gui.components.WebComponentsHelper; import javax.inject.Inject; import java.util.Map; public class RatingScreen extends AbstractWindow { @Inject private BoxLayout container; @Override public void init(Map<String, Object> params) { super.init(params); //  API CUBA    Vaadin  : com.vaadin.ui.Layout containerLayout = WebComponentsHelper.unwrap(container); //       Vaadin : RatingField field = new RatingField(); field.setCaption("Rate this!"); containerLayout.addComponent(field); } }
      
      





Webモジュールは、サードパーティ製と自己記述型の両方のVaadinコンポーネントの統合を完全にサポートしています。 純粋なVaadinでアプリケーションを作成しているかのように、それらを直接使用できます。



Studioからアプリケーションを起動します。アプリケーションサーバーを起動し、 http:// localhost:8080 / appにアクセスして結果を確認します。



画像






サーバー上のJavaコードから使用できる完全に機能するコンポーネントに満足しています。 すべてのコードは、Vaadinアプリケーションでの使用に適しています。



完全なアプリケーションコードは、 github.com / Haulmont / ratingsample.gitにあります。



ブラウザーでのデバッグ



サーバーでのJavaコンポーネントコードのデバッグは非常に簡単なので、ウィジェットコードのデバッグのみを検討します。



GWTコードのデバッグは完全に非自明であり、精度が必要です。 デバッグには、SuperDevModeモードを使用します。 プロジェクトはGWT 2.5.1以降でビルドする必要があります。 このモードでは、ブラウザーでJavaコードをJavaScriptコードにマッピングします(ソースマップ、 developer.chrome.com /devtools/docs/javascript-debugging#source-mapsを参照)。 つまり、ブラウザでJavaコードを表示してデバッグしますが、いくつかの制限があります。



作業のスキームは次のとおりです。

  1. com.google.gwt.dev.codeserver.CodeServerサーバーを起動して、ブラウザー側にJSコードとJavaコードの対応を提供し、ページを更新するときにウィジェットを収集します。
  2. オプションを使用してアプリケーションを開きますか?デバッグとsuperdevmode
  3. 開発者ツールのF12を構成します。右下隅には、設定を開くためのボタンがあります。 [ソースマップを有効にする]オプションをオンにします
  4. ページを更新し、開発者ツールの[ソース]タブを開きます。 GWTウィジェットのすべてのJavaクラスがそこに表示されるはずです。 Chromeデバッガーでは、ブレークポイントを設定し、変数を監視し、式を実行できます。
  5. プロジェクトのウィジェットコードを変更するときは、ページを更新するだけで十分です。ウィジェットセットが再構築され、ブラウザによって選択されます。 これにより、ウィジェットコードの変更をその場で確認できるため、開発が大幅にスピードアップします。


私たちはプロジェクトのすべてを実行しようとします:

1)このモードを実行するには、build.gradleファイルにweb-toolkitモジュールのランタイム依存関係servletApiを追加する必要があります。

build.gradle
 ... configure(webToolkitModule) { dependencies { ... runtime(servletApi) } ...
      
      





2)IDEで「IDEプロジェクトファイルの作成または更新」アクションを実行して、Ideaに新しい依存関係が表示されるようにする

3)アプリケーションの種類と次のパラメーターを使用して、Ideaで新しい起動構成を作成します。



メインクラス: com.google.gwt.dev.codeserver.CodeServer

VMオプション: -Xmx512M

モジュールのクラスパスを使用: app-web-toolkit

プログラム引数: -workDir C:\ Users \ yuriy \ work \ ratingsample \ build \ tomcat \ webapps \ app \ VAADIN \ widgetsets -src C:\ Users \ yuriy \ work \ ratingsample \ modules \ web \ src -src C:\ユーザー\ yuriy \ work \ ratingsample \ modules \ web-toolkit \ src com.haulmont.ratingsample.web.toolkit.ui.AppWidgetSet



build \ tomcat \ webapps \ app \ VAADIN \ widgetsets、modules \ web \ srcおよびmodules \ web-toolkit \ srcディレクトリへのパスは、独自のものに置き換える必要があります。



画像






4)Studioで実行:アプリケーションサーバーの実行開始

5)以前に作成したGWT構成をIdeaで実行します

6)アドレスhttp:// localhost:8080 / appに移動しますか?Debug&superdevmode

7)ChromeでDevToolsを開き、Javaコードを確認します。



画像






この方法の利点は、IDEからの特別なサポートを必要とせず、迅速に機能し、ブラウザでコードを直接デバッグできることです。 短所には、デバッグ中にJavaコードにアクセスできないという事実と、Java条件のブレークポイントが含まれます。これはなんとなく珍しいことです。 ファットマイナスもあります。古いブラウザーはソースマップの方法をまったく知らないため、通常のデバッグは困難です。



結論の代わりに



GWTは非常に強力で開発されたWebフレームワークであり、近年、世界中の多数の開発者によって積極的に使用されています。 Googleはその発想を忘れずに積極的に使用しています。最近では、GWTを集中的に使用するGmail Inbox( http://gmailblog.blogspot.ru/2014/11/going-under-hood-of-inbox.html )をリリースしました。 Webインターフェース。



Vaadinも遅れることはなく、現在ではGWTの最良のサーバー側オプションの1つです。 サーバーベースのモデルを使用すると、アプリケーションをより速く、より簡単に開発でき、データのセキュリティに関する心配が少なくなります。 GWTとVaadinの機能を改善する複雑さは非常に孤立しており、怖がるべきではありません。これらの開発技術の利点は、すべての欠点をカバーしています。



Vaadinは5年以上積極的に使用しており、自信を持っています。 特にビジネス指向のWebアプリケーションを構築するための主要なフレームワークとして検討することを皆に勧めます。



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



All Articles