0
(Noch nicht bewertet)
26. Februar 2016

Movilizer HTML5-Screen: Eine Einführung

Der Movilizer-Client bietet mit seinen verschiedenen Screen-Typen und Controls eine Vielzahl von Möglichkeiten, plattformunabhängige Oberflächen für die überwiegende Mehrheit der Anwendungsfälle zu erstellen. Will man jedoch beispielsweise komplexe Layouts oder Dashboards darstellen, Inhalte dynamisch zur Laufzeit nachladen oder einen größeren Einfluss auf den Look des User Interfaces nehmen, stoßen diese Möglichkeiten schnell an ihre Grenzen.


An dieser Stelle bietet sich der HTML5-Screen an. Dieser erlaubt es, Anwendungen auf Basis von HTML5, CSS3 und JavaScript in vollem Umfang innerhalb des Movilizers auszuführen und unterstützt dabei auch Cordova-Funktionen sowie das Lesen und Schreiben globaler Movilizer-Variablen.

Dieser Blog-Beitrag gibt eine Einführung in die Entwicklung mit dem HTML5-Screen aus Frontend-Sicht anhand eines Beispielprojektes. Dabei wollen wir eine Variable aus dem Movilizer lesen, ihren Inhalt verändern und speichern und den neuen Wert per Datencontainer in die Movilizer-Cloud übertragen.

Projektstruktur

Bevor wir mit der eigentlichen Entwicklung beginnen, müssen wir eine Struktur für unsere HTML-Anwendung anlegen. Diese könnte etwa wie folgt aussehen:

  • Webapp/
  • Webapp/css
  • Webapp/js/application.js
  • Webapp/plugins
  • Webapp/cordova.js
  • Webapp/index.html

Die Bezeichnungen sind dabei größtenteils selbsterklärend: Ordner für die Styles und JavaScript-Dateien sowie eine Index-Datei als Einstiegspunkt für die Webanwendung. Wichtig ist darüber hinaus die Datei cordova.js. Sie wird zur Laufzeit vom Movilizer mit den “echten” Cordova- und Movilizer-spezifischen Funktionen ersetzt. Doch beginnen wir zunächst bei der Index-Datei:

<!DOCTYPE html>
<html lang="de">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1, minimum-scale=1, maximum-scale=1" />
<title>Webapp</title>
<link rel="stylesheet" type="text/css" href="css/bootstrap.min.css" />
<!-- Scripts -->
<script type="text/javascript" charset="utf-8" src="cordova.js"></script>
<script type="text/javascript" charset="utf-8" src="js/jquery.min.js"></script>
<script type="text/javascript" charset="utf-8" src="js/application.js"></script>
</head>
<body></body>
</html>

Der Einfachheit halber binden wir in dieses HTML-Dokument Bootstrap und jQuery ein. Darüber hinaus ist ein Verweis auf besagte Cordova-Datei enthalten. Wie bereits erwähnt wird sie zur Laufzeit ersetzt, somit können wir sie während der Entwicklung nutzen, um Beispieldaten zu erzeugen und Movilizer-Funktionen zu simulieren. Dazu erstellen wir im Ordner plugins zunächst eine leere JavaScript-Datei mit dem Namen Movilizer.js. Über etwas Code in der Datei cordova.js fügen wir diese Datei zur Webapp hinzu:

var headElement = document.getElementsByTagName('HEAD').item(0);
var scriptElement = document.createElement("script");
scriptElement.type = "text/javascript";
scriptElement.src = "plugins/Movilizer.js";
headElement.appendChild(scriptElement);

Da dieser Code nur existiert, solange wir die App nicht innerhalb des Movilizers nutzen, können wir nun die Datei Movilizer.js mit Testdaten und -funktionen füllen. Die Projektstruktur sollte mittlerweile also so aussehen:

  • Webapp
  • Webapp/css/bootstrap.min.css
  • Webapp/js/jquery.min.js
  • Webapp/plugins/Movilizer.js
  • Webapp/cordova.js
  • Webapp/index.html

