{"id":1031,"date":"2011-07-11T11:05:53","date_gmt":"2011-07-11T09:05:53","guid":{"rendered":"http:\/\/www.tutego.de\/blog\/javainsel\/2011\/07\/mein-umstieg-auf-uibinder-gwt\/"},"modified":"2011-07-11T11:05:53","modified_gmt":"2011-07-11T09:05:53","slug":"mein-umstieg-auf-uibinder-gwt","status":"publish","type":"post","link":"https:\/\/www.tutego.de\/blog\/javainsel\/2011\/07\/mein-umstieg-auf-uibinder-gwt\/","title":{"rendered":"Mein Umstieg auf UiBinder (GWT)"},"content":{"rendered":"<p>Das komplette Frontend f\u00fcr die tutego-Kunden\/Trainer\/Serminarverwaltung ist in GWT implementiert. Die Codebase ist auf dem Level von GWT 1.1 und bis zur letzten Version hat sich viel ge\u00e4ndert. \u00dcber Updates blogge ich regelm\u00e4\u00dfig. Es wurde Zeit neu zu betrachten, welche Teile meiner Software ich vielleicht umbauen kann. Viele Themen sind spannend, aber leider gibt im Netz kaum Dokus, und da meine L\u00f6sung l\u00e4uft, war die Motivation nicht sonderlich hoch, Stunden zum Rumspielen zu investieren.<\/p>\n<p>Eine der gro\u00dfen Neuerungen in GWT 2.0 ist die M\u00f6glichkeit, deklarativ Oberfl\u00e4chen aufzubauen. Im Mittelpunkt steht das UiBinder-Famework, was eine XML-Dateien zur Beschreibung eines Widget-Baum nutzt und in eine initialisierte GWT-Komponente \u00fcberf\u00fchrt. Das Feature ist relativ gut dokumentiert und die \u00c4nderungen im Code sind klein. Daher habe ich mir ein paar Stunden f\u00fcr die \u00dcberf\u00fchrung einer View genommen.<\/p>\n<p>F\u00fcr jede View gibt es bei mir eine Klasse, die von einer GWT-Klasse wie Composite abgeleitet ist. Sie wird vom Presenter aufgebaut. Die View-Klasse deklariert eine Reihe von Objektvariablen. Die werden sp\u00e4ter initialisiert. Vorher sahen meine Programme etwas so aus:<\/p>\n<p>\/\/ Contact person<\/p>\n<p>HTML contactPersonLabel = new HTML( &quot;&lt;b&gt;AnsprechpartnerIn&lt;\/b&gt; (Name, EMail, Telefon)&quot; );    <br \/>contactPersonLabel.getElement().getStyle().setMarginTop( 0.5, Unit.EM );     <br \/>panel.add( contactPersonLabel );     <br \/>panel.add( contactPersonTextBox = new FormTextBox() );&#160;&#160;&#160; <\/p>\n<p>\/\/ tutego rate per day <\/p>\n<p>HTML ratePerDayLabel = new HTML( &quot;&lt;b&gt;tutego Tagessatz&lt;\/b&gt; (netto)&quot; );    <br \/>ratePerDayLabel.getElement().getStyle().setMarginTop( 0.5, Unit.EM );     <br \/>panel.add( ratePerDayLabel );     <br \/>panel.add( new HorizontalFlowPanel( ratePerDayTextField = new FormIntegerTextBox(), new HTML( &quot; Euro&quot; ) ) );     <br \/>ratePerDayTextField.setWidth( &quot;5em&quot; ); <\/p>\n<p>Mit Styles arbeite ich auch, wobei ich dies nicht f\u00fcr das Mikrolayout verwende. Lokale Abst\u00e4nde initialisierte ich immer direkt.<\/p>\n<p>Der UiBinder externalisiert die Beschreibung des Objektgraphen in eine XML-Datei. Es ist wichtig zu verstehen, dass der UiBinder keinen Template-Engine ist, also die XML-Datei kein XHTML ist, wo man a) einfach HTML schreiben kann und b) so etwas wie Wiederholungen\/Fallunterscheidungen schreiben kann. Es ist \u201cnur\u201d eine Beschreibung des Objektgraphen und ausschlie\u00dflich GWT-Widgets werden dort referenziert. (Auch wenn das bei einem SpanElement mit &lt;div&gt;&lt;\/div&gt; auf den ersten Blick nicht so aussieht.)<\/p>\n<p>Das GWT-Plugin f\u00fcr Eclipse erm\u00f6glicht gleich das Anlegen der XML-Datei und der Klasse, was f\u00fcr neue Views praktisch ist. Ich habe von Hand eine XML-Datei <em>EditCustomerView.ui.xml<\/em> aufgebaut, die so aussieht:<\/p>\n<p>&lt;!DOCTYPE ui:UiBinder SYSTEM &quot;<a href=\"http:\/\/dl.google.com\/gwt\/DTD\/xhtml.ent&quot;\">http:\/\/dl.google.com\/gwt\/DTD\/xhtml.ent&quot;<\/a>&gt;     <br \/>&lt;ui:UiBinder xmlns:ui=&#8217;urn:ui:com.google.gwt.uibinder&#8216;     <br \/>&#160;&#160;&#160; xmlns:g=&#8217;urn:import:com.google.gwt.user.client.ui&#8216; xmlns:t=&#8217;urn:import:traida.client.web.util&#8216;&gt;     <br \/>&#160;&#160;&#160; &lt;ui:with field=&#8217;res&#8216; type=&#8217;traida.client.resource.TraidaResources&#8216; \/&gt;     <br \/>&#160;&#160;&#160; &lt;ui:style&gt;     <br \/>&#160;&#160;&#160;&#160;&#160;&#160; .bottomGap { margin-bottom: 1em; }     <br \/>&#160;&#160;&#160; &lt;\/ui:style&gt;     <br \/>&#160;&#160;&#160; &lt;g:VerticalPanel&gt;     <br \/>&#160;&#160;&#160;&#160;&#160;&#160;&#160; &lt;g:Label styleName='{res.style.header1}&#8216; <strong>ui:field=&#8217;header&#8216;<\/strong> \/&gt; <\/p>\n<p>&#160;&#160;&#160;&#160;&#160;&#160;&#160; &lt;t:TitledPanel&gt;    <br \/>&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; &lt;g:HTML&gt;Adresse und Kontakt&lt;\/g:HTML&gt;     <br \/>&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; &lt;g:Anchor ui:field=&#8217;editContactAnchor&#8216;&gt;[Bearbeiten]&lt;\/g:Anchor&gt;     <br \/>&#160;&#160;&#160;&#160;&#160;&#160;&#160; &lt;\/t:TitledPanel&gt;     <br \/>&#160;&#160;&#160;&#160;&#160;&#160;&#160; &lt;g:HTML styleName='{style.bottomGap}&#8216; <strong>ui:field=&#8217;businessCardHTML&#8216;<\/strong> \/&gt; <\/p>\n<p>&#160;&#160;&#160;&#160;&#160;&#160;&#160; &lt;t:TitledPanel&gt;    <br \/>&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; &lt;g:HTML&gt;Kurs(e)&lt;\/g:HTML&gt;     <br \/>&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; &lt;g:Anchor <strong>ui:field=&#8217;addNewTrainingAnchor&#8216;<\/strong>&gt;[+]&lt;\/g:Anchor&gt;     <br \/>&#160;&#160;&#160;&#160;&#160;&#160;&#160; &lt;\/t:TitledPanel&gt;     <br \/>&#160;&#160;&#160;&#160;&#160;&#160;&#160; &lt;t:VerticalFlowPanel styleName='{style.bottomGap}&#8216; <strong>ui:field=&#8217;trainingsPanel&#8216;<\/strong> \/&gt; <\/p>\n<p>&#160;&#160;&#160;&#160;&#160;&#160;&#160; &lt;t:TitledPanel&gt;    <br \/>&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; &lt;g:HTML&gt;Vermerk und Logo&lt;\/g:HTML&gt;     <br \/>&#160;&#160;&#160;&#160;&#160;&#160;&#160; &lt;\/t:TitledPanel&gt; <\/p>\n<p>\u2026<\/p>\n<p>&#160;&#160;&#160; &lt;\/g:VerticalPanel&gt; <\/p>\n<p>&lt;\/ui:UiBinder&gt;<\/p>\n<p>Am Anfang werden zwei Namensr\u00e4ume g und t aufgebaut, einmal f\u00fcr die Google-Komponenten, einmal f\u00fcr eigene. Eigene Komponenten k\u00f6nnen einfach verwendet werden, auch die Tastaturvervollst\u00e4ndigung klappt, ist allerdings (nur bei mir?) ziemlich tr\u00e4ge.<\/p>\n<p>Wichtig sind die <strong>ui:field<\/strong>-Elemente. Sie entsprechen Variablen, die in einer Java-Klasse deklariert werden. Parallel zu der XML-Datei <strong>EditCustomerView.ui.xml <\/strong>gibt es eine andere zugeh\u00f6rige Java-Klasse <strong>EditCustomerView<\/strong>. Sie beginnt so:<\/p>\n<p>class EditCustomerView extends Composite    <br \/>{     <br \/>&#160; interface EditCustomerViewUiBinder extends UiBinder&lt;Widget, <strong>EditCustomerView<\/strong>&gt; {}&#160; \/\/ 2     <br \/>&#160; private static EditCustomerViewUiBinder uiBinder = GWT.create(EditCustomerViewUiBinder.class); <\/p>\n<p><strong>&#160; @UiField Label header;&#160;&#160;&#160; \/\/ 1<\/strong><\/p>\n<p>&#160; \u2026 <\/p>\n<p>&#160; EditCustomerView()    <br \/>&#160; {     <br \/>&#160;&#160;&#160; initWidget( <strong>uiBinder.createAndBindUi( this )<\/strong> );&#160; \/\/ 3     <br \/>&#160;&#160;&#160; setWidth( &quot;800&quot; );&#160; <br \/>&#160;&#160;&#160;&#160; \u2026<\/p>\n<p>&#160; }<\/p>\n<p>}<\/p>\n<p>Im Rumpf ist etwas Magie, und drei Details sind wichtig:<\/p>\n<ol>\n<li>Die Klasse hat f\u00fcr alle in der XML-Datei deklarierten ui:field-Dinge eine Objektvariable mit der entsprechenden Annotation @UiField. Die Typen m\u00fcssen zusammenpassen, also Label zu &lt;g:Label&gt; usw. Der UiBinder instanziiert selbst\u00e4ndig die Typen durch den Standardkonstruktor. Ist keiner vorhanden, muss man tricksen und eine Factory einf\u00fchren. <\/li>\n<li>Zu den Generics beim UiBinder: das erste Typargument (hier Widget) ist das, was sp\u00e4ter als Ergebnis \u201crauskommt\u201d. In der XML-Datei nutze ich ein VerticalPanel, sodass der Typ h\u00e4tte eigentlich auch spezieller sein k\u00f6nnen, doch den spezielleren Typ brauche ich nicht. Das zweite Typargument ist die Klasse, die die @UiField-Variablen deklariert, also die eigene Klasse selbst. <\/li>\n<li>Der Aufruf createAndBindUi() baut den Objektbaum auf. Das Ergebnis der Methode ist vom Typ, der im ersten Generics zugewiesen wurde, also im Beispiel Widget. <\/li>\n<\/ol>\n<p>Das zur Technik. Die Frage ist, ob sich die Umstellung gelohnt hat. Bisher habe ich eine View umgestellt, doch das Ergebnis sieht gut aus. Die hierarchische Gliederung ist \u00fcbersichtlich, wobei mir die XML-Ansicht reicht und ich den Designer nicht brauche. Allerdings f\u00e4nde ich es besser, das Layout einfacher ver\u00e4ndern zu k\u00f6nnen. Soll zum Beispiel der Abstand zur n\u00e4chsten Zeile erholt werden, so kann man nicht einfach beim Element selbst ein CSS-Syle setzen, sondern muss ein &lt;style&gt; Element einf\u00fchren, ihm einen Namen geben und dann zuweisen. Das ist mehr Schreibarbeit als n\u00f6tig. Die anderen Views werde ich nun auch umstellen.<\/p>\n<p>Ob ich die UiBinder f\u00fcr jede View einsetzen werde wird sich zeigen, da einige Views viel st\u00e4ter aus HTML aufgebaut werden und die View im Prinzip nur einen Titel setzt und der Rest ist HTML.<\/p>\n<p>Ein paar Links:<\/p>\n<ul>\n<li><a href=\"http:\/\/code.google.com\/intl\/de\/webtoolkit\/doc\/latest\/DevGuideUiBinder.htm\">http:\/\/code.google.com\/intl\/de\/webtoolkit\/doc\/latest\/DevGuideUiBinder.htm<\/a><\/li>\n<li><a href=\"http:\/\/stackoverflow.com\/questions\/2052994\/how-to-declare-dependent-style-names-with-uibinder\">http:\/\/stackoverflow.com\/questions\/2052994\/how-to-declare-dependent-style-names-with-uibinder<\/a><\/li>\n<li><a href=\"http:\/\/aarendar.wordpress.com\/2010\/03\/02\/learning-gwt-uibinder-part-2-styles-and-annotations\/\">http:\/\/aarendar.wordpress.com\/2010\/03\/02\/learning-gwt-uibinder-part-2-styles-and-annotations\/<\/a><\/li>\n<\/ul>\n","protected":false},"excerpt":{"rendered":"<p>Das komplette Frontend f\u00fcr die tutego-Kunden\/Trainer\/Serminarverwaltung ist in GWT implementiert. Die Codebase ist auf dem Level von GWT 1.1 und bis zur letzten Version hat sich viel ge\u00e4ndert. \u00dcber Updates blogge ich regelm\u00e4\u00dfig. Es wurde Zeit neu zu betrachten, welche Teile meiner Software ich vielleicht umbauen kann. Viele Themen sind spannend, aber leider gibt im [&hellip;]<\/p>\n","protected":false},"author":1,"featured_media":0,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"_jetpack_memberships_contains_paid_content":false,"footnotes":"","_links_to":"","_links_to_target":""},"categories":[16],"tags":[],"class_list":["post-1031","post","type-post","status-publish","format-standard","hentry","category-gwt"],"jetpack_featured_media_url":"","jetpack_sharing_enabled":true,"_links":{"self":[{"href":"https:\/\/www.tutego.de\/blog\/javainsel\/wp-json\/wp\/v2\/posts\/1031","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/www.tutego.de\/blog\/javainsel\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/www.tutego.de\/blog\/javainsel\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/www.tutego.de\/blog\/javainsel\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/www.tutego.de\/blog\/javainsel\/wp-json\/wp\/v2\/comments?post=1031"}],"version-history":[{"count":0,"href":"https:\/\/www.tutego.de\/blog\/javainsel\/wp-json\/wp\/v2\/posts\/1031\/revisions"}],"wp:attachment":[{"href":"https:\/\/www.tutego.de\/blog\/javainsel\/wp-json\/wp\/v2\/media?parent=1031"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/www.tutego.de\/blog\/javainsel\/wp-json\/wp\/v2\/categories?post=1031"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/www.tutego.de\/blog\/javainsel\/wp-json\/wp\/v2\/tags?post=1031"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}