This translation is older than the original page and might be outdated. See what has changed.



Javascript code examples


To be able to use the functions cstr, now, len, etc from VBScript, a “vbscriptsupport.js” file has been implemented which we will have to include at the companies collection, that displays these functions to have it availables when we use scripts javascript by using the notation “vbSupport.left(XXXXX)”, “vbSupport.now()”, “vbSupport.cstr(XXXXX)”, etc.



Code to browse a collection record by record (When are too many data to load them in memory).

function recorrerColeccion() {
	var sMensaje = "Estos son los usuarios:";
 
	// Variable con la coleccion que se va a utilizar
	var mColeccion = appData.getCollection("Usuarios");
 
	// Guardamos el filtro que tiene la colección actualmente
	var filtro=mColeccion.getFilter();
 
	// Le ponemos un filtro a la colección
	mColeccion.setFilter("t1.ACTIVO=1 AND t1.IDUSUARIO=" + user.ID + " AND date(t1.FECHA)=date('" + devolverFecha() + "')");
 
	// Variable con la coleccion que se va a utilizar
	mColeccion.startBrowse();
	try {
		var mObjeto;
		while((mObjeto = mColeccion.getCurrentItem()) != undefined) {
			sMensaje = sMensaje + "\n" + "Nombre: " + mObjeto.NOMBRE + " Login: " + mObjeto.LOGIN;
			mColeccion.moveNext();
		}
	} catch(ex) {
		ui.showToast("Hubo un error al hacer startBrowse() en: " + mColeccion.getName());
	} finally {
		// Cerramos el recordset
		mColeccion.endBrowse();
 
		// Dejamos la colección con el filtro que tenía al principio
		mColeccion.setFilter(filtro);
	}
	return sMensaje;
}



A collection with few data (for instance, the details rows of a contents), may be browsed by loading the record in a memory array instead browsing record by record.


function recorrerContents()
{
	// Todas las variables que definamos dentro de una funcion son locales a ésta.
	var coll;
	var collCount;
	var SUM=0;
	var i;
	var obj;
 
	// (Para hacer referencia al objeto actual se utiliza self, para no interferir con el this de javascript).
	self.MAP_TLCABECERAROJO="TOTAL ENTREGAS";
	self.MAP_TLCABECERAROJO1="SUMATORIO ENTREGADAS";
 
	collCount=0;
	SUM=0;
	// Cogemos la coll contents Detalles del objeto actual 
	coll=self.getContents("ContentSinCobrar");
 
	// Cargamos los registros en memoria y averiguamos el número de registros
	coll.loadAll();
	collCount=coll.count();
 
	// Utilizamos un For para recorrer los registros del contents.
	for (i=0;i<collCount;i++){
		// Tomamos un registro del content con el indice de dicho registro.
		obj=coll.get(i);
		SUM=parseFloat(SUM)+parseFloat(obj.IMPORTETOTAL);
	}
	self.MAP_TLVISITASSIN="Entregadas Sin Cobrar: ";
	self.MAP_VISITASSIN=SUM;
 
	// Liberamos la memoria que cargamos con el loadall.
	coll.clear();
}



function MarcarIncidencia(idIncid)
{
	// Buscamos una incidencia en concreto conociendo el valor de algún campo único (Siempre se busca como cadena).
	var collInc = appData.getCollection("IncidenciasAct");
	var objInc = collInc.getObject(vb.cstr(idIncid));
 
	objInc.CERRADA=1;
	objInc.FECHACIERREXONE=vb.now();
	objInc.save();
 
	objInc=null;
	collInc.clear();
}



AppData.PushValue objLoquesea lanza un objeto en edición.

function ejemploFindObject() {
	var nId = 2;
	var mObjeto = appData.getCollection("Usuarios").findObject("ID = " + nId);
	if(mObjeto == undefined) {
		ui.msgBox("No se encontró el usuario con ID " + nId, "Mensaje", 0);
	}
	else {
		ui.msgBox("El login del usuario con ID " + nId + " es: " + mObjeto.LOGIN, "Mensaje", 0);
 
		// Lo lanzamos en edición
		appData.pushValue(mObjeto);
	}
}



Creating a new record with script. The script wil be able to be executed in a create, insert, delete, button

function creaRegistro() {
	var cCierre = appData.getCollection("Cierre");
	var objCierre = cCierre.createObject();
 
	cCierre.addItem(objCierre);
 
	objCierre.NUMINCIDENCIA=self.NUMINCIDENCIA;
	objCierre.NUMPROGRESO=self.NUMPROGRESO;
	objCierre.CODTECNICO=self.CODTECNICO;
 
        objCierre.save();
}



We have several ways to show a message on screen.