Screen

Unsere Webapp soll ein einfaches Formular anzeigen. Dazu kopieren wir folgenden Code in den Body-Bereich der index.html:

<!-- Navigation -->
<nav class="navbar navbar-default navbar-fixed-top">
<div class="container-fluid">
<span class="navbar-brand">Webapp</span>
</div>
</nav>
<div class="container-fluid">
<h3>Product details</h3>
<hr/>
<div class="form-horizontal">
<div class="form-group">
<label class="col-xs-3">Name</label>
<div class="col-xs-9">
<input type="text" id="name" class="form-control">
</div>
</div>
<div class="form-group">
<label class="col-xs-3">Amount</label>
<div class="col-xs-9">
<input type="number" id="amount" class="form-control">
</div>
</div>
<div class="form-group">
<div class="col-xs-offset-3 col-xs-9">
<div class="checkbox">
<label><input type="checkbox" id="onSale"> On sale </label>
</div>
</div>
</div>
<div class="form-group">
<div class="col-xs-12">
<button id="save" class="btn btn-success btn-block">Save </button>
</div>
</div>
</div>
</div>

Die Werte Name, Amount und On Sale wollen wir aus dem Movilizer auslesen und Änderungen dort wieder speichern. Dazu benötigen wir die Funktionen Movilizer.readGlobalVariable und Movilizer.writeGlobalVariable. Damit wir die Webapp zunächst ohne den Movilizer in unserem Browser testen können, simulieren wir ihre Funktion durch folgende Implementierung in der Datei plugins/Movilizer.js:

var Movilizer = function(){};

Movilizer.prototype.readGlobalVariable = function(name, successCallback, errorCallback)
{
var result = JSON.parse(localStorage.getItem(name));
if (result)
successCallback(result);
else
errorCallback();
};

Movilizer.prototype.writeGlobalVariable = function(name, value)
{
localStorage.setItem(name, JSON.stringify(value));
};

var movilizer = new Movilizer();

In unserer Simulation nutzen wir statt des Movilizer-Variablenspeichers den LocalStorage des Browsers. Hier ist es wichtig zu wissen, dass die Lesefunktion asynchron arbeitet und bei entsprechenden Datenmengen auch mal eine Weile braucht. Daher liefert sie das Ergebnis bzw. eventuelle Fehler per Callback-Funktionen zurück.

Die Funktionen rufen wir dann in der application.js auf, um die Daten aus der globalen Movilizer-Variable productData zu lesen und bei Klick auf den Save-Button wieder zu speichern:

$(document).ready(function()
{
// Read movilizer.readGlobalVariable('productData', function(result)
{
$('#name').val(result.name);
$('#amount').val(result.amount);
$('#onSale').prop('checked', result.onSale);
},
function()
{
alert('Could not read global variable!');
});

// Enable click event
$('#save').click(storeFormData);
});

function storeFormData()
{
var result =
{
name: $('#name').val(),
amount: $('#amount').val(),
onSale: $('#onSale').prop('checked')
};

// Save
movilizer.writeGlobalVariable('productData', result);

// Leave screen
movilizer.triggerOKEvent();
}

Movilizer-Variablen werden bei Aufruf der Read- und Write-Funktionen in JavaScript-Objekte umgewandelt bzw. daraus erzeugt. Somit ist die Deklaration …

var example = { first: "First", second: "Second" };
example.third = "Third";

… das JavaScript-Äquivalent zu dieser MEL-Deklaration:

$global:example["first"] = "First";
$global:example["second"] = "Second";
$global:example["third"] = "Third";

An dieser Stelle ist zu beachten, dass Movilizer über JavaScript nur den Zugriff auf globale Variablen erlaubt.

Damit ist der Webapp-Teil der Entwicklung abgeschlossen und wir können uns das Ergebnis im Browser ansehen:

