jQuery Mobile

Introductie

jQuery Mobile is een rich client widget library specifiek voor mobiele apps en is gebaseerd op de jQuery library.

Er is een zeer goede tutorial op internet:

http://the-jquerymobile-tutorial.org/index.php

Het principe is erg simpel: bestaande html elementen worden uitgebreid met nieuwe jQuery Mobile specifieke attributen (data-role) waarmee het element een rich representatie krijgt. Bijv. een list element krijgt het attribuut “data-role” met de waarde “listview” en vervolgens krijgt het een typisch rich mobile representatie. Zie voorbeeld:

<ul data-role="listview" data-inset="true" data-filter="true">
	<li><a href="#">Acura</a></li>
	<li><a href="#">Audi</a></li>
	<li><a href="#">BMW</a></li>
	<li><a href="#">Cadillac</a></li>
	<li><a href="#">Ferrari</a></li>
</ul>

jQuery Mobile biedt een groot scala aan typische mobile gestijlde widgets:

  • Toolbars
  • Buttons
  • Form elementen
  • Lists
  • etc

Wat er echter niet echt wordt geboden zijn :

  • Table Grids: maar dat is eigenlijk een concept wat niet geschikt is voor mobiles.
  • Date pickers: hiervoor wordt een aparte plugin gebruikt. Bijv. van jQuery UI “Datepicker”.