Command Description
ui.msgBox(“Ejemplo”, “Mensaje”, 0);
msg1=ui.msgBox(“Ejemplo”, “Mensaje”, 2);
msg2=ui.msgBox(“Ejemplo”, “Mensaje”, 4);

Message of the framework that needs answer by the user. The last parameter can be:

  • 0, returning no value, it only appears an OK button.
  • 2, showing us 2 buttons: ACCEPT and CANCEL that return 1 or 2 respectively, as one or the other is pressed.
  • 4, that shows us 2 buttons: YES and NO that return 6 or 7 respectively, as one or the other is pressed.
ui.showNotification(1, “Título”, “Mensaje”); Notification in the top bar of notifications, an ID is specified (1) in order if we want to modify the notification further, we can use the same command with another message and the one which did exist already will be modified.
ui.dismissNotification(1); It deletes the Notification from the top bar of notifications made with the “ShowNotification”, we have to specify the ID of the notification to specify the message we want to remove.
ui.showToast(“Se ha recibido un nuevo mensaje”); Mensaje TOAST message of the operative system, see the screenshot below. The message disappears automatically after a couple of seconds on screen.


Example of code:

<!-- En este caso vamos a hacer uso del nodo replica-ok, para capturar el evento de cuando recibimos operaciones en la tabla gen_mensajeria -->
<replica-ok-gen_mensajeria>
	<action name="runscript">
		<script language="Javascript">
 
			// AppData.UserInterface.MsgBox hace exactamente lo mismo, ui es una abreviatura de AppData.UserInterface.
			ui.msgBox("Ejemplo", "Mensaje", 0);
 
			// El primer parámetro es el ID por si hay que modificarla luego
			ui.showNotification(1, “Título”, “Mensaje”);
 
			// Con la siguiente línea cambiaríamos el título y el mensaje de la notificación existente, pues tienen el mismo ID.
			// ui.showNotification(1, “Título2”, “Mensaje2”);
 
			// Si queremos quitar la notificación, hemos de pasarle el "ID" de la que queremos quitar.
			//ui.dismissNotification(1);
 
			// Para sacar un mensajito efímero que dura 3 segundos en pantalla.
			ui.showToast("Se ha recibido un nuevo mensaje");
		</script>
	</action>
</replica-ok-gen_mensajeria>



<!-- En este caso al nodo que ejecutamos lo hemos llamado "onback", de forma que de paso, nos sirve en android, para
     que el botón de volver atrás físico de android nos ejecute ésta misma acción -->
 
<onback refresh="false" show-wait-dialog="false">
	<action name="runscript">
		<script language="javascript">
			//st_msg="Usuario o contraseña incorrectos";
			//if (st_msg!="")
			//	appData.failWithMessage(-8100,st_msg);
 
			appData.failWithMessage(-11888,"##EXIT##");
		</script>
	</action>
</onback>



If we are working offline, by default, everything is done at the framework is stored at the tabla master_replica_queue of the device in order to replicate it to the server later.
If at any time we don´t want this happens, we can make any operation directly over the database of the device without it being replicated to the server.
This behaviour is very used in the maintenances over the local database of the device to delete information that we do not need yet (Reports or old Orders, etc…).
The methodology is very simple, it is about to create a collection that points directly to the device database. By general rule, we usually call it always the same in all projects: “ForMaintenance”. What is really special is the connection=“mtto” attribute that this collection has, which points to the <connection>node that such name has.

It cannot be 2 <connection> nodes with the same name at the mappings. It is advisable that all the <connection> nodes are defined at the APP node and so have them all “centralized” at an only place and being availables to any collection to which we define the connection=“mtto” attribute (or any other name we have set to the connection node).


<!-- Primero hemos de definir una colección que tenga un atributo connection que apunte directamente a la base de datos local del dispositivo -->
<coll name="ForMaintenance" title="ForMaintenance" objname="ForMaintenance" updateobj="ForMaintenance" progid="ASData.CASBasicDataObj" 
	sql="SELECT t1.*
	FROM ##PREF##ForMaintenance t1" connection="mtto">
 
	<!-- Esta definición de connection es preferible hacerla en el nodo APP y así otras colecciones puedan hacer uso de ella. -->
	<connection name="mtto" connstring="bd/gestion.db" />
 
	<group name="General" id="1" />
</coll>
 
<!-- En cualquier momento podemos ejecutar un script que nos ejecute las operaciones "al margen" de la réplica -->
<sin_replica>
	<action name="runscript">
		<script language="Javascript">
 			//Borramos las lineas de Documentos con ID Null
			appData.getCollection("ForMaintenance").executeSqlString("DELETE FROM Gen_detdocumento WHERE IDDOCUMENTO IS NULL");																						</script>
	</action>