Movilizer HTML5 Screen Eine Einfuehrung 1

Movelet und Upload

Als nächstes müssen wir die Webapp in den Movilizer einbinden. Dies geschieht in zwei Schritten. Zuerst packen wir alle Dateien innerhalb des Webapp-Ordners in eine ZIP-Datei:

Movilizer HTML5 Screen Eine Einfuehrung 2

Die ZIP-Datei müssen wir dann in den Document Pool unserer Cloud hochladen. Der Upload kann über das SAP-System oder manuell über ein Upload-Tool erfolgen. Dabei werden verschiedene Daten benötigt:

  • ID und Passwort des Systems, auf das die Dateien übertragen werden sollen
  • Name des Dokumenten-Pools und Key für das Archiv
  • Suffix: Die Endung des Archives, in diesem Fall “zip”
  • Sprache und Acknowledge Key (können leer bleiben)

War der Upload erfolgreich, bestätigt das Tool den Vorgang mit einem schlichten “OK”. Wir können jetzt ein Movelet erzeugen, das die Webapp darstellt. Dafür muss dem Movelet der beim Upload angegebene Document Pool zugewiesen werden. Aus diesem lädt es dann, falls noch nicht geschehen, während des moveletStartAssignment über den Key die Webapp herunter, entpackt (“deployed”) sie lokal und zeigt sie in einem HTML5-Screen an. Wird der Screen verlassen, wird der neue Wert der Variable beim onLeaveOkPersistAssignment in einen Datencontainer geschrieben und versendet. In XML benötigen wir dafür diesen Code:

<MovilizerRequest systemId="${#Project#SystemID}" systemPassword="${#Project#Password}" requestTrackingKey="" useAutoAcknowledge="true" xmlns="http://movilitas.com/movilizer/v11">
<moveletDelete moveletKey="webappdemo" />
<moveletSet>
<movelet moveletKey="webappdemo" name="Webapp-Demo" moveletType="MULTI" initialQuestionKey="#1" privateNamespace="false" validTillDate="2099-01-01T15:00:00.0000" >

<!-- Question type 32 is HTML5 screen -->
<question type="32" key="#1" title="Webapp-Demo">

<answer key="#1_1" nextQuestionKey="#2">
<!-- Define the page that should be displayed in the screen: -->
<text>webapp://Webapp/index.html</text>
</answer>

<onEnterAssignment>
<!-- Create sample productData variable if it does not exist yet -->
if(isBlank($global:productData))
{
$global:productData["name"] = "No name";
$global:productData["amount"] = 0;
$global:productData["onSale"] = false;
}
</onEnterAssignment>
</question>

<question type="41" title="Epsilon" key="#2">
<answer key="#2_1" nextQuestionKey="END" action="UPLOADONLY"></answer>

<onLeaveOkPersistAssignment>
<!-- Load global productData variable (written in JavaScript) and write it to the data container -->
timestamp = systemtime();
writeContainer(concat("product-", timestamp), $global:productData, 0);
</onLeaveOkPersistAssignment>
</question>

<moveletStartAssignment>
<!-- Check if needs to be deployed (true by default) -->
deploy = default($local:deploy, true);

if (deploy)
{
showProgress('Deploying Webapp ...');

<!-- Deploy webapp from Document Pool "WebappDemo" with key "Webapp" locally -->
zip = getDocument($document:"WebappDemo", "Webapp");
deployWebapp(zip, "Webapp");

$local:deploy = false;
}
</moveletStartAssignment>

<!-- Assign Document Pool to movelet -->
<document documentPool="WebappDemo" />

</movelet>
<participant participantKey="usr" name="usr" deviceAddress="+99123456789"/>
</moveletSet>
</MovilizerRequest>

