SAPUI5 Fragment als Dialog öffnen

In SAPUI5 dient ein Fragement der Kapselung und Erhöhung der Wiederverwendbarkeit von Sourcecode. Die Fragment Klasse liegt im sap.ui.core Namespace. Daher muss dieser in der FragmentDefinition deklariert werden. Das Fragment wird in eine eigene Datei mit der Endung .fragment.xml gepackt.

<core:FragmentDefinition xmlns="sap.m" xmlns:core="sap.ui.core" xmlns:smartFilterBar="sap.ui.comp.smartfilterbar"
	xmlns:smartTable="sap.ui.comp.smarttable" xmlns:html="http://www.w3.org/1999/xhtml"
	xmlns:app="http://schemas.sap.com/sapui5/extension/sap.ui.core.CustomData/1">
	<Dialog id="employeeDialog" title="{i18n>employee.fragment.title}" contentWidth="100%" contentHeight="100%">
		<content>
			<smartTable:SmartTable id="LineItemsSmartTable" entitySet="Employee" smartFilterId="smartFilterBar" tableType="ResponsiveTable"
				useExportToExcel="false" useVariantManagement="false" useTablePersonalisation="true" showRowCount="true"
				persistencyKey="SmartTableAnalytical_Explored" enableAutoBinding="true" app:useSmartField="true" vclass="sapUiResponsiveContentPadding"></smartTable:SmartTable>
		</content>
		<endButton>
			<Button text="{i18n>employee.fragment.btnClose}" press="onCloseDialog"/>
		</endButton>
	</Dialog>
</core:FragmentDefinition>

Das Fragment kann im JavaScript, wie in folgendem Code Snippet dargestellt, im Dialog per lazy-loading geladen und anschließend geöffnet werden.

1. Variante

onOpenDialog: function(){
	var oView = this.getView();
	if (!this.byId("employeeDialog")) {
		sap.ui.core.Fragment.load({
			id: oView.getId(),
			name: "at.clouddna.demo.DemoApp.view.EmployeeFragment",
			controller: this
		}).then(function (oDialog) {
			jQuery.sap.syncStyleClass(oView.getController().getContentDensityClass(), oView, oDialog);
			oView.addDependent(oDialog);
			oDialog.open();
		}.bind(this));
	} else {
		this.byId("employeeDialog").open();
	}
}
onCloseDialog: function () {
	this.byId("employeeDialog").close();
	//this.byId("employeeDialog").destroy();
}

2. Variante

_getDialog: function () {
	// create dialog lazily
	if (!this._oDialog) {
		// create dialog via fragment factory
		this._oDialog = sap.ui.xmlfragment("dlgEmployee", "at.clouddna.demo.DemoApp.view.EmployeeFragment", this);
		// connect dialog to view (models, lifecycle)
		this.getView().addDependent(this._oDialog);
	}
	return this._oDialog;
},
onOpenDialog: function () {
	this._getDialog().open();
},
onCloseDialog: function () {
	this._getDialog().close();
}

Controller getriggertes Aggregation Binding

In SAPUI5 / Fiori Apps ist es immer wieder erforderlich dass Tabellen oder Listen initial leer angezeigt werden und erst nachdem beispielsweise eine Selektion durchgeführt wurde die Daten geladen werden. Folgendes Code Snippet zeigt wie sich diese Anforderung anhand einer sap.m.Table umsetzen lässt. In der View muss die items Aggregation auf „{}“ gesetzt werden. Damit werden keine Daten vom Server geladen. Der Sourcecode der View sieht wie folgt aus:

<Table id="employeeTable"  growing="true" growingThreshold="10" items="{}" mode="None">
	<columns>
		<Column id="colName">
			<Text text="{i18n>employeeTable.name.title}"/>
		</Column>
		<Column id="colEmail">
			<Text text="{i18n>employeeTable.email.title}"/>
		</Column>
		<Column id="colAction">
			<Text text="{i18n>employeeTable.action.title}"/>
		</Column>
	</columns>
	<items>
		<ColumnListItem>
			<cells>
				<Label text="{employee>Name}"/>
			</cells>
			<cells>
				<Label text="{employee>Email}"/>
			</cells>
			<cells>
				<Button icon="sap-icon://delete" press="onDeleteEmployee" />
			</cells>
		</ColumnListItem>
	</items>
</Table>

Im Controller laden wir im ersten Schritt den Inhalt der Template Property der items Aggregation. Danach können Filter und Sorter optional gesetzt werden. Es ist zwingend erforderlich die BindingInfo mit Pfad, Template, Filtern und Sortern zu setzen. Abschließend wird das AggregationBinding auf die items Aggregation durchgeführt. Danach werden die Daten in der Tabelle der BindingInfo entsprechend angezeigt. Folgendes Code Snippet zeigt den relevanten Ausschnitt aus der Controller Implementierung:

var sParameter = "SAP";
var _oTable = this.getView().byId("employeeTable");
var oTemplate = _oTable.getBindingInfo("items").template;
var oFilter = new sap.ui.model.Filter({
	path: "Department",
	operator: sap.ui.model.FilterOperator.EQ,
	value1: sParameter
});
var aFilters = [oFilter];
var oBindingInfo = {
	path: "employee>/Employee",
	template: oTemplate,
	filters: aFilters
};
_oTable.bindAggregation("items", oBindingInfo);

Pattern Matched Event

SAP bietet mit Fiori ein geniales Erweiterungskonzept. Dieses setzt jedoch voraus, dass der Entwickler bestimmte Vorgaben einhält. So ist es beispielsweise empfehlenswert keine Logik in die onInit Lifecycle-Methode zu legen. Stattdessen sollte man sich auf den PatternMatched-Event registrieren und im zugehörigen Event-Handler die Initialisierung durchführen. Folgendes Code Snippet zeigt die Umsetzung:

onInit: function () {
	//Register the event handler for the Pattern Matched Event
	this.getRouter().getRoute("Main").attachPatternMatched(this.onPatternMatched, this);
},
onPatternMatched: function (oEventArgs) {
	//Here goes the Source Code doing all the initialization work
},

Es können mehrere Routen auf die gleiche View verweisen und so könnte man auch für jede Route einen eigenen PatternMatched-Event-Handler registrieren.

Ein Beispiel dafür wäre, dass auf Detail-View einmal im Bearbeitungsmodus und einmal im Display-Only-Modus navigiert wird:

onInit: function () {
	this.getRouter().getRoute("DetailDisplay").attachPatternMatched(this.onPatternMatchedDisplay, this);
	this.getRouter().getRoute("DetailChange").attachPatternMatched(this.onPatternMatchedChange, this);
},
onPatternMatchedDisplay: function (oEventArgs) {
	this.bEdit = false;
	this.onPatternMatchedGeneral(oEventArgs);
},
onPatternMatchedChange: function (oEventArgs) {
	this.bEdit = true;
	this.onPatternMatchedGeneral(oEventArgs);
},
onPatternMatchedGeneral: function(oEvent){
	if(this.bEdit){
		...
	}else{
		...
	}
}