</sin_replica>



We can crate a MACRO of user in a collection to replace in real time the SQL of a collection, being able to replace even the SQL of the full collection.

<coll name="Gastos" 
sql="SELECT g.*, tg.NOMBRE as MAP_GASTO, g.FOTO1 as MAP_FOTO1 ... FROM ##PREF##GASTOS g 
LEFT JOIN ##PREF##TiposGasto tg ON g.IDTIPOGASTO=tg.ID WHERE ##TIPO## " 
filter="g.BAJA=0" objname="Gastos" updateobj="Gastos" ... />
 
<macro name="##TIPO##" value="1=1" default="true" />


Once defined the macro in the collection, we can use it later to change the value of that macro and therefore changing the SQL.

In this concrete case, we change the filter of a collection when the value of a “MAP_FTTIPOGASTO” field changes.

<onchange>
	<field name="MAP_FTTIPOGASTO">
		<action name="runscript">
			<script language="javascript">
				collGastos = self.getContents("Gastos");
 
				//Aquí cambiamos el valor de la macro que tenemos definida en la colección Gastos.
				collGastos.setMacro("##TIPO##","tg.NOMBRE LIKE '%"+self.MAP_FTTIPOGASTO+"%'");
 
				//Así sería el comando para obtener el valor de una macro.
				var Valor=collGastos.getMacro("##TIPO##");
 
				self.executeNode("refrescaTotal");
			</script>
		</action>
	</field>
</onchange>



We can createa variable that thave global scope and so they can be available from any place of the application.

An option would be including a .js file in the companies collection, where they would be defined with “var” the different javascript variables that we are going to need to be globals.

The other method would be the one that has been used so far in the platform:

// Para establecer el valor de una variable global:
 
	appData.getCurrentEnterprise().setVariables("GPSTime", objTransac.FECHA);
 
// Para obtener el valor de una variable global:
 
	vLastPieza=appData.getCurrentEnterprise().getVariables("LastPieza");
 
// Para establecer el valor de una variable de la colección "Usuarios":
 
	var mColeccion = appData.getCollection("Usuarios");
	mColeccion.setVariables("mensaje", "hola que tal");
 
// Para obtener el valor de una variable de la colección "Usuarios":
 
	var mColeccion = appData.getCollection("Usuarios");
	var sMensaje = mColeccion.getVariables("mensaje");



	ui.showGroup(self.MAP_PAGINA,"##LEFT_IN##",500,"##LEFT_OUT##",500);



//Para poder dar de __ALTA__ un registro en un contents tendríamos que pasarle el ID de la cabecera...
function addContents(coll,campoenlace) {
	var CollCV = appData.getCollection(coll);
	var ObjCV = CollCV.createObject();
	CollCV.addItem(ObjCV);
	ObjCV(campoenlace) = self.ID;
 
	appData.pushValue(ObjCV);
}
 
//A esta función tenemos que pasarle la referencia al contents y el ID que queremos EDITAR. 
function editContents(coll,idselected) {
	var CollCV = appData.getCollection(coll);
	var ObjCV = CollCV.findObject("ID="+idselected);
	if (ObjCV) {
		appData.pushValue(ObjCV); 
	}	
}
 
//Realmente no borramos el registro físicamente, lo que hacemos es cambiar el campo de enlace con la cabecera
//o podemos ponerle un BAJA=1, para darlo de baja lógica. Posteriormente tendríamos un mantenimiento para
//eliminar éstos registros del dispositivo (Para que no se replique el borrado físico del registro).
function deleteContents(coll,idselected,campoenlace)
	var result = ui.msgBox("¿Está seguro de que desea eliminar el registro seleccionado?","¡ALERTA!",4);
	if (result == 6) {
		var CollCV = appData.getCollection(coll);
		var ObjCV = CollCV.findObject("ID="+idselected);
		if (ObjCV) {
			ObjCV.campoenlace = -9999;
			ObjCV.save();
		}	
	}
}