Diesen Request können wir über Movilizer Eclipse an die Movilizer-Cloud senden. Dort wird das Movelet generiert und an das Gerät (hier mit der Beispiel-Nummer +99123456789) übertragen. Dort können wir das Movelet dann öffnen und sehen, wenn alles funktioniert hat, die erstellte Seite, können die Variablen ändern und speichern.

Movilizer HTML5 Screen Eine Einfuehrung 3

Verlassen wir das Movelet, sollten die eingegebenen Daten per Datencontainer in die Cloud versendet werden und im Portal sichtbar sein:

Movilizer HTML5 Screen Eine Einfuehrung 4

Tipps

  • Da der Movilizer-Container standardmäßig keine Tools zum Debuggen und Untersuchen von JavaScript und HTML bietet, kann die Fehlersuche bei komplexeren Anwendungen sehr schwierig werden. Hier helfen Tools wie z.B. Weinre oder Firebug Lite.
  • Um das clientseitige Entwickeln zu beschleunigen, kann man die Webapp direkt im Movilizer-Container bearbeiten. Dazu muss man sich mit einem Participant, dem das Movelet zugewiesen ist, in einem Movilizer Swing Client anmelden. Nach erfolgreichem Deployment befindet sich die Webapp im Unterordner “webapps” im gleichen Ordner, in dem auch der Client liegt. Änderungen daran werden nach neuem Öffnen des Movelets direkt sichtbar. Allerdings ist hier Vorsicht geboten, da der Movilizer bei Synchronisation alle Änderungen wieder überschreibt!

Ich freue mich auf Ihr Feedback!


Movilizer HTML5 Screen: An Introduction

The Movilizer Clients offers a vast amount of different screen types and widgets, enabling developers to create platform independent user interfaces for the majority of cases. However, the possibilities are limited if it comes to more complex layouts, dashboards, displaying data changes in real time or completely custom UI designs. This is where the HTML5 comes in: It allows to run fully-featured applications based on HTML5, CSS3 and JavaScript from inside the Movilizer App, access global Movilizer variables and use Cordova functions.

Today’s blog post gives you an introduction to the HTML5 screen from a frontend development point of view on the example of a small application that reads, changes and stores the content of a Movilizer variable and sends it to the Movilizer Cloud using Data Containers.

Projectstructure

The first step is to create a file/folder structure for the HTML application:

  • Webapp
  • Webapp/css
  • Webapp/js/application.js
  • Webapp/plugins
  • Webapp/cordova.js
  • Webapp/index.html

Most of the names are self-explanatory: Folders containing stylesheets and JavaScript sources as well as an index file that is displayed by the Movilizer. Apart from that, we need a file called cordova.js. It will be replaced by the Movilizer at runtime with “real” Cordova and Movilizer-specific functions. But for now, let’s just start with the index file:

<!DOCTYPE html>
<html lang="de">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1, minimum-scale=1, maximum-scale=1" />
<title>Webapp</title>
<link rel="stylesheet" type="text/css" href="css/bootstrap.min.css" />
<!-- Scripts -->
<script type="text/javascript" charset="utf-8" src="cordova.js"></script>
<script type="text/javascript" charset="utf-8" src="js/jquery.min.js"></script>
<script type="text/javascript" charset="utf-8" src="js/application.js"></script>
</head>
<body></body>
</html>

For the sake of simplicity, we link Bootstrap and jQuery. Furthermore, the file contains a reference to the said Cordova file. Since it will be replaced once the document is displayed in the Movilizer, we can use it to store dummy data and simulate Movilizer functions during development. To do so, we need to create an empty JavaScript file called Movilizer.js in the plugins folder. By inserting a few lines of code into the cordova,js file, we tell our webapp to load the file:

var headElement = document.getElementsByTagName('HEAD').item(0);
var scriptElement = document.createElement("script");
scriptElement.type = "text/javascript";
scriptElement.src = "plugins/Movilizer.js";
headElement.appendChild(scriptElement);

