Widgets verwenden und erstellen
Widgets sind wiederverwendbare Steuerelemente für grafische Benutzeroberflächen von Web-Applikationen, wie z.B. Kalender, Toolbars, Menüs, RSS-Feeds oder Tabellen.
Das INUBIT Widget Framework bietet Ihnen die Möglichkeit, eigene Widgets zu erstellen. Für Ihre Widgets können Sie z.B. die Funktionen des Ext JS-Frameworks nutzen, das zusammen mit der INUBIT-Software ausgeliefert wird. Das Ext JS-Framework ist ein client-seitiges JavaScript- bzw. Ajax-Framework und bietet eine umfangreiche Sammlung von Steuerelementen.
Im Lieferumfang der INUBIT-Software ist das einsatzbereite Widget Grid enthalten. Dieses Widget stellt Daten als Tabelle dar und bietet u.a. Paging und Sortierung auf Basis des Ext JS-Grids, z.B.:
Funktionsprinzip
Ein Widget besteht aus HTML-, JavaScript- und CSS-Code. In der INUBIT-Software wird dieser Code bei der Ausführung des Task Generators genauso dynamisch erzeugt wie das normale Formular, in dem das Widget eingebettet ist:
-
Die XML-Struktur des Formulars, die im Register Formular Mapping des Task Generators angezeigt wird, enthält auch die XML-Struktur des Widgets. Für das mitgelieferte Grid-Widget wird z.B. eine XML-Tabellenstruktur aufgebaut, die zusätzlich Daten zur Konfiguration der Tabelle enthält, wie z.B. für das Paging, die maximale Anzahl der Datensätze auf einer Seite.
Meist wird diese XML-Struktur dynamisch mit Daten aus der Eingangsnachricht gefüllt.
-
Ein widget-spezifisches XSLT-Stylesheet erzeugt aus der XML-Struktur den HTML-Code, der im Browser im Formular angezeigt wird. Das widget-spezifische XSLT-Stylesheet enthält neben XSLT-Befehlen auch JavaScript- und CSS-Code, mit dem Funktionalität und Layout des Widgets realisiert werden.
Die XML-Struktur und das XSLT-Stylesheet des Widgets werden im Repository abgelegt. Damit bleibt die XML-Struktur des Formulars im Task Generator kompakt und lesbar und das Widget kann in anderen Task Generatoren wiederverwendet werden.
Widgets werden, genau wie die anderen GUI-Komponenten, per Drag’n’Drop in ein Formular integriert.
Sie können dasselbe Widget mehrfach auf einer Seite verwenden, auch in verschiedenen Panels.
Falls die Verwendung mehrerer Widgets Rendering-Probleme verursacht, stellen Sie sicher, dass Sie eindeutige Namen in jeder Komponente oder Meta (falls vorhanden) verwenden. |
Grid-Widget verwenden und konfigurieren
Mit dem Grid-Widget können Sie in Ihrer Web-Applikationen Daten in Tabellenform darstellen. Das Grid-Widget basiert auf Funktionen des Ext JS-Frameworks und bietet folgende Features:
-
Sortierung
Spalteninhalt auf- oder absteigend sortieren:
Abhängig von der Konfiguration der Tabelle findet die Sortierung server- oder client-seitig statt. Siehe
-
Beispiel: Dynamische Tabelle mit client-seitiger Sortierung ohne Paging
-
Beispiel: Dynamische Tabelle mit Paging und server-seitiger Sortierung
-
Spaltenreihenfolge ändern
Per Drag-and-Drop:
-
Spalten ein/ausblenden
-
Paging
Große Datenmengen können aufgeteilt und seitenweise angezeigt werden. Die Anzahl der auf einer Seite angezeigten Daten ist konfigurierbar, die Daten werden dynamisch über Ajax-Calls nachgeladen.
So gehen Sie vor
-
Zeigen Sie im Task Generator das Register Formular Mapping an.
-
Ziehen Sie das Grid-Icon aus der Widget-Leiste des Designers in dessen Arbeitsfläche:
Im Bereich XML-Ziel wird die erwartete XML-Struktur des Grids angezeigt:
Sie können die XML-Struktur des Grids statisch oder dynamisch mit Daten füllen.
Beispiele siehe:
Beispiel: Dynamische Tabelle mit client-seitiger Sortierung ohne Paging
Die folgende Abbildung zeigt das Formular Mapping einer Tabelle, die durch XML-Eingangsnachrichten befüllt wird. Die Anzahl der Reihen und Spalten wird durch die XML-Eingangsnachrichten definiert:
Die Sortierung nach Tabellenspalten steht automatisch zur Verfügung, solange das sortable
-Attribut des Column
- Elements den Wert true
hat.
Beispiel: Dynamische Tabelle mit Paging und server-seitiger Sortierung
Der Einsatz des Paging-Mechanismus und der server-seitigen Sortierung setzt voraus, dass die Daten im Grid-Widget aus einer Datenquelle geladen werden können.
Dazu muss der Workflow, der Ihr Grid-Widget als Instanz realisiert, wie in der folgenden Abbildung aufgebaut sein:
Der Task Generator, welcher das Grid-Widget erzeugt, ist über einen Pfad mit der Datenquelle verbunden: In dem abgebildeten Workflow beginnt dieser Pfad bei dem Workflow Connector WFC_JumpTarget
und führt zu einem Database Connector in einem Subworkflow, von dort aus wieder zurück zum Task Generator.
Die Einsprungstelle in diesen Pfad, also die ID des ersten Moduls, wird im Grid-Widget eingegeben.
Benutzeraktionen, wie z.B. die Anzeige des Grid-Forms oder das Sortieren der Daten, lösen bei dem Task Generator einen Request aus. Der Inhalt des Requests ist abhängig von der Benutzeraktion: Bei einer Sortieraktion enthält der Request z.B. folgende Informationen:
-
Erster Datensatz, der auf der Seite angezeigt werden sollen
-
Anzahl aller Datensätze, die auf der Seite angezeigt werden sollen
-
Gewähltes Sortierkriterium (aufsteigend/absteigend)
Der Request wird an das erste Modul im Pfad übergeben und von diesem weitergeleitet. Der Database Connector wertet den Request aus und stellt die Daten bereit. Danach werden die angeforderten Daten an den Task Generator gesendet und in der Tabelle angezeigt.
Einsprungstelle in Pfad zur Datenquelle festlegen
Die folgende Abbildung zeigt das Formular Mapping der Tabelle:
Das erste Modul im Pfad zur Datenquelle definieren Sie im Register Eigenschaften über das jump
-Attribut des Widget
- Elements: Ein Klick auf die leere Zelle neben dem Attribut öffnet einen Dialog, in dem Sie das Modul auswählen.
Das Modul muss in demselben Workflow wie der Task Generator liegen! |
Ob und welche Module zwischen der Einsprungstelle und der Datenquelle liegen, ist abhängig davon, ob der Request weiter aufbereitetet werden muss. Dies kann z.B. nötig sein, wenn Sie die Daten server-seitig sortieren lassen möchten.
Anzahl der angezeigten Datensätze festlegen
Die Anzahl der Datensätze pro Seite legen Sie im Register Eigenschaften über die start
- und limit
-Attribute des
Header
-Elements fest:
-
start
: Nummer des ersten angezeigten Datensatzes -
limit
: Anzahl der angezeigten Datensätze
Server-seitige Sortierung konfigurieren
Die Sortierung müssen Sie manuell implementieren. Wenn Sie als Datenquelle eine Datenbank einsetzen, dann können Sie die Sortierkriterien z.B. durch entsprechende SQL-Befehle umsetzen.
Die Sortierkriterien sind in dem Request enthalten, der an die Datenquelle gesendet wird. Sie können den Request anzeigen, in dem Sie im Watch-Modus den Watchpoint vor der Datenquelle öffnen.
Widget erstellen und in die INUBIT-Software integrieren
Benötigte Dateien
Um ein eigenes Widget zu entwickeln, müssen Sie folgende Dateien erstellen:
Name | Funktion |
---|---|
|
XML-Struktur des Widgets. Diese hat zwei Funktionen:
|
|
XSLT-Stylesheet für die Darstellung des Widgets in der Web-Applikation. Enthält z.B. HTML, JavaScript-Code (evtl. Ext JS-Framework-Code) für die Funktionalität des Widgets und Layout-Informationen (als CSS-Referenzen). Siehe Beispiel: Widget.xsl |
|
XSLT-Stylesheet zur Anzeige von Widgets in Web-Applikationen im Business Process Center (BPC). Es enthält z.B. HTML, JavaScript-Code (möglicherweise Code des Ext JS-Framework) für die Funktionen des Widgets und Layout-Informationen (als CSS-Referenzen). Siehe Beispiel: Widget_bpc.xsl |
|
Grafik für das Widget-Icon in der Symbolleiste des Task Generators. Über das Icon kann die XML-Struktur per Drag-and-Drop an der gewünschten Position im Formular eingefügt werden. Die Grafik muss im gif-Format mit einer max. Höhe von 14 Pixel vorliegen. |
|
Optional.
Java-Klasse für die Darstellung des Widgets im Reiter Formular Mapping des Task Generators.
Die Klasse muss Wenn Sie keine Java-Klasse bereitstellen, müssen Sie in der XML-Struktur Ihre Widgets ( Wenn keine Java-Klasse vorhanden ist, wird im Designer ein Platzhalter statt des Widgets angezeigt. Die Funktionalität des Widgets wird dadurch nicht beeinträchtigt. |
Die Dateien sollten im globalen Bereich des Repositorys unter Global > System > Widgets in einem eigenen Ordner verfügbar gemacht werden und anonym lesbar sein. Alternativ dazu können die Dateien auch in irgendeinem anderen Repository-Ordner außerhalb des Repository-Ordners Global bereitgestellt werden.
Siehe
-
Ext JS-Beispiele:
-
Ext JS 6:
https://<server>:<port>/html/js/extjs6/
-
Ext JS 7:
https://<server>:<port>/html/js/extjs7/
-
-
Ext JS-Homepage siehe: https://www.sencha.com/
So gehen Sie vor
-
Nutzen Sie die Dateien
Widget.xml
undWidget.xsl
(Widget_bpc.xsl
für BPC-Portalbenutzer) des mitgelieferten Grid-Widgets als Vorlagen: Laden Sie diese Dateien aus dem Repository-Verzeichnis Global > System > Widgets > Grid herunter. -
Öffnen Sie die Datei
Widget.xml
und passen Sie die XML-Struktur an. Beachten Sie dabei die Kommentare in der Datei, diese erläutern die Verwendung der verschiedenen XML-Elemente. -
Passen Sie die Datei
Widget.xsl
(Widget_bpc.xsl
für BPC-Portalbenutzer) an. Beachten Sie auch in dieser Datei die Kommentare. -
Erzeugen Sie ein Icon mit dem Namen
Widget.gif
. -
Erzeugen Sie im Repository ein neues Verzeichnis unterhalb von Global > System> Widgets. Fügen Sie alle Dateien diesem Verzeichnis hinzu. Stellen Sie sicher, dass die Dateien anonym lesbar sind.
-
Wenn Sie das neue Widget sofort einsetzen möchten, müssen Sie das Icon zuerst sichtbar machen: Öffnen Sie einen Task Generator, zim Modul-Editor auf Lokal-Seite zeigen Sie das Register Formular Mapping an und wählen Sie Burger-Menü > Ansicht > Externe Elemente neu laden.
Ansonsten steht das Widget erst nach dem Neustart der INUBIT Workbench zur Verfügung.
Beispiel: Datepicker-Widget
Ein Datepicker vereinfacht die Eingabe eines Datums:
Der Datepicker nutzt das Ext JS-Framework und basiert auf der Klasse DateField
.
Beispiel: Widget.xml
<?xml version="1.0" encoding="UTF-8"?>
<wd:Description xmlns:wd="http://inubit.com/widget/schema" type="ExtDateField" uiClass="com.inubit.web.extjs.form.ExtDateFieldWidget" namespace="http://inubit.com/widget/ExtForm" prefix="">
<Widget>
<wd:Attributes>
<!-- Keep in mind that, if you are using several date pickers in a form,
every instance must have a unique name-->
<name/>
</wd:Attributes>
<DateField>
<wd:Attributes>
<!-- The date field should be 300 px wide;
the text field can have all attributes of a "normal" text field -->
<width>300</width>
</wd:Attributes>
</DateField>
<Meta>
<wd:Attributes>
<!-- The format value can be defined in the designer and defines in which format the date
is displayed, permissible values are specified by the Ext JS framework -->
<format/>
</wd:Attributes>
</Meta>
</Widget>
</wd:Description>
Beispiel: Widget.xsl
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:isgrid="http://inubit.com/widget/ISGrid" xmlns:inubit="http://inubit.com/xsl" version="2.0">
<xsl:output method="html" encoding="UTF-8"/>
<xsl:template match="extform:Widget[@type='ExtDateField']">
<xsl:variable name="wid" select="concat($ns,@name)"/>
<xsl:variable name="tf">
<TextField name="{@name}" xmlns="">
<xsl:for-each select="extform:DateField/@*[name()!='name']"><xsl:attribute name="{local-name(.)}"><xsl:value-of select="."/></xsl:attribute></xsl:for-each>
<xsl:value-of select="extform:DateField"/>
</TextField>
</xsl:variable>
<script language="javascript" type="text/javascript">
addCssIS('/html/js/extjs/resources/css/ext-all.css');
addCssIS('/html/js/extjs/resources/css/xtheme-gray.css');
</script>
<xsl:apply-templates select="$tf/TextField"/>
<script language="javascript" type="text/javascript">
Ext.onReady(function(){
try {
new Ext.form.DateField({
applyTo: '<xsl:value-of select="$wid"/>'
<xsl:if test="extform:DefaultTextField/@mandatory = 'true'">
,allowBlank: false
</xsl:if>
<xsl:if test="extform:Meta/@format != ''">
,format: '<xsl:value-of select="extform:Meta/@format"/>'
</xsl:if>
});
}
catch(e) {
showMessageIS('Error in ExtDateField widget!',e);
}
});
</script>
</xsl:template>
</xsl:stylesheet>
Beispiel: Widget_bpc.xsl
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:isgrid="http://inubit.com/widget/ISGrid" xmlns:inubit="http://inubit.com/xsl" version="2.0">
<!-- The widget namespace must be identical with the one defined in the Widget.xml -->
<!--$Id: Widget_bpc.xsl,v 1.0 2022/01/24 $-->
<xsl:output method="html" encoding="UTF-8"/>
<!-- need to set this params, will be filled from outside -->
<xsl:param name="initDataISGrid"/>
<xsl:param name="jumpDataISGrid"/>
<!-- Dummy variables to test this script in an XSLT-Converter; they are set from outside on execution time -->
<!--<xsl:variable name="proxy"></xsl:variable>-->
<!--<xsl:variable name="lang"></xsl:variable>-->
<!--<xsl:variable name="ns"></xsl:variable>-->
<!--<xsl:function name="inubit:translate"><xsl:param name="element"/></xsl:function>-->
<!-- The value of the type attribute must be the same as the widget type defined in the widget.xml -->
<xsl:template match="isgrid:Widget[@type='ISGrid']">
<!-- Portlet namespace und unique name of the widget are used as identifier -->
<xsl:variable name="wid" select="concat($ns,@name)"/>
<!-- div for the grid widget; EXT JS will fill it -->
<div id="{$wid}">
<input type="hidden" id="{concat($wid, '_content')}" name="{concat(@name, '_content')}" value=""/>
</div>
<!-- JavaScript and XSLT code of the widget; for this grid widget EXT JS code is used -->
<script language="javascript" type="text/javascript">
Ext.require([
'Ext.data.*',
'Ext.grid.*',
'Ext.util.*',
'Ext.toolbar.Paging',
'Ext.tip.QuickTipManager'
]);
Ext.onReady(function() {
try {
<!-- Count of columns is needed multiple times -->
<xsl:variable name="columnCount" select="count(isgrid:Header/isgrid:Column)"/>
<!-- Create the Data Model -->
if(typeof(<xsl:value-of select="concat($ns,@id)"/>DataModel) == 'undefined'){
Ext.define('<xsl:value-of select="concat($ns,@id)"/>DataModel', {
extend: 'Ext.data.Model',
proxy: {
type: 'ajax',
reader: 'xml'
},
fields: [
<xsl:for-each select="isgrid:Header/isgrid:Column">
{name: '<xsl:value-of select="inubit:translate(.)"/>', mapping: 'Column:nth(<xsl:value-of
select="position()"/>)'}
<xsl:if test="position()<$columnCount">,</xsl:if>
</xsl:for-each>
<xsl:choose>
<xsl:when test="not(@itemId)">
,{name: 'id', mapping: '@id' }
</xsl:when>
<xsl:otherwise>
,{name: 'id', mapping: '@itemId' }
</xsl:otherwise>
</xsl:choose>
]
})
};
<!-- Create the Data Store -->
<xsl:value-of select="$wid"/>storeExtJsIS = Ext.create('Ext.data.Store', {
<!-- need to sort over all data on the server side -->
<xsl:if test="not(isgrid:Row)">remoteSort: true,</xsl:if>
<xsl:if test="not(isgrid:Row)">pageSize: <xsl:value-of select="isgrid:Header/@limit"/>,
</xsl:if>
model: '<xsl:value-of select="concat($ns,@id)"/>DataModel',
proxy: {
type: 'ajax',
<xsl:choose>
<xsl:when test="isgrid:Row">
<!-- load the init data, that is mapped directly in the form -->
url: '<xsl:value-of select="$initDataISGrid"/><xsl:value-of select="@name"/>',
</xsl:when>
<xsl:otherwise>
<!-- load the first data from jump; this is for paging purpose -->
url: '<xsl:value-of select="$jumpDataISGrid"/><xsl:value-of select="@name"/>',
</xsl:otherwise>
</xsl:choose>
reader: {
type: 'xml',
root: 'Widget',
// records will have an "Row" tag
record: 'Row',
<xsl:choose>
<xsl:when test="not(@itemId)">
idProperty: '@id',
</xsl:when>
<xsl:otherwise>
idProperty: '@itemId',
</xsl:otherwise>
</xsl:choose>
totalProperty: '@total'
},
simpleSortMode : true
}
});
<!-- If there are no rows, paging with a jump in the workflow is used to load data -->
<xsl:if test="not(isgrid:Row)">
<!-- paging bar -->
var <xsl:value-of select="$wid"/>pagingBarExtJsIS = Ext.create('Ext.toolbar.Paging',{
store: <xsl:value-of select="$wid"/>storeExtJsIS,
displayInfo: true,
emptyMsg: '-'
});
</xsl:if>
<!-- Create the Grid -->
if(Ext.Element.cache['<xsl:value-of select="$wid"/>']){
Ext.Element.cache['<xsl:value-of select="$wid"/>'].destroy(); }
<xsl:value-of select="$wid"/>gridExtJsIS = new Ext.grid.GridPanel({
store: <xsl:value-of select="$wid"/>storeExtJsIS
,itemId: '<xsl:value-of select="$wid"/>gridExtJsIS'
,columns: [
<xsl:for-each select="isgrid:Header/isgrid:Column">
{header: '<xsl:value-of select="inubit:translate(.)"/>'
<xsl:if test="@width">
,width:
<xsl:value-of select="@width"/>
</xsl:if>
,dataIndex: '<xsl:value-of select="inubit:translate(.)"/>'
,sortable: <xsl:value-of select="not(@sortable='false')"/>}
<xsl:if test="position()<$columnCount">,</xsl:if>
</xsl:for-each>
]
<xsl:if test="@enableRowDND='true'">,viewConfig: { plugins: { ptype: 'gridviewdragdrop' } }</xsl:if>
<xsl:if test="@rowSelectable='true'">,selModel: { selType: 'checkboxmodel'<xsl:if
test="@singleRowSelection='true'">, mode : "SINGLE"</xsl:if>}
,listeners: {select: function(selModel, record, index, options) {
var selectedrow = <xsl:value-of select="concat($wid, 'gridExtJsIS')"/>.getSelectionModel().getSelection();
var result='';
Ext.each(selectedrow, function (item) {
result=result + "<"+item.id+ ">on</"+item.id+">";
});
document.getElementById('<xsl:value-of select="concat($wid, '_content')"/>').value =result; }}
</xsl:if>
,renderTo: '<xsl:value-of select="$wid"/>',
inubitBpcWebAppNs: '<xsl:value-of select="$ns"/>'
,
<xsl:choose>
...
</xsl:choose>
</script>
</xsl:template>
</xsl:stylesheet>