<script language="VBScript">
    'ERROR DISPONIBLE EN TODAS LAS PLATAFORMAS
        'Muestra un mensaje de error con el texto que le pongamos
        appdata.failwithmessage -8100,"Mensaje de error"
 
    'ERRORES DISPONIBLES EN PLATAFORMA POCKET PC
        'Muestra un mensaje de error con el texto que le pongamos
        appdata.failwithmessage -7500,"Mensaje de warning"
 
        'Refresca la cabecera cuando se ejecuta con el Selecteditem. YA NO ES NECESARIO.
        appdata.failwithmessage -666,"Mensaje de información"
 
        'Mensaje de informacion con icono de Información
        appdata.failwithmessage -11888,"Mensaje que queremos mostrar"
 
        'Mensaje personalizado con posiblidad de bloqueo con botón.
        appdata.failwithmessage -11822,"Mensaje de información"
 
        'Mensaje propio de CGS
        appdata.failwithmessage -11811,"Mensaje de información"
 
        'Inicia la replica desde un script
        Appdata.FailWithMessage -8100,"##STARTREPLICA##"
 
        'Esta macro funciona conjuntamente con CGStart (kiosko de PDA). 
        'Cierra el frame y le dice al kiosko q reinicie la PDA. 
        'Si el kiosko no está instalado, simplemente se cierra la aplicación.
        Appdata.FailWithMessage -8100,"##RESETAPP##"
 
        'Salir de la aplicacion.
        Appdata.FailWithMessage -8100,"##EXITAPP##"
 
        'Usado para salir de las ventanas de Nomen y Edit.
        Appdata.FailWithMessage -8100,"##EXIT##"
</script>



CODE TO MAKE A BUTTON.

It is made up of 2 nodes:

Node 1 Prop of type “B” (button) It is the node that defines where it comes up, the title and which node it calls to execute its code. It has the method attribute, that is the one which makes the call to the node that has its code, the name “ExecuteNode”, is the name of the “method” name, and it has to be called in the same way (uppercase-lowercase).
Node 2 Node in which the code to execute is put The name is defined by the user, and as only requirement, it is the node name, it has to be called the same (uppercase-lowercase) that the node to which the button node calls in its method attribute.


<prop name="Iniciar" type="B" method="ExecuteNode (inicializar)" title="Iniciar Ruta" visible="1" labelwidth="24" onchange="refresh255"/>
 
<inicializar>
	<action name="runscript">
		<script language="javascript">
			var vResult;
			vResult =ui.msgBox("Esta acción reiniciará toda la ruta. ¿Está seguro que desea ejecutarla?","Advertencia",1);
			if (vResult==1) {
				//La siguiente sentencia METE las cosas en réplica, como siempre en operaciones atómicas,
				//es decir, operaciones que únicamente afectan a un registro cada vez. Si no se desea meter
				//éstas operaciones en réplica, utilizar el método del "ForMaintenance".
				appData.executeSql("UPDATE Gen_Rutas SET VISITADO=0 WHERE IDUSUARIO=" + self.IDUSUARIO);
				ui.msgBox("Ruta inicializada","Advertencia",0);
			}						
		</script>
	</action>
</inicializar>



Generating an UUID with Javascript.

function generateUUID() {
    var d = new Date().getTime();
    var uuid = 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, function(c) {
        var r = (d + Math.random()*16)%16 | 0;
        d = Math.floor(d/16);
        return (c=='x' ? r : (r&0x3|0x8)).toString(16);
    });
    return uuid;
}



// Ejemplo de uso: var f=getFecha("dmy","/");
function getFecha(formato,separador)
{ 
	var sToday = "";
	var today = new Date();
	var dd = today.getDate();
	var mm = today.getMonth()+1; // De 0-11 así que sumamos 1.
	var yyyy = today.getFullYear();
 
	if(dd<10) {
	    dd='0'+dd;
	} 
 
	if(mm<10) {
	    mm='0'+mm;
	} 
 
	if (!separador) {
		separador="/";
	}
	if (formato) {
		switch (formato){
		case "dmy":
			sToday = dd+separador+mm+separador+yyyy;
			break;
		case "mdy":
			sToday = mm+separador+dd+separador+yyyy;
			break;
		case "ymd":
			sToday = yyyy+separador+mm+separador+dd;
			break;
		default: 
			sToday = dd+"/"+mm+"/"+yyyy;
			break;
		}
	}
	else {
		sToday = dd+"/"+mm+"/"+yyyy;
	}
	return sToday;
} 



COLLECTION THAT DISPLAYS THE AMOUNT OF MISSING DATA TO SEND.

This collection can be as complicated as we want to, even by putting Join with another tables, to say from which table remains the data to be sent from the mobile device to the central.

The MASTER_REPLICA_QUEUE table, only can be used to see data, never for writing directly on it, since it is a system table, with which the data will be sent from the mobile device to the central server.

<coll name="InformacionQueue" title="la informacion queue" 
	sql="SELECT COUNT(ID) AS MAP_CUENTA
	FROM master_replica_queue"
	objname="master_replica_queue" updateobj="master_replica_queue" progid="ASData.CASBasicDataObj" forprint="false" 
	editwidth="75" editheight="76" autorefresh="true">
	<group name="General" id="1"/>
	<prop name="MAP_CUENTA" visible="7" group="1" type="N" fieldsize="20" labelwidth="8" size="30">DATOS PARA ENVIAR</prop>
</coll>