Since this code only exists if we don’t run the application from inside the Movilizer App, we can now insert test data and functions into Movilizer.js. By now, the file/folder structure should look like this:

  • Webapp
  • Webapp/css/bootstrap.min.css
  • Webapp/js/jquery.min.js
  • Webapp/plugins/Movilizer.js
  • Webapp/cordova.js
  • Webapp/index.html

Screen

We want our webapp to display a simple form, so we put the following code in the body section of the index.html file:

<!-- Navigation -->
<nav class="navbar navbar-default navbar-fixed-top">
<div class="container-fluid">
<span class="navbar-brand">Webapp</span>
</div>
</nav>
<div class="container-fluid">
<h3>Product details</h3>
<hr/>
<div class="form-horizontal">
<div class="form-group">
<label class="col-xs-3">Name</label>
<div class="col-xs-9">
<input type="text" id="name" class="form-control">
</div>
</div>
<div class="form-group">
<label class="col-xs-3">Amount</label>
<div class="col-xs-9">
<input type="number" id="amount" class="form-control">
</div>
</div>
<div class="form-group">
<div class="col-xs-offset-3 col-xs-9">
<div class="checkbox">
<label><input type="checkbox" id="onSale"> On sale </label>
</div>
</div>
</div>
<div class="form-group">
<div class="col-xs-12">
<button id="save" class="btn btn-success btn-block">Save </button>
</div>
</div>
</div>
</div>

The name, amount and on sale values should be read from and saved to the Movilizer. This requires two functions: Movilizer.readGlobalVariable and Movilizer.writeGlobalVariable. In order to test the application in a web browser, we simulate the functions by inserting another bit of code into plugins/Movilizer.js:

var Movilizer = function(){};

Movilizer.prototype.readGlobalVariable = function(name, successCallback, errorCallback)
{
var result = JSON.parse(localStorage.getItem(name));
if (result)
successCallback(result);
else
errorCallback();
};

Movilizer.prototype.writeGlobalVariable = function(name, value)
{
localStorage.setItem(name, JSON.stringify(value));
};

var movilizer = new Movilizer();

In this implementation, we use our browser’s local storage instead of the Movilizer. At this point, it is important to know that the function that reads the variables works asynchronously. For large amounts of data this process might take some time, so the result or possible errors will be returned by callback functions.

We call the new functions from applications.js to read data from the global Movilizer variable product Data, save them and leave the screen if the user clicks on the save button:

$(document).ready(function()
{
// Read movilizer.readGlobalVariable('productData', function(result)
{
$('#name').val(result.name);
$('#amount').val(result.amount);
$('#onSale').prop('checked', result.onSale);
},
function()
{
alert('Could not read global variable!');
});

// Enable click event
$('#save').click(storeFormData);
});

function storeFormData()
{
var result =
{
name: $('#name').val(),
amount: $('#amount').val(),
onSale: $('#onSale').prop('checked')
};

// Save
movilizer.writeGlobalVariable('productData', result);

// Leave screen
movilizer.triggerOKEvent();
}

Movilizer variables will be converted to JavaScript objects and vice versa. Thus, the JavaScript declaration …

var example = { first: "First", second: "Second" };
example.third = "Third";

… is equivalent to the following MEL declaration:

$global:example["first"] = "First";
$global:example["second"] = "Second";
$global:example["third"] = "Third";

Please note that it is only possible to access global MEL variables from JavaScript.

Now our application is ready to be tested in the browser:

Movilizer HTML5 Screen Eine Einfuehrung 5

Movelet and Upload

Next, we need to display the webapp in the Movilizer Container. This can be done in two steps: First of all, we create a ZIP archive containing every file inside the webapp folder:

Movilizer HTML5 Screen Eine Einfuehrung 6

The ZIP file has to be uploaded to the Document Pool of the Movilizer Cloud we’re using. Uploading can be done using the SAP system or manually with the help of an uploading tool. To do so, we need some data:

  • ID and password of the system on which the webapp should be stored
  • Name of the Document Pool and a Key for the archieve
  • Suffix: File ending, “zip” in this case
  • Language and Acknowledge Key (optional)