Een mooie is DateBox (http://dev.jtsage.com/jQM-DateBox/)

De opzet van de mobiele pagina’s waarin de jQuery Mobile elementen worden opgenomen is volgens onderstaande page template. Daarin is te zien hoe het data-role attribuut wordt toegepast op <div> html elementen om hiermee een page te maken met daarbinnen div containers voor header en content.

<!DOCTYPE html>
<html>
<head>

	<title>My Page</title>
	<meta name="viewport" content="width=device-width, initial-scale=1">
	<link rel="stylesheet" href="jquery.mobile-1.2.0.min.css" />
	<script src="jquery-1.8.2.min.js"></script>
	<script src=" jquery.mobile-1.2.0.min.js"></script>
</head>
<body>

<div data-role="page">

	<div data-role="header">
		<h1>My Title</h1>
	</div><!-- /header -->

	<div data-role="content">
		<p>Hello world</p>
	</div><!-- /content -->

</div><!-- /page -->

</body>
</html>

Behalve dit “One Page” template is het ook mogelijk om meerdere pages in één html bestand op te nemen.

jQuery Mobile en Cross Domain Requests

Er ontstaan problemen als JavaScript vanuit een browser een url wil aanroepen die niet in het eigen domein ligt. Dit probleem heeft een beveiligingsoorzaak. (Attacks kunnen plaatsvinden door de url aan te passen). Het probleem kan, bij gebruik van jQuery, worden opgelost met jQuery instelling van de $.support.cors boolean. Deze geeft aan of jQuery denkt dat de browser de W3C “Cross-Origin Resource Sharing” feature ondersteunt voor cross-domain requests. Bij gebruik van jQuery $.ajax() moet de $.support.cors boolean op true worden gezet.

Als jQuery Mobile een externe pagina will laden zal request via de $.mobile.loadPage() functie verlopen. Deze zal elleen cross-domain requests toestaan als de $.mobile.allowCrossDomainPages optie op true is ingesteld.

jQuery Mobile Ajax Page Management

jQuery Mobile heeft een nogal bijzondere manier om met Pages om te gaan. Er worden bij pagina overgangen niet standaard nieuwe pages geladen, maar deze worden middels een Ajax request geinjecteerd in het DOM van de aanvrager. Dat heeft nogal wat consequenties:

  • Er kan niet echt (eenvoudig) gebruik worden gemaakt van request parameter overdracht. Er moet naar alternatieven worden gezocht. Bijv. via globale variabelen.
  • De header info van andere pages wordt niet geladen (behalve title), dus ook de scripts niet. Scripts moeten in de body worden opgenomen, of in .js bestanden.
  • Debuggen kan lastiger worden, omdat bijv. Firebug de code van geladen pages niet ziet
  • Er kunnen naamcoflicten optreden tussen elementen van verschillende pagina’s.
  • De standaard jQuery Document events zoals DOMContentLoaded werken niet meer. Er moeten andere , jQuery Mobile specifieke events worden gebruikt zoals pageinit.

Eigenlijk ontstaat er één grote index.html file waarin alle gelinkte pages worden opgenomen.

In de volgende paragrafen wordt jQuery Mobile besproken aan de hand van voorbeeld pages voor een department met een one-to-many relatie naar employees.
De verschillende html pages en javascript bestanden zijn:

  • indexJQM.html : de Home Page
  • main.js : globale variabelen met server url definitie
  • departmentsJQM.html : overzichtslijst met departments
  • departmentEditJQM.html : Show/Create/Update/Delete pagina voor een department
  • departments.js : List/Create/Update/Delete functies voor een department
  • employeesJQM.html: overzichtslijst met employees
  • employeeEditJQM.html: Show/Create/Update/Delete pagina voor een employee
  • employees.js : List/Create/Update/Delete functies voor een employee

De Home Page HEAD

Hieronder een voorbeeld van de head van een indexJQM.html pagina.

<!DOCTYPE html>
<html>
<head>
	<meta charset="utf-8"/>
	<title>Mobile Demo</title>
	<meta name="viewport" content="width=device-width, initial-scale=1"/>
	<link rel="stylesheet" href="libs/jquery.mobile-1.2.0.min.css" />
	<link rel="stylesheet" type="text/css" href="libs/jquery.mobile.datebox.min.css" />
	<script src="libs/cordova-2.1.0.js" type="text/javascript"></script>
	<script src="libs/jquery-1.8.2.min.js"></script>
	<script src="libs/jquery.mobile-1.2.0.min.js"></script>
	<script type="text/javascript" src="libs/date.js"></script>
	<script type="text/javascript" src="libs/jquery.mobile.datebox.min.js"></script>
	<script type="text/javascript"
src="http://dev.jtsage.com/cdn/datebox/i18n/jquery.mobile.datebox.i18n.en_US.utf8.js"></script>
	<script src="main.js"></script>
	<script src="departments/departments.js"></script>
	<script src="employees/employees.js"></script>
	<script type="text/javascript">
		document.addEventListener("DOMContentLoaded", onDocumentReady, true);
		function onDocumentReady(){
			jQuery.mobile.allowCrossDomainPages = true;
			jQuery.support.cors = true;
			$("#linkemployees").on("vclick", function (event) {
				selectedDepartmentId = null;
				return false;
			});
		}
		$( document ).on("mobileinit", function(){
			//$.mobile.allowCrossDomainPages = true;
		});
  	</script>
  	<style>
  	.error{background:red;}
  	</style>
</head>

Er zijn hier de CSS links en Script includes te zien voor de volgende onderdelen:

  • Cordova (PhoenGap)
  • jQuery
  • jQuery Mobile
  • DateBox (Date Picker plugin voor jQuery Mobile)
  • date.js: datum formaat conversie scripts

Naast de generieke onderdelen wordt hier ook de javascript opgenomen voor de functionele applicatie onderdelen “departments” en “employees”. Deze moeten hier worden opgenomen omdat deze in de head van de departments en employee pages zelf niet worden opgenomen.

We zien verder

  • de initiele start EventListener.
  • In de initiele EventListener :
    • globale jQuery instellingen om cross-domain requests mogelijk te maken
    • Een eventlistener op event vclick voor de link naar de employees page. Hierin wordt een globale variabele geinitialiseerd. Deze dient om de employees page te kunnen starten vanuit de home page en vanuit departments page voor een specifieke department.
  • Een globaal gebruikt css class definitie, t.b.v. rood aangeven van fouten op pagina’s.

De Home Page BODY

Hieronder is het body deel van de voorbeeld Home Page.
Deze is opgezet conform de basis jQuery Mobile structuur zoals eerder toegelicht.
Eigenlijk zijn hier in de content container alleen buttons te zien om de departments of employees pages te kunnen selecteren.

<body  data-theme="a">

<div data-role="page" id="pgIndex">
	<div data-role="header">
		<h1>Mobile Demo</h1>
	</div><!-- /header -->

	<div data-role="content">
		<a href="departments/departmentsJQM.html" data-role="button"
                   data-iconpos="right" data-icon="arrow-r">Departments</a>
		<a href="employees/employeesJQM.html" data-role="button"
                   data-iconpos="right" data-icon="arrow-r" id="linkemployees">Employees</a>
	</div><!-- /content -->

	<div data-role="footer">
		<h1>VDS<sup>©</sup></h1>
	</div><!-- /footer -->

</div><!-- /page -->

</body>
</html>

In het body element wordt met data-theme=”a” de basis style ingesteld. Met letters a,b,c,… wordt een bepaalde stijl geselecteerd.
Een link element wordt een button door attribuut data-role=”button”.
Met data-icon=”arrow-r” wordt een icoon aan de button toegevoegd, in dit geval een pijltje naar rechts. Met data-iconpos=”right” wordt de positie van het icon in de button aangegeven.

Globale server url definities (main.js)

In het main.js bestand worden globale definities van de server url opgenomen. Hierdoor hoeft bij aanpassing van de server url dit maar op één plek te worden gewijzigd. Als serverhost wordt het ip-adres 10.0.2.2 gebruikt. Dit is een speciale alias voor de localhost die in het Android virtual device (AVD) niet gebruikt kan worden.
localhost wordt door de AVD gezien als de host van het device en niet van een server waar mee wordt verboden.

NB: Als de 10.0.2.2 problemen geeft dan kan het helpen om een nieuwe emulator met de AVD te maken. Als je start met een ander ip-adres, bijv. het echte ip-adres, dan werkt het vaak niet als het wordt aangepast naar 10.0.2.2.

var serverhost = "10.0.2.2";
var serverport = "8082";
var serverproject = "employeesREST";
var serverurl = "http://" + serverhost + ":" + serverport + "/" + serverproject;

Opzet functionele pagina’s

Departments en Employees krijgen elk een eigen sub-directory.
Voor departments vinden hierin de volgende 3 bestanden:

  • departmentsJQM.html : overzichtslijst met departments
  • departmentEditJQM.html : Show/Create/Update/Delete pagina voor een department
  • departments.js : List/Create/Update/Delete functies voor een department

Hieronder een voorbeeld van departmentsJQM.html (de Head inhoud wordt niet getoond, want is, behalve de title toch niet van belang).

departmentsJQM.html

<!DOCTYPE html>
<html>
<head> …. </head>
<body  data-theme="a">
<div id="pgDepartmentsJQM" data-role="page">
	<div data-role="header" >
  		<a href="../indexJQM.html" data-icon="home" class="ui-btn-left">Home</a>
 		<h1>Departments</h1>
  		<a href="#" data-icon="add" id="add">Add</a>
	</div><!-- /header -->

	<div data-role="content">
            <ul id="dplist" data-role="listview" data-filter="true">
            </ul>
	</div><!-- /content -->

	<div data-role="footer">
		<h1>VDS<sup>©</sup></h1>
	</div><!-- /footer -->

	<script>
	$("#pgDepartmentsJQM").on("pageinit", getDepartmentListJQM);
	$("#add").on("vclick", function (event) {
		selectedDepartmentId = null;
        		$.mobile.changePage("departmentEditJQM.html");
        		return false;
	});
	</script>
</div><!-- /page -->
</body>
</html>

In de div data-role=”header” zien we hoe de buttons en titel boven aan de pagina worden gedefinieerd. Met css class definities wordt de positie van de button ingesteld: class=”ui-btn-left”.

In de content div zien we de definitie van een lijst. Deze wordt gevuld vanuit de functie getDepartmentListJQM (zit in departments.js). Deze functie wordt met “on()” gebonden aan het event “pageinit” van de pagina. Deze zorgt er dus voor dat de data voor de pagina eerst wordt geladen.

Verder zien in het script deel aan de “add” button een click event gebonden. Deze zorgt er voor dat de global variabele selectedDepartmentId (zit in departments.js) op null wordt gezet en dat er een nieuwe page departmentEditJQM.html wordt geladen. Deze page is multifunctioneel en wordt gebruikt voor de create/update/delete functionaliteit. Als de global null is weet deze page dat er sprake is van een create actie.

departmentsEditJQM.html
Hieronder de eerste delen van deze pagina: de header, footer en content.

<!DOCTYPE html>
<html>
<head> … </head>

<body  data-theme="a">

<div data-role="page" id="pgDepartmentEdit" data-add-back-btn="true">
	<div data-role="header">
  		<a id="cancel" href="../indexJQM.html" data-icon="home"
                   class="ui-btn-left">Home</a>
  		<a id="canceledit" href="#" data-icon="back"
                   class="ui-btn-left">Cancel</a>
		<h1>Department Details</h1>
		<a id="edit" href="" data-theme="b" data-icon="check"
                   class="ui-btn-right">Edit</a>
	</div><!-- /header -->

	<div data-role="content">
	<form id="fmDepartmentEdit" method="post">
		<label for="name">Naam:</label>
		<input type="text" name="name" id="name" data-mini="true"
                       disabled="disabled" style="opacity:1;"/>
		<label for="address">Adres:</label>
		<input type="text" name="address" id="address" data-mini="true"
                       disabled="disabled" style="opacity:1;"/>
		<label for="budget">Budget:</label>
		<input type="text" name="budget" id="budget" data-mini="true"
                       disabled="disabled" style="opacity:1;"/>
		<br/>
		<a id="linkdetails" href="../employees/employeesJQM.html"
                   data-role="button" data-icon="arrow-r"
                   data-iconpos="right" data-theme="b">Employees</a>
	</form>
	 <span id="msg"></span>
	</div><!-- /content -->

	<div data-role="footer">
		<a id="delete" data-icon="delete" data-transition="slideup"
                   data-rel="dialog" class="ui-btn-left">Delete</a>
		<H1>VDS<sup>©</sup></H1>
		<a id="submit" href="" data-theme="b" data-icon="check"
                   class="ui-btn-right">Save</a>
	</div><!-- /footer -->

In de header worden weer de buttons gedefineerd. Vervolgens zien we in de content het form. Initieel zijn hierin de input elementen gedisabled. Het form kent verschillende modi: display, edit en add mode. In display mode wordt de inhoud van een department getoond, en een link naar de employees page voor de department. Als op de edit button wordt gedrukt wordt de page aangepast naar edit mode. De link naar employees verdwijnt, de input elementen worden geënabled, en er worden save en delete buttons zichtbaar gemaakt.

In de content zit ook een button link naar de employeesJQM.html waarmee de Master->Detail link tussen departments en employees wordt gerealiseerd.

Om in de display mode de input elementen beter zichtbaar te maken wordt de css property opacity ingesteld. Input elementen worden bij disabled state standaard wazig gemaakt met de opacity property. Het gedrag om de modi aan te passen zit in de volgende script onderdelen op de volgende pagina. In de footer zien we de delete en save button. Deze worden alleen getoond in edit mode.

Hieronder de eerste scripting delen van de pagina.

$('#pgDepartmentEdit').on('pageinit', getDepartmentJQM);

$("#submit").on("click", function(event) {
       if ($("#name").val() != "" &&  $("#address").val() != "") {
          saveDepartment();
          $.mobile.changePage("departmentsJQM.html");
          return false;
       } else {
       	  if (!$("#name").val()) {$("#name").addClass("error");}
          else {$("#name").removeClass("error");};

          if (!$("#address").val()) {$("#address").addClass("error");}
          else {$("#address").removeClass("error");};

          alert("Input verplicht!!!");
          $("#msg").text("Not valid!").show().fadeOut(5000);
          return false;
       }
});

$("#delete").on("click", function(event) {
        deleteDepartment();
        $.mobile.changePage("departmentsJQM.html");
        return false;
});

Als eerste wordt getDepartmentJQM aangeroepen om de data van de geslecteerde department te laden.

Als tweede de code welke wordt uitgevoerd als de submit button van het form wordt geklikt. In dat geval wordt de saveDepartment() functie aangeroepen en wordt vervolgens teruggegaan naar de departments overzicht page. Er zit in dit stukje code een check op de ingevoerde velden. Er wordt gecontroleerd of deze niet leeg zijn. Als ze niet voldoen dan wordt er foutmeldingen getoond. Allereerst worden met de jQuery addClass/removeClass calls de velden rood gekleurd en er wordt een message getoond die na 5 sec. verdwijnt.

Als derde de code welke wordt uitgevoerd als de delete button van het form wordt geklikt. In dat geval wordt de deleteDepartment() functie aangeroepen en wordt vervolgens teruggegaan naar de departments overzicht page.

NB: De return false is hier van belang om te voorkomen dat de pagina weer terug flowt.

Hieronder de laatste scripting delen van de pagina.
Dit is de code die er voor zorgt dat in de verschillende scherm modi de juiste knopjes worden getoond of verborgen en input elementen geënabled/gedisabled.

   // Zet initieel op display mode
    $('#pgDepartmentEdit').on('pageinit', function() {
	$("#canceledit").hide();
	$("#submit").hide();
	$("#delete").hide();
    });

    // Zet op edit mode na click edit button
    $("#edit").on("click", function(event) {
    	$('input').textinput('enable');
    	$("#canceledit").show();
    	$("#submit").show();
		$("#delete").show();
    	$("#edit").hide();
    	$("#cancel").hide();
    	$("#linkdetails").hide();
        return true;
    });
    // Zet weer op display mode na click cancel button in edit mode
    $("#canceledit").on("click", function(event) {
    	$('input').textinput('disable');
    	$("#canceledit").hide();
    	$("#submit").hide();
	$("#delete").hide();
    	$("#edit").show();
    	$("#cancel").show();
    	$("#linkdetails").show();
        return true;
    });
    // Zet op add new record mode, als selectedDepartmentId=null
    $('#pgDepartmentEdit').on('pageinit', function() {
	    if (!selectedDepartmentId) 	{
		$("#edit").trigger('click'); //trigger edit button
		$("#delete").hide();        //hide niet relevante buttons
	    	$("#canceledit").hide();
	    	$("#linkdetails").hide();
	    	$("#cancel").show();		 //show gewone cancel button
	    }
    });
  

departments.js

Hieronder de List/Create/Update/Delete functies voor een department zoals opgenomen in de script file departments.js. Bovenin zien we de globale variabele selectedDepartmentId. Hierin wordt opgeslagen welke department is gekozen in de list in departmentsJQM.html.

var selectedDepartmentId;

/* CRUD Functies voor Department */

Onderstaande functie maakt de list elementen voor de page.
Er wordt een jQuery ajax call uitgevoerd naar een REST webservice.
Het succes deel maakt de list elementen o.b.v. de ontvangen data.
Onderdeel in de list is natuurlijk de url link naar de CRUD pagina departmentEditJQM.html. Hier wordt de Id van het department versleuteld in het id attribuut van het <li> element. Er wordt aan elk <li> element ook een vclick event gekoppeld om de door een gebruiker aangeklikte en dus geselecteerde department op te slaan in de globale variabele selectedDepartmentId.

Belangrijk is het initieel leeg maken van het list element en na het vullen uitvoeren van de listview(“refresh”) call om jQuery Mobile de gelegenheid te geven om de verrijkte mobile opmaak aan de list elementen te geven.

function getDepartmentListJQM() {
    $.ajax(
        {
            type: "POST",
            url: serverurl + "/dep/list",
            dataType: "json",
            success: function (data) {
            	$("#dplist").empty();
                $.each(data.rows, function (i, item) {
                    $("#dplist").append('<li id="' +
                      item.departmentId +'">
                      <a href="departmentEditJQM.html?departmentId=' + item.departmentId +
                      '">' + item.name + '</a></li>');
                });
                $("#dplist").listview("refresh");
        	   $("li").on("vclick", function (event) {selectedDepartmentId =
                                                           $(this).attr("id");});
            },
            error: function (jqXHR, textStatus, errorThrown) {
            }
        });
}

Onderstaande functie haalt het geselecteerde department op voor de CRUD page departmentEdit.html. Er wordt een jQuery ajax call uitgevoerd naar een REST webservice. Met de ontvangen gegevens worden de input elementen gevuld.

function getDepartmentJQM() {
	if (selectedDepartmentId != null) {
	    $.ajax(
          {
            type: "POST",
            url: serverurl + "/dep/show/" + selectedDepartmentId,
            dataType: "json",
            success: function (data) {
            	console.log('data received : ' + data);
            	$("#name").val(data.rows.name);
            	$("#address").val(data.rows.address);
            	$("#budget").val(data.rows.budget);
             },
            error: function (jqXHR, textStatus, errorThrown) {
            }
	    });
	} else {
		console.log("departmentId niet gevonden, wellicht add");
	}
}

Onderstaande functie slaat de in de de CRUD page departmentEdit.html ingevoerde of aangepaste gegevens van de geselecteerde department op.
Afhakelijk van de al dan niet vulling van de globale variabele selectedDepartmentId wordt met de url een Create of Update call voorbereid.
In deze url worden de input elementen inhoud opgenomen als request parameters.
Met deze url wordt een jQuery ajax call uitgevoerd naar een REST webservice.

function saveDepartment() {
    var  urlrequest;
    if (selectedDepartmentId) {
    	urlrequest= serverurl + "/dep/update/" + selectedDepartmentId;
	} else {
    	urlrequest= serverurl + "/dep/create";
	}
	urlrequest = urlrequest +
	"?name=" + $("#name").val() +
	"&address=" + 	$("#address").val() +
	"&budget=" + $("#budget").val();

    $.ajax(
    {
        type: "POST",
        url: urlrequest,
        dataType: "json",
        success: function (data) {
        	console.log("data received : " + data);
        },
        error: function (jqXHR, textStatus, errorThrown) {
        }
    });
}

Onderstaande functie verwijdert de geselecteerde department.
Er wordt een jQuery ajax call uitgevoerd naar een REST webservice.
De geslecteerde department in de globale variabele selectedDepartmentId wordt in het url pad meegegeven (dus niet als request parameter).

function deleteDepartment() {
    var  urlrequest = serverurl + "/dep/delete/" + selectedDepartmentId;

    $.ajax(
    {
        type: "POST",
        url: urlrequest,
        dataType: "json",
        success: function (data) {
        	console.log("data received : " + data);
        },
        error: function (jqXHR, textStatus, errorThrown) {
        }
    });
}

Voor employees hebben we nog de volgende bestanden:

  • employeesJQM.html: overzichtslijst met employees
  • employeeEditJQM.html: Show/Create/Update/Delete pagina voor een employee
  • employees.js : List/Create/Update/Delete functies voor een employee

Deze volgen grotendeels dezelfde opzet als departments.
De volgende bijzonderheden t.o.v. departments lichten we er hier speciaal uit:

employeeEditJQM.html :
Hierin zitten een tweetal bijzondere element constructies :

  • Een foreign key look-up naar departments middels een selectmenu element.
  • Een datum input met een date picker. Hieraan zitten veel lastige aspecten m.b.t. het vertalen van date format representatie. De database hanteert andere standaarden dan JPA, en die zijn weer anders dan wat we op het scherm willen zien.

employees.js :
Hierin wordt in getEmployeeListJQM ofwel de volledige lijst employees
gelezen of alleen de employees voor de geselecteerde department.

employeeEditJQM.html + employees.js

Hieronder het form onderdeel met daarin de nieuwe element types.
Voor birthDate wordt een “date” type gespecificeerd en wordt gebruikt gemaakt van het DateBox plugin element. Dit wordt gespecificeerd met

data-role=’datebox’ : specificeert dat het een DateBox element is
data-options=’{“mode”:”flipbox”}’ : geeft aan dat het type flipbox wordt gebruikt.

Het DateBox element kent verschillende soorten datepickers hier wordt de flipbox gebruikt (android style).

<form id="fmEmployeeEdit" method="post">
	<label for="firstName">Voornaam:</label>
	<input type="text" name="firstName" id="firstName" data-mini="true"
               disabled="disabled" style="opacity:1;"/>
	<label for="lastName">Achternaam:</label>
	<input type="text" name="lastName" id="lastName" data-mini="true"
               disabled="disabled" style="opacity:1;"/>

	<label for="birthDate">Geboortedatum:</label>
	<input type='date' name='birthDate' id='birthDate'
               data-role='datebox' data-options='{"mode":"flipbox"}'
               data-mini='true' data-theme='c'/>

	<div data-role="fieldcontain">
	   	<label for="department" class="select">Afdeling:</label>
	   	<select name="department" id="department">
	   	</select>
	</div>
</form>

DateBox
Het DateBox element moet apart worden gedownload

(http://dev.jtsage.com/jQM-DateBox/).

Daarvoor zijn de volgende files geïnstalleerd en de referenties in de head van indexJQM.html toegevoegd (de twee vetgedrukte):

	<link rel="stylesheet" href="libs/jquery.mobile-1.2.0.min.css" />
	<link rel="stylesheet" type="text/css" href="<strong>libs/jquery.mobile.datebox.min.css</strong>" />
	<script src="libs/cordova-2.1.0.js" type="text/javascript"></script>
	<script src="libs/jquery-1.8.2.min.js"></script>
	<script src="libs/jquery.mobile-1.2.0.min.js"></script>
	<script type="text/javascript" src="libs/date.js"></script>
	<script type="text/javascript" src="<strong>libs/jquery.mobile.datebox.min.js</strong>"></script>
	<script type="text/javascript"
src="http://dev.jtsage.com/cdn/datebox/i18n/jquery.mobile.datebox.i18n.en_US.utf8.js"></script>
	<script src="main.js"></script>
	<script src="departments/departments.js"></script>
	<script src="employees/employees.js"></script>

NB1: dit wijkt af van de voorbeeld bestanden die op de site van DateBox worden aangegeven. Als het voorbeeld van de site wordt gevolgd gaat het niet goed.

NB2: Er moet nogal wat aan date format conversie worden toegevoegd om dit goed te laten werken.

Departments->Employees Master->Detail Relatie
Onderstaand fragment toont de functie getEmployeeListJQM waarin afhankelijk van de globale variabele selectedDepartmentId de volledige lijst employees wordt opgehaald of alleen de employees voor de geselecteerde department.

function getEmployeeListJQM() {
    var  urlrequest;
    if (selectedDepartmentId) {
    	urlrequest= serverurl + "/emp/list?departmentId=" + selectedDepartmentId;
	} else {
    	urlrequest= serverurl + "/emp/list";
	}

De code vervolgt met soortgelijke Ajax calls als bij departments.

Selectmenu Element met foreign key lookup van Detail->Master Relatie
Het tweede bijzondere element t.o.v. departments is het selectiemenu element waarin de lijst departments wordt opgenomen en een foreign key lookup wordt gerealiseerd.
Hieronder het eerder getoonde form fragment:

	<div data-role="fieldcontain">
	   	<label for="department" class="select">Afdeling:</label>
	   	<select name="department" id="department">
	   	</select>
	</div>

De selectmenu wordt gedefinieerd met de omhullende div container met data-role fieldcontain.
Het volgende code fragment toont het ophalen van de department lijst in de functie getEmployeeJQM.

function getEmployeeJQM() {
	if (selectedEmployeeId != null) {
	    $.ajax(
	    {
            type: "POST",
            url: serverurl + "/dep/cblist",
            dataType: "json",
            success: function (data) {
            	console.log('cb data received : ' + data);
            	$("#department").empty();
                $.each(data, function (i, item) {
                    $("#department").append('<option value="' + item.id + '">' +
                          item.name + '</option>');
                });
                $("#department").select("refresh");
             },
             error: function (jqXHR, textStatus, errorThrown) {
	    });

Er wordt hier gebruikgemaakt van een speciaal voor dit doel beschikbare webservice cblist.
Deze maakt een speciale lijst met alleen de departmentId en de department naam.
De id wordt gebruikt als key in de select lijst : de value attribuut van het element. De elementen worden aangemaakt als vulling van het nog lege selectmenu element. Ook hier is weer de refresh van belang als de lijst is gevuld.

De code vervolgt hier met een soortgelijke Ajax call om de geselecteerde employee op te halen.
Hier echter weer twee bijzonderheidjes :
1. Het instellen van de actuele foreign key waarde in de selectmenu lijst.
2. De benodigde date format vertaling om een goed datum formaat op het scherm te krijgen.

$.ajax({
   type: "POST",
   url: serverurl + "/emp/show/" + selectedEmployeeId,
   dataType: "json",
   success: function (data) {
	console.log('data received : ' + data);
        $("#firstName").val(data.rows.firstName);
        $("#lastName").val(data.rows.lastName);
        $("#birthDate").val(formatDate(new Date(getDateFromFormat(data.rows.birthDate,
                             "y-M-dTm:m:m+s:s")),"dd-MM-yyyy"));
        $("#department").val(data.rows.department.departmentId).attr('selected',true)
                        .siblings('option').removeAttr('selected');
        $("#department").selectmenu("refresh", true);
      },
   error: function (jqXHR, textStatus, errorThrown) {
   }

Het instellen van de actuele foreign key waarde
Met een complexe constructie wordt van het department element de waarde ingesteld op de gekozen foreign key departmentId, en aanvullend wordt het attribuut “selected=true” toegevoegd en van alle anderen (de option siblings) wordt hetzelfe attribuut, indien aanweig, verwijderd.
Het geselecteerde element wordt dus ingesteld d.m.v. de aanwezigheid van dit attribuut.

De date format vertaling
Het voorgaande code fragment toont voor het birthDate veld hoe deze wordt geconverteerd naar een datum formaat om te tonen op het scherm. In dit voorbeeld zien we hoe uit een MySQL database met JPA (OpenJPA) een datumwaarde zoals “1959-03-14T00:00:00+01:00″ wordt gekregen. Deze wordt als inputformat “y-M-dTm:m:m+s:s” gespecificeerd.
De waarde wordt voor representatie naar het scherm vertaald naar format “dd-MM-yyyy”.
Daardoor zal het resultaat worden weergegeven als “14-03-1959”.

In de onderstaande code fragment van de functie saveEmployee wordt de datum format conversie andersom weer uitgevoerd. Nu moet het format rekening houden met de standaard van MySQL, nl “yyyy/MM/dd”. Nu wordt het formaat “d-M-y” vertaald naar het voor MySQL benodigde formaat “yyyy/MM/dd”.

function saveEmployee() {
    var  urlrequest;
    if (selectedEmployeeId) {
    	urlrequest= serverurl + "/emp/update/" + selectedEmployeeId;
	} else {
    	urlrequest= serverurl + "/emp/create";
	}
	urlrequest = urlrequest +
	"?firstName=" + $("#firstName").val() +
	"&lastName=" + 	$("#lastName").val() +
	"&birthDate=" + formatDate(new Date(getDateFromFormat($("#birthDate").val(),
                          "d-M-y")),"yyyy/MM/dd") +
	"&department=" + $("#department").val();

Voor deze date format conversies wordt een JavaScript library gebruikt van JavaScript Toolbox : http://www.javascripttoolbox.com/lib/date/ Het bestand data.js bevat de volgende format conversie functies:

  • String formatDate(Data inputdate, String doelformat);
  • Date getDateFromFormat(String inputstring, String inputformat);
This entry was posted in Mobile and tagged . Bookmark the permalink.

Geef een reactie

Je e-mailadres wordt niet gepubliceerd.

De volgende HTML tags en attributen zijn toegestaan: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong>