Chapter 1. Einführung User Interface

Table of Contents

Fortgeschrittenes am User Interface
Fehler und Ursachen
Inhalt der Person Entität als Formular anzeigen
Fehler und Ursachen

Fortgeschrittenes am User Interface

  1. Nun wird es Zeit einen decorator für den screen der Anwendung anzulegen. Legen Sie die Datei CommonScreens.xml im Verzeichnis widget an. Diese Datei enthält die allgemeinen screens welche durchweg in der Anwendung verwendet werden. Ein allgemeiner screen könnte ein Kopf- und ein Fußteil haben. Jeder andere Screen welches diesen als decorator hat dann ebenfalls Kopf- und Fußteil.

    <?xml version="1.0" encoding="UTF-8"?>
    <screens xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
            xsi:noNamespaceSchemaLocation="http://ofbiz.apache.org/dtds/widget-screen.xsd">
        <screen name="main-decorator">
            <!-- The main-decorator screen 'wraps' or 'decorates' all of the screens in the
                practice component. It is also decorated - by the GlobalDecorator screen. -->
            <section>
                <actions>
                    <!-- base/top/most specific map first, then more common map added for shared labels.
                        Setting things up this way enables a component to redefine the more common
                        UI labels. -->
                    <property-map resource="CommonUiLabels" map-name="uiLabelMap" global="true"/>
                    <property-map resource="PracticeUiLabels" map-name="uiLabelMap" global="true"/>
                    <!-- The layoutSettings field is a Map that is used to pass variables and layout
                        information to the GlobalDecorator and any templates that it uses. -->
                    <set field="layoutSettings.companyName" from-field="uiLabelMap.PracticeCompanyName" global="true"/>
                    <set field="activeApp" value="practice" global="true"/>
                    <!-- Default (main) javascript files -->
                    <set field="layoutSettings.javaScripts[+0]" value="/images/prototypejs/validation.js" global="true"/>
                    <set field="layoutSettings.javaScripts[]" value="/images/prototypejs/prototype.js" global="true"/>
                    <set field="applicationMenuName" value="PracticeAppBar" global="true"/>
                    <set field="applicationMenuLocation" value="component://practice/widget/PracticeMenus.xml" global="true"/>
                </actions>
                <widgets>
                    <include-screen name="GlobalDecorator" location="component://common/widget/CommonScreens.xml"/>
                </widgets>
            </section>
        </screen>
        
        <screen name="CommonPracticeDecorator">
            <section>
                <widgets>
                    <decorator-screen name="main-decorator">
                        <decorator-section name="body">
                            <decorator-section-include name="body"/>
                        </decorator-section>
                    </decorator-screen>
                </widgets>
            </section>
        </screen>
    </screens>    
    

    Wie immer können Sie Bezug auf die CommonScreens.xml Datei aus dem Beispielen nehmen und Beispiele zur Verwendung von main-decorator einsehen. Zu diesem Thema finden Sie wichtige Informationen unter den beiden Links: Understanding the OFBiz Widget Toolkit und den Abschnitt "The Decorator" in den FAQ .

  2. Legen Sie ein Menü für die Anwendung an. Dafür legen Sie eine Datei Namens PracticeMenus.xml im Verzeichnis widget der Komponente an. Ziehen Sie in die Datei ExampleMenus.xml des Beispielprojektes als Referenz heran.

    <?xml version="1.0" encoding="UTF-8"?>
    <menus xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="http://ofbiz.apache.org/dtds/widget-menu.xsd">
        <menu name="PracticeAppBar" title="PracticeApplication" extends="CommonAppBarMenu" extends-resource="component://common/widget/CommonMenus.xml">
            <menu-item name="main" title="Main"><link target="main"/></menu-item>
        </menu>
    </menus>
    

    Binden Sie dieses Menüdatei in ihren CommonPracticeDecorator wie folgt ein.

    <screen name="CommonPracticeDecorator">
            <section>
                <widgets>
                    <include-menu location="component://practice/widget/PracticeMenus.xml" name="PracticeAppBar"/>
                    <decorator-section-include name="body"/>
                </widgets>
            </section>
        </screen>
    

  3. Legen Sie das Verzeichnis actions im Verzeichnis WEB-INF an. In dieses Verzeichnis werden wir Skripte ablegen. Über Skipte werden Daten zur Laufzeit bereitgestellt. Die Skripte sind Groovy-Skripte (in früheren Versionen wurden beanshell-Skripte verwendet). Unser Skript wird Daten aus der Datenbank lesen und zur Laufzeit für das UI aufbereiten.

    Referenzen: Tips & Tricks while working with Groovy und http://groovy.codehaus.org/ .

    Achten Sie bei Groovy-Skripten immer auf die verwendeten Imports. Importieren Sie immer nur das was auch Verwendung findet. Um Log-Meldungen zu schreiben verwenden Sie bitte von Anfang an die Debug -Klasse.

    Legen Sie eine Datei mit dem Namen person.groovy im Verzeichnis actions an. Dieses Skript wird alle Werte für die Entität Person aus der Datenbank lesen. Für den Moment ist der Code mit einer Zeile wirklich winzig.

    context.persons = delegator.findList("Person", null, null, null, null, false);
    

    Der obige Code zieht alle Werte von der Entität Person und legt sie in den Context der entsprechenden Person. In der ftl Datei wird über die Liste der Personen iteriert.

    Wichtige Informationen dazu finden Sie unter: Which variables are available in screen context?

  4. Im Verzeichnis practice/webapp/practice/ legen sie nun die Datei person.ftl an. Diese Datei ist für die Anzeige der Werte, welche über das Groovy-Skript gelesen wurden, verantwortlich.

    Siehe dazu auch: http://freemarker.sourceforge.net/docs/ .

    Im Moment brauchen Sie nur über die Liste der Personen aus dem Context zu iterieren. Dazu sind die wenigen folgenden Zeilen Code notwendig.

    <#if persons?has_content>
      <h2>Some of the people who visited our site are:</h2>
      <br>
      <ul>
        <#list persons as person>
          <li>${person.firstName?if_exists} ${person.lastName?if_exists}</li>
        </#list>
      </ul>
    </#if>

  5. Nun legen Sie einen screen mit dem Namen person über die Datei PracticeScreens.xml an. Gleichzeitig erzeugen Sie einen neuen Menüeintrag über die PracticeMenus.xml Datei.

    Der neue Menüeintrag in der PracticeScreens.xml sieht folgender Maßen aus:

    <screen name="person">
            <section>
                <actions>
                    <script location="component://practice/webapp/practice/WEB-INF/actions/person.groovy"/>
                </actions>
                <widgets>
                    <decorator-screen name="CommonPracticeDecorator" location="${parameters.mainDecoratorLocation}">
                        <decorator-section name="body">
                            <platform-specific>
                                <html>
                                    <html-template location="component://practice/webapp/practice/person.ftl"/>
                                </html>
                            </platform-specific>
                        </decorator-section>
                    </decorator-screen>       
                </widgets>
            </section>
        </screen>															

  6. Nun ändern Sie die controller.xml Datei so, dass auf den neuen screen verwiesen wird.

    <?xml version="1.0" encoding="UTF-8"?>
    <site-conf xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    	xsi:noNamespaceSchemaLocation="http://ofbiz.apache.org/dtds/site-conf.xsd">
    
    	<include location="component://common/webcommon/WEB-INF/common-controller.xml" />
    	<description>Practice Component Site Configuration File</description>
    	<owner>Copyright 2001-2009 The Apache Software Foundation</owner>
    
    	<handler name="screen" type="view"
    		class="org.ofbiz.widget.screen.ScreenWidgetViewHandler" />
    
    	<!-- Request Mappings -->
    	<request-map uri="main">
    		<security https="false" auth="false" />
    		<response name="success" type="view" value="main" />
    	</request-map>
    	<request-map uri="person">
    		<security https="false" auth="true" />
    		<response name="success" type="view" value="person" />
    	</request-map>
    	<!-- end of request mappings -->
    
    	<!-- View Mappings -->
    	<view-map name="main" type="screen"
    		page="component://practice/widget/PracticeScreens.xml#main" />
    	<view-map name="person" type="screen"
    		page="component://practice/widget/PracticeScreens.xml#person" />
    	<!-- end of view mappings -->
    
    </site-conf>
    

    Nun starten Sie die Anwendung und prüfen das Ergebnis http://localhost:8080/practice/control/person .

    Das Beispiel kann ohne Anmeldung aufgerufen werden. Wegen besser Sichtbarkeit wurde für das Bildschirmfoto eine anderes Thema (Theme) gewählt. Dazu ist allerdings eine Anmeldung notwendig.

    Falls also ihr screen kein Menü enthält, aktivieren Sie die Authentifizierung. Sobald auth="false" in der controller.xml Datei auf auth="true" für die URI person gestellt wurde wird eine Anmeldung benötigt und dann erscheint auch das Menü.