If uploading was successful, the tool confirms the process with a plain “OK” message. After that, we can create a Movelet that displays the webapp. Therefore, the Movelet needs to know the Document Pool from which it downloads the webapp during the moveletStartAssignment, if not done already. It unzips (“deploys”) the application locally and renders it in the HTML screen. If the user leaves the screen the new value will be written to a Data Container and sent to the Movilizer Cloud during the onLeaveOkPersistAssignment. This can be achieved by putting the following code in the Movelet XML file:

<MovilizerRequest systemId="${#Project#SystemID}" systemPassword="${#Project#Password}" requestTrackingKey="" useAutoAcknowledge="true" xmlns="http://movilitas.com/movilizer/v11">
<moveletDelete moveletKey="webappdemo" />
<moveletSet>
<movelet moveletKey="webappdemo" name="Webapp-Demo" moveletType="MULTI" initialQuestionKey="#1" privateNamespace="false" validTillDate="2099-01-01T15:00:00.0000" >

<!-- Question type 32 is HTML5 screen -->
<question type="32" key="#1" title="Webapp-Demo">

<answer key="#1_1" nextQuestionKey="#2">
<!-- Define the page that should be displayed in the screen: -->
<text>webapp://Webapp/index.html</text>
</answer>

<onEnterAssignment>
<!-- Create sample productData variable if it does not exist yet -->
if(isBlank($global:productData))
{
$global:productData["name"] = "No name";
$global:productData["amount"] = 0;
$global:productData["onSale"] = false;
}
</onEnterAssignment>
</question>

<question type="41" title="Epsilon" key="#2">
<answer key="#2_1" nextQuestionKey="END" action="UPLOADONLY"></answer>

<onLeaveOkPersistAssignment>
<!-- Load global productData variable (written in JavaScript) and write it to the data container -->
timestamp = systemtime();
writeContainer(concat("product-", timestamp), $global:productData, 0);
</onLeaveOkPersistAssignment>
</question>

<moveletStartAssignment>
<!-- Check if needs to be deployed (true by default) -->
deploy = default($local:deploy, true);

if (deploy)
{
showProgress('Deploying Webapp ...');

<!-- Deploy webapp from Document Pool "WebappDemo" with key "Webapp" locally -->
zip = getDocument($document:"WebappDemo", "Webapp");
deployWebapp(zip, "Webapp");

$local:deploy = false;
}
</moveletStartAssignment>

<!-- Assign Document Pool to movelet -->
<document documentPool="WebappDemo" />

</movelet>
<participant participantKey="usr" name="usr" deviceAddress="+99123456789"/>
</moveletSet>
</MovilizerRequest>

The request can be sent to the Movilizer Cloud using the Movilizer Eclipse software. If the Movelet was successfully created, it will be transferred to the device (+99123456789 in this case). If everything went well, we can open the application and change and save the variable on the device:

Movilizer HTML5 Screen Eine Einfuehrung 7

After we closed the Movelet, we should be able to see the Data Container on the Movilizer Cloud:

Movilizer HTML5 Screen Eine Einfuehrung 8

Hints

  • The Movilizer Container does not offer any tools to debug or inspect JavaScript and HTML by default, but we can use tools like Weinre or Firebug Lite instead.
  • To speed up client-side development it is possible to edit the webapp directly in the Movilizer Container if you run the Movelet on a Movilizer Swing Client. The webapp will be deployed to the “webapps” folder in the same folder in which the client executable is. Any changes will be visible after closing and re-opening the movelet, but any changes will be overwritten by the Movilizing during synchronization!

I am looking forward to your feedback!

Schreiben Sie einen Kommentar

Ihre E-Mail-Adresse wird nicht veröffentlicht. Erforderliche Felder sind mit * markiert.