Fehler und Ursachen

Es ist alles soweit erledigt, die ersten Anfragen an OFBiz werden gestartet und dann das: irgend etwas geht schief. Mögliche Fehler und ihre Ursachen für dies Abschnitt:

  • Der Aufruf http://localhost:8080/practice/control/person liefert folgende Fehlermeldung:

    Message: Unknown request [person]; this request does not exist or cannot be called directly.

    Lösung: Überprüfen Sie die Groß-/Kleinschreibung und Pfade zu den Dateien (besonders person.groovy, person.ftl und actions). Die originale, englische Anleitung ist an ein paar Stellen diesbezüglich Fehlerhaft.

    Dies betrifft auch die Groß-/Kleinschreibung in den Dateien: PracticeScreens.xml und controller.xml .

  • Der Aufruf http://localhost:8080/practice/control/person liefert folgende Fehlermeldung:

    org.ofbiz.widget.screen.ScreenRenderException: Error rendering screen [component://common/widget/CommonScreens.xml#login]: java.lang.IllegalArgumentException: Could not find screen with name [main-decorator] in class resource [component://practice/widget/CommonScreens.xml] (Could not find screen with name [main-decorator] in class resource [component://practice/widget/CommonScreens.xml])

    Lösung: Prüfen Sie den Aufbau der CommonScreens.xml Datei.

Inhalt der Person Entität als Formular anzeigen

  1. Fügen Sie einen weiteren Punkt zum Menü in der PracticeMenus.xml hinzu.

  2. Legen Sie ein Datei namens PracticeForms.xml im Verzeichnis widget an. Erzeugen Sie ein Formular für eine Liste um die Datensätze der Entität Person anzuzeigen. (Nehmen Sie Bezug ExampleScreens.xml und ExampleForms.xml )

    <?xml version="1.0" encoding="UTF-8"?>
    <forms xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 
            xsi:noNamespaceSchemaLocation="http://ofbiz.apache.org/dtds/widget-form.xsd">
        <form name="ListPersons" type="list" list-name="persons" list-entry-name="person" target="updatePracticePerson">
            <auto-fields-service service-name="updatePracticePerson" default-field-type="edit" map-name="person"/>
            <field name="partyId"><hidden/></field>
            <field name="deleteLink" title="" widget-style="buttontext">
                <hyperlink target="deletePracticePerson" description="${uiLabelMap.CommonDelete}" also-hidden="false">
                    <parameter param-name="partyId" from-field="person.partyId"/>
                </hyperlink>
            </field>
            <field name="submitButton" title="${uiLabelMap.CommonUpdate}"><submit button-type="button"/></field>
        </form>
    </forms>
    
    						

  3. Erzeugen Sie in der CommonScreens.xml Datei einen weiteren screen mit den Namen personForm und fügen Sie folgendes Formular für Listen ein.

    <screen name="PersonForm">
            <section>
                <actions>
                    <set field="headerItem" value="personForm"/>
                    <set field="titleProperty" value="PageTitlePracticePersonForm"/>
                    <entity-condition entity-name="Person" list="persons"/>
                </actions>
                <widgets>
                    <decorator-screen name="CommonPracticeDecorator" location="${parameters.mainDecoratorLocation}">
                        <decorator-section name="body">
                            <label text="Person List" style="h2"/>
                            <include-form name="ListPersons" location="component://practice/widget/PracticeForms.xml"></include-form>
                        </decorator-section>
                    </decorator-screen>       
                </widgets>
            </section>
    </screen>
    
    						

  4. Jetzt muss noch controller.xml angepasst werden um den screen anzuzeigen.

    Fügen Sie dazu bitte folgende Abschnitte an die entsprechenden stellen der controller.xml Datei ein.

    <request-map uri="PersonForm">
        <security https="false" auth="true"/>
        <response name="success" type="view" value="PersonForm"/>
    </request-map>
    <request-map uri="updatePracticePerson">
        <security https="true" auth="true"/>
        <event type="service" invoke="updatePracticePerson"/>
        <response name="success" type="view" value="PersonForm"/>
        <response name="error" type="view" value="PersonForm"/>
    </request-map>
    

    und

        <view-map name="PersonForm" type="screen" page="component://practice/widget/PracticeScreens.xml#PersonForm"/>
    

    Im Unterschied zur Liste der Personen erfordert das Formular nun die Authentifizierung ( auth="true" ).

  5. Es muss die Datei PracticeScreens.xml angepasst werden. Ergänzen Sie die Datei an passender Stelle um folgende Zeilen.

    <screen name="PersonForm">
        <section>
            <actions>
                <set field="headerItem" value="personForm"/>
                <set field="titleProperty" value="PageTitlePracticePersonForm"/>
                <entity-condition entity-name="Person" list="persons"/>
            </actions>
    
            <widgets>
                <decorator-screen name="CommonPracticeDecorator" location="${parameters.mainDecoratorLocation}">
                    <decorator-section name="body">
                        <label text="Person List" style="h2"/>
                        <include-form name="ListPersons" location="component://practice/widget/PracticeForms.xml"/>
                    </decorator-section>
                </decorator-screen>
            </widgets>
        </section>
    </screen>
    

  6. Es muss noch ein Service definiert werden. Erzeugen Sie die Datei /practice/servicedef/services.xml an.

    <?xml version="1.0" encoding="UTF-8"?>
    
    <services xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
        xsi:noNamespaceSchemaLocation="http://ofbiz.apache.org/dtds/services.xsd">
        <description>Practice Services</description>
        <!-- Party related Services -->
    
        <service name="updatePracticePerson" default-entity-name="Person"
            engine="simple"
            location="component://practice/script/org/hotwax/practice/PracticeServices.xml"
            invoke="updatePracticePerson" auth="true">
    
            <description>Update a person</description>
    
            <auto-attributes include="pk" mode="IN" optional="false" />
            <attribute name="firstName" mode="IN" type="String"
                optional="false" />
            <attribute name="middleName" mode="IN" type="String"
                optional="true" />
            <attribute name="lastName" mode="IN" type="String" optional="false" />
        </service>
    
    </services>
    

  7. Die Service-Definition muss in der ofbiz-component.xml eingebunden werden. Dazu fügen sie folgende Zeile an passender Stelle ein.

    <service-resource type="model" loader="main" location="servicedef/services.xml"/> 
    

  8. Nun starten Sie die Anwendung und rufen http://localhost:8080/practice/control/PersonForm auf. Nach der Anmeldung sehen sie folgendes:

Das erste Formular ist erstellt auch wenn noch nicht alle Funktionen implementiert sind. Wir werden dies im weiteren noch nachholen.

Kleine Rekapitulation, bis jetzt haben Sie an folgendem gearbeitet: controller requests mappings, Screen widget, form widget, Decorator, Menus, groovy, ftl.

Fehler und Ursachen

Mögliche Fehler und ihre Ursachen für dies Abschnitt:

  • Aufruf von alhost:8080/practice/control/PersonForm liefert folgendes:

    org.ofbiz.widget.screen.ScreenRenderException: Error rendering screen [component://common/widget/CommonScreens.xml#GlobalDecorator]: java.lang.RuntimeException: Error rendering included form named [ListPersons] at location [component://practice/widget/PracticeForms.xml]: java.lang.IllegalArgumentException: Error finding Service with name updatePracticePerson for auto-fields-service in a form widget (Error rendering included form named [ListPersons] at location [component://practice/widget/PracticeForms.xml]: java.lang.IllegalArgumentException: Error finding Service with name updatePracticePerson for auto-fields-service in a form widget)

    Lösung: Die Einträge für updatePracticePerson in der controller.xml könnten fehlerhaft sein. Oder die service definition ( servicedef/services.xml ) sind fehlerhaft oder nicht in der ofbiz-component.xml Datei eingebunden.

  1. Die Anwendung benötigt noch einen main Decorator über den die Auszeichnung erfolgt.

    Legen Sie einen screen mit dem Namen main-decorator in der CommonScreens.xml . (Nehmen Sie CommonScreens.xml aus dem Beispielen als Referenz.)

    <screen name="main-decorator">
            <section>
                <actions>
                    <property-map resource="CommonUiLabels" map-name="uiLabelMap" global="true"/>
                    <set field="layoutSettings.companyName" from-field="uiLabelMap.PracticeCompanyName" global="true"/>
                    <set field="activeApp" value="practice" global="true"/>
                    <set field="applicationMenuName" value="PracticeAppBar" global="true"/>
                    <set field="applicationMenuLocation" value="component://practice/widget/PracticeMenus.xml" global="true"/>
                </actions>
                <widgets>
                    <include-screen name="GlobalDecorator" location="component://common/widget/CommonScreens.xml"/>
                </widgets>
            </section>
    </screen>
    

  2. Den Decorator haben Sie bereits in den CommonPracticeDecorator screen ( PracticeScreens.xml ) eingefügt.