Documentos de Académico
Documentos de Profesional
Documentos de Cultura
de contenido
Contenidos 1.1
Introducción 1.2
Instalación 1.3
Nuestra primera aplicación 1.4
Código HTML básico de una aplicación 1.4.1
Compilar un proyecto 1.4.2
Aviso durante la carga 1.4.3
Instanciar una aplicación 1.4.4
Comprobar los resultados 1.4.5
Fichero único vs. MVC 1.4.6
Uso de Componentes y Contenedores 1.5
Instanciar componentes 1.5.1
Configuración de componentes 1.5.2
Identificadores y referencias 1.5.3
Array de items y el atributo xtype 1.5.4
Añadir componentes a contenedores 1.5.5
Eliminar componentes de un contenedor 1.5.6
Mostrar y ocultar componentes 1.5.7
Eventos 1.5.8
Destruir componentes 1.5.9
Layouts 1.6
Layout tipo hbox 1.6.1
Layout tipo vbox 1.6.2
Layout tipo card 1.6.3
Layout tipo fit 1.6.4
Docking o acoplamiento 1.6.5
Pack y align 1.6.6
Transiciones de cambio de vista 1.7
Componentes 1.8
Toolbars 1.8.1
2
Botones 1.8.2
TabPanel 1.8.3
Carousel 1.8.4
Diálogos 1.8.5
Formularios 1.8.6
Almacenamiento 1.9
Data Model 1.9.1
Data Store 1.9.2
Proxy 1.9.3
Componentes asociados a datos 1.10
Plantillas 1.10.1
DataViews 1.10.2
Listados 1.10.3
Formularios 1.10.4
Más información 1.11
Ejercicios 1 1.12
Ejercicios 2 1.13
Ejercicios 3 1.14
3
Contenidos
Contenidos
Existen muchas librerías o frameworks de desarrollo que están orientados a la creación de
webs para móviles y que intentan darle el aspecto de una aplicación nativa. Una de las más
conocidas y utilizadas en la actualidad es Sencha Touch. Este tipo de librerías son de gran
ayuda ya que nos ahorran muchísimo tiempo en la programación ya que con un único
desarrollo Web lo podemos aprovechar de forma generalizada para los diferentes sistemas
de dispositivo móvil, como Android, iOS, etc.).
En este libro se tratan desde los aspectos más básicos de Sencha Touch, como la
instalación de la libreria y la creación de una primera aplicación, hasta otros más avanzados
como el almacenamiento de datos y el uso de componentes asociados a datos.
Introducción
Instalación
Nuestra primera aplicación
Código HTML básico de una aplicación
Compilar un proyecto
Aviso durante la carga
Instanciar una aplicación
Comprobar los resultados
Fichero único vs. MVC
Uso de Componentes y Contenedores
Instanciar componentes
Configuración de componentes
Identificadores y referencias
Array de items y el atributo xtype
Añadir componentes a contenedores
Eliminar componentes de un contenedor
Mostrar u ocultar componentes
Eventos
Destruir componentes
Layouts
Layout tipo hbox
Layout tipo vbox
Layout tipo card
Layout tipo fit
4
Contenidos
Docking o acoplamiento
Pack y align
Transiciones de cambio de vista
Transiciones con animación
Componentes
Toolbars
Botones
TabPanel
Carousel
Diálogos
Formularios
Almacenamiento
Data Model
Data Store
Proxy
Componentes asociados a datos
Plantillas
DataViews
Listados
Formularios
Más información
Ejercicios
5
Introducción
Introducción
Sencha Touch es un framework para el desarrollo de aplicaciones móviles centrado en
WebKit (base de los navegadores web Safari, Google Chrome, Epiphany, Maxthon, y Midori
entre otros). Fue el primer framework basado en HTML5 y JavaScript, además utiliza CSS3
para realizar animaciones. La apariencia de las aplicaciones desarrolladas es similar al de
las aplicaciones nativas en Android, BlackBerry e iOS. Sencha Touch está disponible tanto
en versión con licencia comercial como con licencia Open Source GPL v3.
Sencha Touch funciona perfectamente junto a PhoneGap (ver capítulo correspondiente), por
lo que puede ser usado para distribuir nuestras aplicaciones en la App Store o en Android
Market. Se basa en el uso de un mecanismo que empotra nuestra aplicación en una shell
6
Introducción
nativa de la forma más sencilla posible. Además, gracias a PhoneGap podemos hacer uso
de la API nativa del dispositivo para acceder a la lista de contactos, la cámara y muchas
otras opciones directamente desde JavaScript.
Integración de datos
Al igual que con ExtJS (biblioteca de JavaScript para el desarrollo de aplicaciones web
interactivas), Sencha Touch implementa el patrón de diseño MVC en el lado del cliente y
nos ofrece una API rica y poderosa para manejar flujos de datos desde una increíble
variedad de fuentes. Podemos leer datos directamente a través de AJAX, JSON, YQL o la
nueva capacidad local storage de HTML5. Podemos enlazar esos datos a elementos
específicos de nuestras vistas, y utilizar los datos sin conexión gracias a los almacenes
locales.
Sencha Touch:
Tiene una curva de aprendizaje mucho mayor y necesita una mayor comprensión del
lenguaje de programación JavaScript, pero gracias a esto proporciona una API mucho
más potente.
Dispone de un mayor número de controles para la interfaz de usuario, así como efectos
de transición y animaciones entre páginas mucho más personalizables.
Más rápido en mayor número de dispositivos móviles (en Android a partir de la versión
2.1). El comportamiento y velocidad de Sencha Touch es mucho mejor que el de otros
frameworks, a excepción del tiempo de carga inicial, pues JQuery Mobile pesa menos.
Al estar basado en ExtJS (usan el mismo núcleo), es muy robusto y potente, además
de ser un framework ampliamente probado y usado (también debido a que fue uno de
los primeros en aparecer).
Al igual que en ExtJS, y en comparación con JQuery Mobile, se escribe mucho código.
Esto podría ser tomado como un pro o como un contra. Es bueno porque indica una
mayor potencia de configuración y personalización, pero por contra conlleva más
tiempo de desarrollo y de aprendizaje.
JQuery Mobile:
7
Introducción
Es necesario escribir muy poco código (y casi no se usa JavaScript) para lograr
aplicaciones móviles muy interesantes. En lugar de orientarse a la programación
JavaScript, JQuery Mobile se centra en usar etiquetas HTML con atributos definidos
por el framework.
Ambos frameworks son buenas opciones para el desarrollo de aplicaciones móviles. Los
dos utilizan HTML5, JavaScript e integran la tecnología AJAX. La decisión dependerá de las
necesidades de la aplicación a desarrollar. En principio, Sencha Touch es más apropiado
para aplicaciones grandes, que necesiten de mayor personalización o configuración y que
vayan a hacer un mayor uso del lenguaje de programación JavaScript. JQuery Mobile se
suele utilizar para aplicaciones en las que se necesite una interfaz de usuario que conecte
directamente con un servidor y que haga un menor uso de JavaScript.
8
Instalación
9
Nuestra primera aplicación
mkdir myapp
cd myapp
Una vez creada la aplicación podemos comprobar que funcione correctamente de varias
formas:
10
Nuestra primera aplicación
Sencha Touch solo funciona con navegadores basados en WebKit, como son: Safari,
Google Chrome, Epiphany, Maxthon o Midori. Si lo probamos en un navegador que no lo
soporte, como Firefox o Internet Explorer, solamente veremos una página en blanco o un
resultado erróneo. Por lo tanto para probar nuestros proyectos Web tendremos que instalar
uno de los navegadores soportados, como Google Chrome (http://www.google.es/chrome) o
Apple Safari (http://www.apple.com/es/safari/).
Aunque la mayoría de webs que podemos hacer con Sencha Touch se podrían ejecutar y
visualizar directamente sin necesidad de un servidor Web, sí que será necesario su uso si
queremos probar nuestros proyectos utilizando algún emulador o un dispositivo móvil real.
Estructura de carpetas
Las carpetas y ficheros principales que se generan con un nuevo proyecto son:
11
Nuestra primera aplicación
12
Código HTML básico de una aplicación
<!DOCTYPE HTML>
<html manifest="" lang="en-US">
<head>
<meta charset="UTF-8">
<title>Mi aplicación</title>
<script id="microloader" type="text/javascript"
src=".sencha/app/microloader/development.js"></script>
</head>
<body>
</body>
</html>
A continuación analizaremos por separado cada una de las partes de este código:
<!DOCTYPE HTML>
<html manifest="" lang="en-US">
...
</html>
La primera línea nos indica que este es un documento del tipo HTML5. Las etiquetas de
<html> y </html> indican el inicio y final del documento HTML y deben de contener en su
<head>
<meta charset="UTF-8">
<title>Mi aplicación</title>
...
</head>
<body>
</body>
Todo documento HTML (y HTML5 también) debe de contener primero una sección de
cabecera ( <head> ) y a continuación una sección con el contenido principal o cuerpo del
documento ( <body> ). En este caso el cuerpo del documento ( <body> ) se encuentra vacío.
Esto se debe a que la librería Sencha Touch crea todo el contenido de la Web, incluidos
todos los elementos de la interfaz, mediante código JavaScript.
13
Código HTML básico de una aplicación
La cabecera del documento ( <head> ) debe de contener como mínimo los metadatos
acerca del tipo de contenido, el conjunto de caracteres usados, y un título que mostrará el
navegador en la parte superior de la ventana. Además debe de contener un enlace a la
librería de javascript de carga de Sencha Touch:
Este fichero se utiliza para entornos de desarrollo, pero para producción o testing
tendremos que compilar la aplicación mediante Sencha Cmd o generar nuestra propia
compilación. En futuras secciones se tratará más en profundidad este tema.
Ahora ya tenemos cargadas las librerías de Sencha Touch y el código de nuestra aplicación
para empezar a trabajar. De momento, si lo visualizamos en un navegador solo veremos
una página en blanco ya que el código de nuestra aplicación ( app.js ) de momento está
vacío.
14
Compilar un proyecto
Compilar un proyecto
Al crear un nuevo proyecto este viene preparado para un entorno de desarrollo, por lo que
si queremos obtener la versión final de producción con el código optimizado para su
utilización tendremos que ejecutar (dentro de la carpeta del proyecto):
las direcciones:
http://docs.sencha.com/touch/2.4/getting_started/using_creating_builds.html
http://www.sencha.com/blog/getting-started-with-sencha-touch-2-build-a-weather-utility-app-
part-3/
15
Aviso durante la carga
<body>
<div style="margin:100px auto 0 auto; width:220px; font-size:16pt;">
Cargando aplicación...
</div>
</body>
16
Instanciar una aplicación
Ext.application({
name: 'MyApp',
launch: function() {
Ext.create("Ext.Panel", {
fullscreen: true,
html: '¡Hola Mundo!'
});
}
});
A continuación analizaremos por separado cada una de las partes de este código:
Ext.application({
name: 'MyApp',
launch: function() {
...
}
});
Con Ext.application({ ... }); creamos una nueva instancia de Sencha Touch, es decir,
este es el constructor de nuestra aplicación. Entre las llaves {} le pasaremos la lista de
opciones de configuración para crear nuestra aplicación. En primer lugar le damos un
nombre name: 'MyApp' , con esto automáticamente se crea una variable global llamada
MyApp junto con los siguientes namespaces:
MyApp
MyApp.views
MyApp.controllers
MyApp.models
MyApp.stores
17
Instanciar una aplicación
La función launch: function() { ... } solo se ejecuta una vez al cargar la aplicación, y es
donde deberemos de colocar el código necesario para definir y cargar nuestra aplicación.
En el ejemplo propuesto creamos un panel usando el siguiente código:
Ext.create("Ext.Panel", {
fullscreen: true,
html: '¡Hola Mundo!'
});
Con esto ya hemos creado nuestra primera aplicación, un panel que ocupa toda la pantalla
con el texto "¡Hola Mundo!".
código.
18
Comprobar los resultados
Recordad que además del acceso directo se puede acceder usando un servidor web (en
general será preferible esta 2ª opción ya que en muchas ocasiones el acceso directo puede
dar problemas de permisos o de cross origin request). Para el servidor tenemos dos
alternativas:
Usar un servidor web propio instalado en nuestro sistema operativo, copiar nuestro
proyecto dentro de la carpeta pública del servidor y acceder a través de la dirección:
http://localhost/MyApp
Usar el servidor Web que proporciona Sencha Cmd. Para activarlo tenemos que ir a la
carpeta de nuestra aplicación y en un terminal ejecutar sencha web start & . Para
acceder en este caso utilizaremos la ruta: http://localhost:1841/
19
Comprobar los resultados
Estos emuladores son parte del IDE de Xcode y del SDK de Android. Para poder utilizarlos
necesitaremos tener instalados los SDKs además de tener el código en un servidor Web.
Para más información consultar la sección inicial "Instalación de un servidor Web" y
"Emuladores".
Para depurar nuestras aplicaciones, además de las herramientas para el desarrollador que
incorpora el navegador podemos instalar la siguiente extensión especial para Sencha:
https://chrome.google.com/webstore/detail/app-inspector-for-
sencha/pbeapidedgdpniokbedbfbaacglkceae?hl=en
20
Fichero único vs. MVC
Siguiendo este patrón una aplicación se conforma por una lista de Modelos, Vistas,
Controladores, Stores (o Almacenes) y Profiles (o Perfiles), además de una serie de
metadatos y recursos como el icono, imágenes, etc.
En este curso de introducción no vamos a tratar el modelo MVC, pero todos los conceptos y
herramientas que vamos a ver sirven también para el patrón MVC.
http://docs.sencha.com/touch/2.4/core_concepts/about_applications.html
21
Uso de Componentes y Contenedores
Qué es un componente
La mayoría de las clases visuales de Sencha Touch son componentes que heredan de
Ext.Component lo que les da una serie de propiedades:
Qué es un contenedor
Las aplicaciones se forman mediante multitud de componentes, normalmente anidados
unos dentro de otros. Los contenedores son un tipo de componente especial que permite
agrupar y organizar otros componentes dentro de si mismos. La mayoría de las aplicaciones
tendrán un único contenedor (el viewport) que ocupará toda la pantalla, el cual contendrá
una serie de componentes hijos. Por ejemplo en una aplicación de correo el contenedor del
viewport contendrá dos componentes principales, uno para la lista de mensajes y otro para
la previsualización de los correos.
Los layouts determinan la disposición de los componentes hijos dentro del contenedor. En la
aplicación del ejemplo de correo podríamos utilizar un layout horizontal del tipo HBox para
indicar que la lista se sitúe en la parte izquierda y el panel de previsualización a la derecha
ocupando el resto del espacio.
22
Instanciar componentes
Instanciar componentes
Los componentes se crean igual que el resto de clases en Sencha Touch, utilizando el
método Ext.create . A continuación se incluye un ejemplo:
Este trozo de código crea una instancia de un panel, le asigna un contenido HTML básico y
lo almacena en la variable panel . Un Panel es un tipo de componente que puede contener
HTML u otros items o paneles.
En el código de ejemplo simplemente se instancia el panel pero sin llegar a mostrarse, esto
es porque al instanciar un elemento no se renderiza (no se hace visible) en la pantalla de
forma automática. Esta característica nos permite crear componentes cuando queramos y
mostrarlos cuando nos hagan falta, lo cual en general será más rápido que instanciarlos y
mostrarlos inmediatamente.
Si queremos mostrar el panel que hemos creado simplemente tendríamos que añadirlo al
viewport de la aplicación, de la forma:
Ext.Viewport.add(panel);
23
Configuración de componentes
Configuración de un componente
Al crear un componente le podemos pasar un objecto con las opciones para configurarlo.
Este objecto lo podemos crear directamente en la propia declaración del componente
englobando las opciones entres llaves {} e indicando las opciones de configuración como
pares de clave - valor. Por ejemplo:
Cada componente tiene multitud de opciones de configuración, las cuales las podemos
consultar en la documentación de Sencha Touch.
Ext.application({
name : 'MiApp',
requires: ['Ext.MessageBox'],
launch : function() {
// Crear un panel y pasarle opciones de configuración
var panel = Ext.create('Ext.Panel', {
fullscreen: true,
html: 'Esto es un panel!'
});
24
Configuración de componentes
Cada opción de configuración tiene sus métodos tipo getter y setter, se generan
automáticamente a partir del nombre del atributo por lo que siempre siguen el mismo
patrón. Por ejemplo, la opción de configuración html tiene los métodos getHtml y
setHtml , o por ejemplo la opción de configuración defaultType tiene los métodos
getDefaultType y setDefaultType .
25
Identificadores y referencias
Identificadores y referencias
Como ya hemos visto, al crear un elemento lo podemos almacenar en una variable de
javascript que posteriormente podemos utilizar para hacer referencia a él. Pero Sencha
Touch también permite definir un identificador ( id ) mediante el cual posteriormente
podremos hacer referencia a ese elemento. La forma de definirlo, por ejemplo, para un
panel es la siguiente:
Posteriormente desde otro elemento podremos referirnos a este panel de dos formas:
Por ejemplo, para añadir este elemento a un panel contenedor tendríamos que hacer:
Este identificador podemos usarlo con todos los elementos: botones, barras, etc. Es una
buena práctica definirlo para todos los elementos que creamos que vayamos a referenciar
posteriormente. En este documento, por simplicidad, no lo incluiremos en todos los
ejemplos, solamente cuando sea necesario.
26
Array de items y el atributo xtype
Si los elementos han sido creados previamente podemos usar su nombre de variable para
añadirlos de la forma:
items: [elemento]
items: [elemento1, elemento2]
Pero Sencha Touch también incluye la posibilidad de crear estos elementos en línea, lo cual
será mucho más rápido y nos ahorrará código. Para ello tendremos que especificar
directamente las opciones del objeto a crear entre llaves {} , de la forma:
27
Array de items y el atributo xtype
Ext.application({
name : 'MiApp',
launch : function() {
Ext.create('Ext.Container', {
fullscreen: true,
layout: 'fit',
items: [
{
xtype: 'panel',
html: 'Este panel se ha creado mediante xtype'
},
{
xtype: 'toolbar',
title: 'Mi App',
docked: 'top'
}
]
});
}});
Componentes generales:
xtype Class
actionsheet Ext.ActionSheet
audio Ext.Audio
button Ext.Button
component Ext.Component
container Ext.Container
image Ext.Img
label Ext.Label
loadmask Ext.LoadMask
map Ext.Map
mask Ext.Mask
media Ext.Media
panel Ext.Panel
segmentedbutton Ext.SegmentedButton
28
Array de items y el atributo xtype
sheet Ext.Sheet
spacer Ext.Spacer
title Ext.Title
titlebar Ext.TitleBar
toolbar Ext.Toolbar
video Ext.Video
carousel Ext.carousel.Carousel
carouselindicator Ext.carousel.Indicator
navigationview Ext.navigation.View
datepicker Ext.picker.Date
picker Ext.picker.Picker
pickerslot Ext.picker.Slot
slider Ext.slider.Slider
thumb Ext.slider.Thumb
tabbar Ext.tab.Bar
tabpanel Ext.tab.Panel
tab Ext.tab.Tab
viewport Ext.viewport.Default
xtype Class
dataview Ext.dataview.DataView
list Ext.dataview.List
listitemheader Ext.dataview.ListItemHeader
nestedlist Ext.dataview.NestedList
dataitem Ext.dataview.component.DataItem
29
Array de items y el atributo xtype
xtype Class
checkboxfield Ext.field.Checkbox
datepickerfield Ext.field.DatePicker
emailfield Ext.field.Email
field Ext.field.Field
hiddenfield Ext.field.Hidden
input Ext.field.Input
numberfield Ext.field.Number
passwordfield Ext.field.Password
radiofield Ext.field.Radio
searchfield Ext.field.Search
selectfield Ext.field.Select
sliderfield Ext.field.Slider
spinnerfield Ext.field.Spinner
textfield Ext.field.Text
textareafield Ext.field.TextArea
textareainput Ext.field.TextAreaInput
togglefield Ext.field.Toggle
urlfield Ext.field.Url
fieldset Ext.form.FieldSet
formpanel Ext.form.Panel
30
Añadir componentes a contenedores
Ext.application({
name : 'MiApp',
launch : function() {
var secondPanel = Ext.create('Ext.Panel', {
html: 'Segundo panel'
});
En este caso le asignamos el layout hbox al panel principal contenedor para que los
paneles hijos se vayan añadiendo de forma horizontal. Además se utilizan un par de
propiedades nuevas: style , la cual nos permite escribir código CSS para aplicar estilos a
un componente y defaults , que nos permite establecer valores por defecto que se
aplicarán a todos los componentes que contenta. En este caso al primer panel se le
asignará un flex de 1, por lo que ocupará todo el ancho, pero al añadir el segundo panel
también se le asignará un flex de 1 por lo que el ancho se repartirá y cada panel ocupará
la mitad del espacio disponible (en la sección de layouts se tratará este tema más en
profundidad).
31
Eliminar componentes de un contenedor
mainPanel.remove(secondPanel);
32
Mostrar y ocultar componentes
principal haríamos:
mainPanel.show();
mainPanel.hide();
33
Eventos
Eventos
Todos los componentes de Sencha Touch lanzan eventos ante determinados cambios,
estos eventos pueden ser escuchados y realizar una acción cuando son activados. Por
ejemplo, al escribir en un campo de texto este lanza su evento change , por lo que
podríamos escuchar a dicho evento usando un listener como se muestra en el siguiente
ejemplo:
Ext.create('Ext.form.Text', {
label: 'Name',
listeners: {
change: function(field, newValue, oldValue) {
// El contenido ha cambiado
}
}
});
34
Destruir componentes
Destruir componentes
Cuando no se va a necesitar más un elemento se recomienda eliminarlo completamente
para ahorrar memoria. Hemos de tener en cuenta que en los dispositivos móviles la
memoria es un recurso escaso y si nuestra aplicación es muy grande puede llegar a
ralentizar el móvil. Por este motivo se ha introducido el método destroy que elimina el
componente que lo llame:
mainPanel.destroy();
Este comando eliminaría el mainPanel del DOM y además eliminaría todos los listeners que
estuvieran escuchando a sus eventos. Hemos de tener cuidado ya que también se eliminará
todo el contenido del elemento, por ejemplo si es un contenedor se eliminarían sus paneles
hijos.
35
Layouts
Layouts
Los layouts se utilizan para especificar las dimensiones y posicionamiento de los
componentes en una aplicación. Por ejemplo, en un aplicación de correo en general se
colocarán dos paneles una a continuación del otro en horizontal, el de la izquierda para lista
de mensajes ocupando un tercio del ancho y el de la derecha para la previsualización
ocupando el resto del espacio.
A continuación se analizarán los diferentes tipos de layouts que incorpora Sencha Touch:
hbox
vbox
card
fit
36
Layout tipo hbox
Además, para especificar el espacio que han de ocupar los componentes dentro del
contenedor podemos utilizar la propiedad flex , la cual indica la proporción de espacio que
ocupará un componente. Al indicar el espaciado de una serie de componentes con flex
no es necesario que sumen 100, sino que se sumará el total de las cantidades y ese será el
100% del espacio. Por ejemplo, para conseguir una columna que ocupe 1/3 y otra de 2/3
especificaríamos los siguientes valores para la propiedad flex:
Para conseguir una disposición como la de la imagen tendríamos que indicar el layout tipo
hbox al contenedor padre y establecer el atributo flex de cada hijo de la forma:
Ext.create('Ext.Container', {
fullscreen: true,
layout: 'hbox',
items: [
{
xtype: 'panel',
html: 'Columna que ocupa 1/3 del ancho.',
flex: 1,
style: 'background-color: #5E99CC;'
},
{
xtype: 'panel',
html: 'Columna que ocupa 2/3 del ancho.',
flex: 2,
style: 'background-color: #759E60;'
}
]
});
37
Layout tipo hbox
38
Layout tipo vbox
El código para crear una pantalla de este tipo sería idéntico al utilizado en el ejemplo
anterior pero cambiando el tipo de layout hbox por vbox:
Ext.create('Ext.Container', {
fullscreen: true,
layout: 'vbox',
items: [
{
xtype: 'panel',
html: 'Fila que ocupa 1/3 del alto.',
flex: 1
},
{
xtype: 'panel',
html: 'Fila que ocupa 2/3 del alto.',
flex: 2
}
]
});
39
Layout tipo card
A continuación se incluye un ejemplo de un panel con un layout tipo card que contiene
cuatro tarjetas:
panel.setActiveItem(1);
40
Layout tipo card
Al añadir las cartas del layout las podemos crear directamente en el array de items, como
en el ejemplo, o añadirlas posteriormente con el método add del panel (como ya se vio en
una sección anterior).
41
Layout tipo fit
Por ejemplo, si tenemos un contenedor de 200px de ancho por 200px de alto y le añadimos
un hijo y el layout tipo fit, el componente añadido será expandido para ocupar el mismo
tamaño que el padre:
42
Docking o acoplamiento
Docking o acoplamiento
Todos los layouts tienen la capacidad de acoplar elementos "adicionales" de forma fija en
cualquiera de sus laterales. Al acoplar un elemento el resto de contenido del layout será
redimensionado para adaptarse. Las posiciones en las que se puede acoplar un elemento
son: top, right, bottom, o left.
Es importante notar que los elementos acoplados son "adicionales" al contenido del layout,
es decir, si por ejemplo acoplamos un elemento a un contenedor con un layout tipo hbox, el
elemento acoplado no seguirá la alineación horizontal, sino que se pondrá en la posición
que se especifique con docked de entre la lista de posiciones permitidas.
En la imagen superior tenemos un layout tipo hbox con dos columnas y un elemento
acoplado en la parte superior. A continuación se incluye el código para crear una disposición
de este tipo:
43
Docking o acoplamiento
Ext.create('Ext.Container', {
fullscreen: true,
layout: 'hbox',
items: [
{
docked: 'top',
xtype: 'panel',
height: 20,
html: 'Elemento acoplado en la parte superior.'
},
{
xtype: 'panel',
html: 'Columna izquierda.',
flex: 1
},
{
xtype: 'panel',
html: 'Columna derecha.',
flex: 2
}
]
});
Podemos acoplar tantos elementos como queramos, los cuales se irán añadiendo en el
mismo orden en el que se asignen.
44
Pack y align
Pack y Align
Las características pack y align del layout sirven para controlar la alineación de los
elementos hijos dentro de un contenedor:
Pack: es la alineación en el mismo eje de alineación del layout utilizado. Por ejemplo,
en un layout tipo hbox sería el horizontal y en uno tipo vbox el vertical. Se pueden
asignar tres posibles valores: start, center y end.
Align: es la alineación en el eje perpendicular al de la alineación del layout utilizado. Por
ejemplo, en un layout del tipo hbox será la vertical y en uno del tipo vbox la horizontal.
Puede tener cuatro posibles valores: start, center, end y stretch.
Ext.create('Ext.Panel', {
fullscreen: true,
layout: {
type: 'hbox',
align: 'center',
pack: 'center'
},
items: {
xtype: 'panel',
html: 'Contenido centrado.'
}
});
45
Transiciones de cambio de vista
El panel que se verá al principio es el primero que se añade a la lista de items, quedando el
otro (o los otros) ocultos. En la siguiente imagen se puede ver un esquema del intercambio
de paneles (en nuestro ejemplo con dos paneles). El panel asignado al "viewport" (o
contenedor base) queda invisible por detrás y mediante un botón podemos pasar de un
panel a otro:
46
Transiciones de cambio de vista
A continuación se incluye el código para el "panel1". Un simple panel con una barra de
herramientas en la parte superior que contiene un botón. Lo más importante aquí es la
función "handler" del botón, en la cual llamamos a panelPrincipal.setActiveItem (función
explicada a continuación) para cambiar al "panel2". El código para el "panel2" sería
exactamente igual, pero cambiando la variable, el html y la función handler.
Es importante destacar que en este ejemplo hemos hecho referencia al panel base
( panelPrincipal ) y al panel a cambiar ( panel2 ) mediante su nombre de variable, pero
también podríamos haberle asignado un identificador y haberlo obtenido con
Ext.getCmp(id) .
Método setActiveItem
La función setActiveItem( Object/Number item ) permite cambiar entre el panel activo o
visible por otro panel indicado. La forma de indicar el panel puede ser mediante su número
en el array de paneles del contenedor (empezando por cero) o mediante el propio objeto a
mostrar.
Método animateActiveItem
La función animateActiveItem( Object/Number item, atributos_animación ) funciona igual
que setActiveItem , permite cambiar el panel actual por otro panel indicado mediante su
posición en el array de items (empezando por cero) o mediante una referencia al propio
objeto. Además, esta función permite definir la animación que se realizará al intercambiar
los paneles. Los tipos de animaciones que podemos utilizar son:
47
Transiciones de cambio de vista
fade: difumina el panel actual, fundiéndolo con el panel de destino, hasta completar la
transición.
pop: realiza una especie de animación 3D. Escala el panel actual minimizándolo hasta
ocultarlo, mientras que aumenta el tamaño del panel a visualizar.
slide: realiza un desplazamiento para intercambiar un panel por otro, podemos indicar
una dirección: left, right, up, down (por ejemplo: direction: 'left').
Para todos ellos podemos definir una duración en milisegundos (por ejemplo "duration:
2000").
panelPrincipal.animateActiveItem(
panel2,
{type: 'slide', direction: 'up', duration: 2000});
48
Componentes
Componentes
En esta sección se introducen los principales componentes que incorpora Sencha Touch y
que podemos utilizar para la elaboración de las pantallas de una aplicación. Algunos de
estos componentes son:
Toolbars
Botones
TabPanel
Carousel
Diálogos
Formularios
49
Toolbars
Toolbars
Hasta ahora hemos visto como utilizar paneles y contenedores ( Ext.Panel y
Ext.Container ), en esta sección vamos a ver como añadir barras de herramientas dentro
de estos elementos.
Para añadir barras de herramientas a un panel tenemos dos opciones, igual que para otro
tipo de componentes, estas son:
Crear la barra de herramientas de forma separada usando el constructor " var toolbar
= Ext.create('Ext.Toolbar', { ... }); " y posteriormente añadirla al panel en su
En ambos casos, dentro del constructor podemos usar los siguientes atributos:
50
Toolbars
51
Botones
Botones
Los botones se añaden a las barras de herramientas ( Toolbar ) u otro tipo de componentes
mediante su propiedad items . La forma de construir un botón, igual que para otros
componentes, son dos:
En el siguiente ejemplo se crea una barra de herramientas con dos botones, uno creado de
forma inline y otro de forma separada:
ui: 'normal'
ui: 'back'
ui: 'forward'
ui: 'round'
52
Botones
ui: 'action'
ui: 'confirm'
ui: 'decline'
Además podemos usar los modificadores "-small" y "-round" sobre los tipos de botón
"action", "confirm" y "decline" para obtener botones más pequeños o redondeados:
Si queremos variar el ancho de un botón podemos utilizar la propiedad " width: '200px' "
en píxeles o " width: '95%' " indicando porcentajes.
Iconos
También podemos usar algunos iconos predefinidos indicando el nombre del icono
mediante la propiedad "iconCls: 'nombre'", de la forma:
53
Botones
Opcionalmente podemos cambiar el color de estos botones con iconos mediante los tipos
(ui) de 'action', 'decline' o 'confirm', obteniendo:
Imágenes externas
Si queremos usar una imagen externa tenemos que aplicar al botón un estilo CSS. El botón
lo definimos de forma normal, pero utilizamos su propiedad cls para indicar el nombre del
estilo:
El estilo "btnAyuda" lo tendremos que definir indicando la imagen de fondo a usar junto con
el ancho y el alto del botón. El tamaño de la imagen que usemos deberá de coincidir con el
tamaño aplicado al botón para que no se vea recortado. El tamaño habitual de un botón es
de 45x35 píxeles. Además es imprescindible añadir en el CSS la propiedad !important al
cargar la imagen de fondo. Esto es debido a que Sencha Touch sobrescribe algunos estilos
y tenemos que aplicar esta propiedad para que prevalezca el nuestro:
.btnAyuda {
background: url(resources/imgs/ayuda.png) !important;
width: 45px;
height: 35px;
}
Badges
De forma sencilla podemos añadir una insignia distintiva a los botones para destacar alguna
información. Para esto utilizamos la propiedad "badgeText: '2'", que daría como resultado:
54
Botones
Alineaciones
Por defecto los botones aparecerán alineados a la izquierda del contenedor. Para crear
otras alineaciones utilizaremos un espaciador "{ xtype: 'spacer' }". Este espaciador es un
componente no visible que ocupará todo el espacio libre disponible, por lo tanto si lo
asignamos a la izquierda de un botón lo "empujará" a la derecha, y si añadimos dos
espaciadores, uno a cada lado de un botón, lo centrará. En el siguiente código podemos ver
diferentes ejemplos de alineaciones:
// Alineación derecha
items: [
{ xtype: 'spacer' },
{ xtype: 'button', ui: 'normal', text: 'Botón' }
]
// Alineación centrada
items: [
{ xtype: 'spacer' },
{ xtype: 'button', ui: 'normal', text: 'Botón' },
{ xtype: 'spacer' }
]
Acciones
Para añadir acciones a los botones tenemos que definir su propiedad "handler", a la cual le
asignaremos una función. Esta función la podemos definir en línea, de la forma handler:
function () { ... } , o creando una función independiente para separar mejor el código,
como en el ejemplo:
55
Botones
56
TabPanel
TabPanel
Un TabPanel es similar a un panel con un layout tipo card, pero al que se ha añadido
además la funcionalidad de mostrar automáticamente una barra de herramientas con la lista
de tabs que contiene y que nos permitirá cambiar entre ellos.
Esta barra de tabs se puede posicionar en la parte superior (top) o en la inferior (bottom) del
panel, y opcionalmente se le puede asignar un título e iconos a los botones.
En el siguiente ejemplo se muestra un TabPanel con los tabs en la parte inferior, además se
le han añadido iconos:
Ext.application({
name: 'MiApp',
launch: function() {
Ext.create('Ext.TabPanel', {
fullscreen: true,
tabBarPosition: 'bottom',
defaults: {
styleHtmlContent: true
},
items: [
{
title: 'Home',
iconCls: 'home',
html: 'Home Screen'
},
{
title: 'Contact',
iconCls: 'user',
html: 'Contact Screen'
}
]
});
}
});
57
TabPanel
Ext.application({
name: 'MiApp',
launch: function() {
Ext.create('Ext.TabPanel', {
fullscreen: true,
defaults: {
styleHtmlContent: true
},
items: [
{
title: 'Home',
html: 'Home Screen'
},
{
title: 'Contact',
html: 'Contact Screen'
}
]
});
}
});
58
TabPanel
Animaciones en un TabPanel
Los TabPanel realizan el cambio de panel automáticamente (no tenemos que escribir
código para esto) y tienen asignada la animación tipo slide por defecto. Si queremos
podemos cambiarla por cualquier otra (podemos usar las mismas que en un card layout) de
la forma:
59
TabPanel
Ext.application({
name: 'MiApp',
launch: function() {
Ext.create('Ext.TabPanel', {
fullscreen: true,
defaults: {
styleHtmlContent: true
},
layout: {
type: 'card',
animation: {
type: 'fade'
}
},
items: [
{
title: 'Home',
html: 'Home Screen'
},
{
title: 'Contact',
html: 'Contact Screen'
}
]
});
}
});
60
Carousel
Carousel
El Carousel es un contenedor de paneles que nos permite cambiar entre ellos simplemente
arrastrando el dedo. Solo se muestra un panel en cada momento junto con un pequeño
indicador con puntos que referencia el número de paneles disponibles.
Es muy sencillo configurarlo, en su sección ítems tenemos que definir cada uno de los
paneles. Si queremos que se utilicen los estilos HTML básicos tenemos que activar la
opción defaults: { styleHtmlContent: true } , como en el siguiente ejemplo:
61
Carousel
Dentro de los items de un carousel podemos añadir cualquier tipo de componente de entre
los disponibles en Sencha Touch.
Una opción interesante de configuración es la orientación del panel, que básicamente lo que
hace es cambiar la posición de los puntos y la dirección de movimiento de los paneles. Para
configurarlo usamos la propiedad direction: 'horizontal' (por defecto) o direction:
'vertical' .
62
Diálogos
Pero para que funcionen los avisos tenemos que precargar la clase Ext.MessageBox en el
atributo requires de la aplicación (o de la clase o componente que lo utilice):
Ext.application({
name: 'MiApp',
requires: ['Ext.MessageBox'],
launch: function() {
...
}
});
Alertas
Muestra un mensaje de aviso con un solo botón OK, como podemos ver en la imagen
siguiente:
63
Diálogos
En este caso usamos la función vacía Ext.emptyFn para que no se ejecute nada. En su
lugar podríamos haber puesto directamente el nombre de una función a llamar.
Confirmación
Este mensaje de aviso nos da la opción de aceptar o rechazar, como podemos ver en la
siguiente imagen:
function myFunction(btn)
{
if( btn == "yes" )
Ext.Msg.alert( "¡Ha pulsado sí! :D" );
else
Ext.Msg.alert( "Ha pulsado no :(" );
}
64
Diálogos
65
Formularios
Formularios
Para crear formularios utilizamos el constructor Ext.create('Ext.form.Panel', { ... }); , el
cual se comporta exactamente igual que un panel, pero permitiendo añadir fácilmente en el
array " items " campos de tipo formulario. En el siguiente ejemplo se crea un formulario que
contiene un campo de texto y un área de texto:
Ext.application({
name: 'MiApp',
launch: function() {
Ext.create('Ext.form.Panel', {
fullscreen: true,
items: [
{
xtype: 'textfield',
name: 'title',
label: 'Title',
required: true
},
{
xtype: 'textareafield',
name: 'narrative',
label: 'Narrative'
}
]
});
}
});
Tipos de campos
Para todos los campos podemos especificar un nombre " name ", una etiqueta " label " y
si es requerido " required: true " (esta propiedad solo es visual, añade un asterisco ( * ) en
el nombre del campo, pero no realiza ninguna validación).
El nombre ( name ) se utiliza para cargar y enviar los datos del formulario (como veremos
más adelante), y la etiqueta ( label ) se mostrará visualmente en la parte izquierda de cada
campo. El valor de todos los campos se encuentra en su atributo " value ", el cual también
podemos utilizarlo para especificar un valor inicial.
Los principales tipos de campos que podemos utilizar son los siguientes (indicados según
su nombre " xtype " en negrita):
66
Formularios
urlfield: campo de texto para direcciones Web, incluye validación de URL correcta:
togglefield: permite seleccionar entre dos valores (0 ó 1). Por defecto se encuentra
desactivado, para activarlo por defecto tenemos que añadir " value:1 " a la definición
del campo:
67
Formularios
datepickerfield: campo para seleccionar fechas. Al pulsar sobre el campo aparece una
ventana en la que podemos seleccionar fácilmente una fecha. Podemos indicarle una
fecha inicial utilizando " value: {year: 1989, day: 1, month: 5} ":
items: [{
xtype: 'fieldset',
title: 'About Me',
items: [
{ xtype: 'textfield', name : 'firstName', label: 'First Name' },
{ xtype: 'textfield', name : 'lastName', label: 'Last Name'}
]
}]
68
Formularios
selectfield: campo desplegable para seleccionar entre una lista de valores. Las
posibles opciones se indican en la propiedad " options " como un array. Para cada
opción tenemos que indicar sus valores text (texto que se mostrará) y value (valor
devuelto para la opción seleccionada).
items:[{
xtype: 'selectfield',
label: 'Select',
options: [
{text: 'First Option', value: 'first'},
{text: 'Second Option', value: 'second'},
{text: 'Third Option', value: 'third'}
]
}]
checkboxfield: el campo checkbox nos permite elegir uno o varios elementos de una
lista. Cada campo de la lista se tiene que declarar como un item independiente, pero
todos ellos deben de tener el mismo nombre " name " para poder ser agrupados (muy
importante para posteriormente poder recoger los datos correctamente). Además
podemos utilizar la propiedad " checked: true " para que aparezcan marcados
inicialmente:
69
Formularios
items: [
{
xtype: 'checkboxfield',
name : 'check_color', // Nombre del grupo
value: 'red',
label: 'Red',
checked: true
}, {
xtype: 'checkboxfield',
name : 'check_color',
value: 'green',
label: 'Green'
}, {
xtype: 'checkboxfield',
name : 'check_color',
value: 'blue',
label: 'Blue'
}
]
radiofield: el campo de tipo "radio" nos permite elegir solo un elemento de una lista.
Cada campo de la lista se tiene que declarar como un item independiente, pero todos
ellos deben de tener el mismo nombre " name " para poder ser agrupados (muy
importante para posteriormente poder recoger los datos correctamente). Además
podemos utilizar la propiedad " checked: true " en uno de ellos para que aparezca
marcado inicialmente:
70
Formularios
items: [
{
xtype: 'radiofield',
name : 'radio_color', // Nombre del grupo
value: 'red',
label: 'Red',
checked: true
}, {
xtype: 'radiofield',
name : 'radio_color',
value: 'green',
label: 'Green'
}, {
xtype: 'radiofield',
name : 'radio_color',
value: 'blue',
label: 'Blue'
}
]
Con lo que obtendríamos un resultado similar a (en la imagen se han agrupado además
dentro de un fieldset):
71
Almacenamiento
Almacenamiento
En esta sección vamos a ver las opciones que tenemos para trabajar con los datos de una
aplicación. Para esto Sencha Touch pone a nuestra disposición varias herramientas:
Data Model: Nos permitirá representar las entidades de datos como colecciones de
campos con sus datos asociados.
Data Store: Son colecciones de datos de un modelo. Además nos permitirá realizar otro
tipo de operaciones más avanzadas como ordenar, filtrar, agrupar o lanzar eventos.
Proxy: Permiten cargar y almacenar los datos desde una fuente de datos remota o local
en un data model o un data store.
Además también se estudiarán varias formas de utilizar estos datos en una aplicación, por
ejemplo en listados, data views o en formularios. A continuación veremos en detalle cada
uno de estos apartados.
72
Data Model
Data Model
Los Data Model nos permiten representar las entidades de datos, junto con sus validaciones
de formato y las relaciones con otros modelos, como si estos fuera objetos o clases. Por
comparación podríamos pensar que un Data Model es como una clase (en POO) con la
definición de los datos que la componen y funciones para poder validar y trabajar con esos
datos.
A continuación podemos ver un esquema de todas las opciones que agrupa un Data Model,
las cuales iremos viendo se las siguientes secciones:
Definir un modelo
73
Data Model
En este ejemplo hemos creado el modelo User con cinco campos. Por defecto, en todos
los modelos se asigna como identificador principal el campo id , el cual se utiliza para
varias cosas (como saber si el modelo ha sido guardado). Si queremos variar el campo
identificador podemos hacerlo con idProperty: 'id' .
En la sección fields se definen el resto de campos que componen el modelo. Para cada
campo podemos utilizar:
Cuando se asigna el tipo auto o no se asigna ningún tipo el campo podrá obtener cualquier
tipo de valor y no realizará ninguna validación de formato.
74
Data Model
queremos crear una instancia y un objeto con los valores a asignar a la instancia. Por
ejemplo:
var ed = Ext.create('User', {
usuario: 'ajgallego',
nombre: 'Javier Gallego',
genero: 'Masculino',
activo: true
});
Validaciones
Los modelos de datos incluyen soporte para realizar validaciones, las cuales las deberemos
de incluir dentro de la misma clase a continuación del campo fields . Por ejemplo:
Ext.define('User', {
extend: 'Ext.data.Model',
config: {
fields: [
// ...
],
validations: [
{ type: 'presence', field: 'nombre' },
{ type: 'length', field: 'nombre', min: 5 },
{ type: 'format', field: 'usuario', matcher: /([a-z]+)[0-9]{2,3}/},
{ type: 'inclusion', field: 'genero', list: ['Masculino', 'Femenino'] },
{ type: 'exclusion', field: 'usuario', list: ['admin'] }
]
}
});
75
Data Model
http://docs.sencha.com/touch/2.4/core_concepts/data/models.html
76
Data Model
En estos casos, en los que creemos nuevas definiciones, estaremos obligados a cargarlos
por separado. Para ello Sencha Touch incorpora un potente sistema que nos permite
modularizar nuestro código siguiente el patron MVC muy fácilmente. Solamente tendremos
que seguir los siguientes pasos:
Crear una fichero con el mismo nombre que el modelo dentro de la carpeta app/model .
Si por ejemplo nuestro modelo se llama User el fichero se tendrá que llamar User.js .
El contenido del fichero del modelo seguirá la sintaxis que hemos visto hasta ahora
pero añadiendo el espacio de nombres <nombre-app>.model.<nombre-modelo> al nombre
del modelo, por ejemplo si nuestra aplicación se llamase MyApp y el modelo a crear
User , el modelo tendría que quedar como el siguiente:
Por último tendremos que decirle a nuestra aplicación que cargue la definición del
modelo para que podamos utilizarla. Para esto simplemente tenemos que hacer:
Ext.application({
name: 'MyApp',
models:['User'],
launch: function() { ... }
});
A la hora de cargar el modelo podemos indicarlo usando solamente el nombre del modelo o
usando el espacio de nombres completo ( MyApp.model.User ). Pero a la hora de utilizarlo
(por ejemplo, para crear una instancia del modelo o asociarlo a un store) tendremos que
usar la ruta del espacio de nombres completa.
77
Data Store
Data Store
Los almacenes de datos (data store) se utilizan para encapsular o almacenar una colección
de instancias de un modelo determinado. Además disponen de funciones para ordenar,
filtrar y consultar los datos. De forma opcional podemos indicar que utilicen un proxy para
sincronizar estos datos con un almacén local o remoto.
En esta sección nos vamos a centrar en las características para gestionar un store: añadir,
ordenar, filtrar, buscar y eliminar. En la siguiente sección sobre proxies veremos como hacer
persistentes estos datos.
Añadir datos
Podemos añadir datos directamente junto a la definición de un Store, solo tenemos
insertarlos como un array a través de su propiedad "data". Suponiendo que el modelo
"User" solo tuviera dos campos (id, name), podríamos añadir datos de la forma:
O también podemos añadir datos posteriormente llamando a la función " add " del objeto:
78
Data Store
Ext.create('Ext.data.Store', {
model: 'User',
sorters: [
{ property: 'usuario', direction: 'DESC' }
],
filters: [
{ property: 'genero', value: 'Femenino' }
]
});
Buscar registros
En algunos casos antes de añadir un registro será necesario comprobar si el registro está
repetido. Para esto podemos utilizar el método findRecord(campo, valor) del Store, el cual
devuelve el registro encontrado o null en caso de no encontrar ninguna coincidencia. En el
siguiente ejemplo se compara el campo id de los datos del Store, con el campo id del
registro a añadir:
Otra opción para buscar registros es la función find(campo, valor) la cual devuelve el
índice del registro encontrado (o -1 en caso de no encontrarlo), y posteriormente podríamos
llamar a getAt(index) para obtener los datos.
Eliminar registros
Para eliminar un registro de un Store usaremos la función remove(registro) , por ejemplo:
myStore.remove( registro );
79
Data Store
store.sync();
miListado.refresh();
80
Proxy
Proxy
Los proxies se utilizan para definir la forma de leer y escribir la información. Dependiendo
del proxy que utilicemos podremos almacenar los datos de forma local o de forma remota.
Además, estos pueden ser definidos tanto en el data store como en el data model.
Almacenamiento en local:
Ajax (ajax): Genera peticiones ajax para cargar o enviar los datos a un servidor.
JsonP (jsonp): JSONP o JSON con padding es una técnica de comunicación
utilizada para realizar llamadas asíncronas a dominios diferentes y de esta forma
evitar los errores por peticiones cross-domain.
Rest (rest): Especialización del proxy ajax para realizar peticiones a un servidor
RESTful.
81
Proxy
Además hemos añadido la propiedad autoLoad: true para que se carguen los datos al
inicio desde el proxy indicado. Si no lo hiciéramos así el store inicialmente estaría vacío,
aunque también podríamos cargarlos utilizando el método myStore.load() .
La propiedad reader especifica la forma de codificar / decodificar los datos. En este caso
se ha especificado el tipo json, pero Sencha Touch admite también el tipo xml.
Al cargar la aplicación con el store del ejemplo se auto-descargarían los datos desde la URL
indicada. A continuación, y según tiene definido el store, espera recibir un objeto JSON con
un array de datos con los mismos campos que el modelo User asociado. El formato de los
datos en JSON debería ser similar al siguiente:
{
success: true,
users: [
{ id: 1, name: 'Greg' },
{ id: 2, name: 'Seth' }
]
}
En caso de que la raíz de los datos del array fuese distinta podríamos indicarlo
configurando el reader de la forma:
...
proxy: {
type: 'ajax',
url : 'users.json',
reader: {
type: 'json',
root: 'usersList'
}
}
82
Proxy
El proxy también se puede definir directamente en el modelo de datos, lo cual tiene dos
beneficios. Primero, si el modelo se utiliza en varios stores solo se tendrá que especificar
una vez la configuración del proxy. Y segundo, podremos cargar datos y guardarlos sin
necesidad de referenciar el store.
En el siguiente ejemplo se puede ver como definir un proxy directamente en el modelo (las
opciones de configuración son exactamente las mismas que si lo hiciéramos en el store):
Ext.define('User', {
extend: 'Ext.data.Model',
config: {
fields: ['id', 'name', 'age', 'gender'],
proxy: {
type: 'rest',
url : 'data/users',
reader: {
type: 'json',
root: 'users'
}
}
}
});
En este otro ejemplo se muestra como guardar y cargar datos directamente desde el
modelo que hemos creado en el ejemplo anterior:
83
Proxy
// Crear un usuario
var item = Ext.create('User', {
name: 'Bilbo Bolsón',
age : 111
});
Al usar almacenamiento en local es muy importante que el modelo de datos tenga un "id"
único, que por defecto tendrá ese nombre de campo (id); en caso de utilizar otro lo
podríamos indicar mediante la propiedad " idProperty: 'myID' " del modelo.
Para que el proxy utilice el almacenamiento en local simplemente tendremos que indicar el
tipo ( type: 'localstorage' ) y un identificador ( id , usado como clave para guardar los
datos). En el siguiente ejemplo se crea un almacén de datos para el modelo "User" que
hemos definido en las secciones anteriores.
84
Proxy
85
Componentes asociados a datos
En primer lugar analizaremos las "plantillas", las cuales nos permitirán indicar la disposición
(o vista) de los datos de un almacenamiento, y seguidamente veremos tres componentes
que podremos utilizar para mostrar los datos de un almacenamiento, estos son:
DataViews
Listados
Formularios
86
Plantillas
Plantillas
Las plantillas se utilizan para describir la disposición y la apariencia visual de los datos de
nuestra aplicación. Nos proporcionan funcionalidad avanzada para poder procesarlos y
darles formato, como: auto-procesado de arrays, condiciones, operaciones matemáticas,
ejecución de código en línea, variables especiales, funciones, etc.
Para instanciar un template utilizamos el constructor " Ext.XTemplate( template ) ", donde
template será una cadena con la definición del template a utilizar. Posteriormente podemos
utilizar la función " overwrite(elemento, datos) " del template para aplicar un template con
unos datos sobre un elemento dado. En la sección de "Visualización de datos" se detalla
otra forma de aplicar un template en un panel.
Auto-procesado de arrays
Para crear un template que procese automáticamente un array se utiliza la etiqueta <tpl
for="variable">plantilla</tpl> , teniendo en cuenta que:
87
Plantillas
var myData = {
name: 'Tommy Maintz',
drinks: ['Agua', 'Café', 'Leche'],
kids: [
{ name: 'Tomás', age:3 },
{ name: 'Mateo', age:2 },
{ name: 'Salomón', age:0 }
]
};
myTpl.overwrite(myPanel.element, myData.kids);
myTpl.overwrite(myPanel.element, myData);
Si el array solo contiene valores (en el objeto de datos de ejemplo, sería el array "drinks"),
podemos usar la variable especial {.} dentro del bucle para obtener el valor actual:
myTpl.overwrite(myPanel.element, myData);
Condiciones
88
Plantillas
" > " o las "comillas" deberemos escribirlos codificados: < , > o " , y que si
usamos los operadores " elseif " o " else " tendremos que cerrar la plantilla al final.
Ejemplos:
Visualización
Para renderizar el contenido de una plantilla sobre un panel (u otro elemento que lo soporte,
como veremos más adelante), podemos usar la función " tpl.overwrite(elemento, datos) "
que ya hemos usado en los ejemplos anteriores. O usar la propiedades " tpl " junto con
" data ", de la forma:
// O también:
// myTpl.overwrite(myPanel.element, myData);
89
DataViews
Data Views
Los Data Views nos permiten mostrar datos de forma personalizada mediante el uso de
plantillas y opciones de formato. Principalmente se utilizan para mostrar datos provenientes
de un store y aplicarles formato utilizando las plantillas " Ext.XTemplate ", como hemos visto
en la sección anterior. Además también proporcionan mecanismos para gestionar eventos
como: click, doubleclick, mouseover, mouseout, etc., así como para permitir seleccionar los
elementos mostrados (por medio de un "itemSelector").
En este otro ejemplo creamos un DataView para mostrar el contenido de un store que
incluimos dentro de la propia clase con un array de datos interno. Además utilizamos la
propiedad itemTpl el lugar de tpl , lo que nos permite indicar el template de cada
elemento o item del array directamente.
Esta "vista de datos" podemos mostrarla en nuestra aplicación tal cual la hemos creado o
también podemos añadirla a la sección items de un panel:
90
DataViews
91
Listados
Listados
Permiten mostrar datos en forma de listado a partir de una plantilla por defecto de tipo lista.
Estos datos se obtienen directamente de un "store" y se mostrarán uno a uno en forma de
listado según la plantilla definida en " itemTpl ". Además incorpora funcionalidades para
gestionar eventos como: itemtap, itemdoubletap, containertap, etc.
Utilizarlo es muy simple, solo tenemos que definir el "store" que queremos utilizar y la
plantilla para cada uno de los elementos con " itemTpl ", por ejemplo:
En el ejemplo anterior hemos creado el store a utilizar directamente dentro de la lista con un
array de datos interno, pero también podríamos crear el store de datos por separado y
conectarlo alguna fuente de datos local o remota (como se vio en la sección
correspondiente):
92
Listados
Es muy importante diferenciar " itemTpl " de la propiedad " tpl " que ya habíamos visto (en
las que usábamos los XTemplate ). En " itemTpl " se procesa cada elemento del listado
individualmente. Otra diferencia es que tenemos que utilizar como separador para la
concatenación el símbolo de unión "+" y no la coma ",".
En el "store" debemos de utilizar la propiedad " sorters " para ordenar el listado, pues sino
nos aparecerá desordenado. Por ejemplo, podríamos indicar (en el "store") que se ordene
por el apellido " sorters: 'lastName' ".
Una vez obtenido ya podemos realizar operaciones sobre él como añadir, modificar o
eliminar algún registro (consultar sección correspondiente).
93
Listados
Actualizar datos
Si modificamos el almacén de datos asociado con el listado tendremos que actualizarlo para
que se visualicen correctamente los nuevos datos en el listado. En primer lugar llamaremos
al método sync() del store para sincronizar los cambios. A continuación, si es necesario,
ordenamos los datos (pues el registro se habrá añadido al final). En el ejemplo se ordenan
de forma descendente por fecha. Por último llamamos al método refresh() del listado para
actualizar la vista.
notesStore.add( registro );
notesStore.sync();
notesStore.sort([{ property: 'date', direction: 'DESC'}]);
myList.refresh();
Agrupar elementos
Una propiedad muy útil que nos ofrecen los listados es la posibilidad de agrupar los
elementos (como podemos ver en la imagen inferior). Para esto activaremos la propiedad
" grouped: true " del listado y opcionalmente podremos indicar que se muestre una barra
lateral de navegación " indexBar: true ".
Pero para que esta propiedad funcione correctamente tendremos que indicar dentro del
store la forma de agrupar los elementos. Tenemos dos opciones:
94
Listados
groupField: 'campo' - para agrupar por un campo (por ejemplo: elementos de género
masculino y femenino).
opción es mucho más avanzada y nos permitirá agrupar, por ejemplo, usando la
primera letra del apellido (como se muestra en la imagen de ejemplo).
Acciones
Para añadir acciones al presionar sobre un elemento del listado tenemos varias opciones:
itemtap : permite realizar una acción al presionar sobre un elemento de la barra. Este
evento lo debemos definir dentro de la sección " listeners " de nuestro Ext.List , de
la forma:
listeners: {
itemtap: function(view, index, target, record) {
alert( "tap on" + index );
}
}
95
Listados
Donde el parámetro record representa el objeto sobre el que se ha pulsado. Este valor
lo podríamos aprovechar para cargarlo directamente en un formulario o realizar alguna
operación con él.
itemdoubletap : permite realizar una acción al presionar dos veces consecutivas sobre
un elemento. Este evento lo debemos definir dentro de la sección " listeners " de
nuestro Ext.List , de la forma:
listeners: {
itemdoubletap: function(view, index, target, record){
alert("doubletap on "+index);
}
}
indicamos el valor booleano "true" simplemente añadirá un icono con una flecha a la
derecha de cada elemento (como podemos ver en la imagen inferior).
En lugar de un valor booleano, podemos indicarle una función. En este caso, además
de añadirse el icono en cada elemento, también se ejecutará la función cada vez que
se presiones sobre dicho icono. Solo se capturará cuando se presione sobre el icono,
no sobre toda la barra (como en el caso de itemtap). A continuación se incluye un
ejemplo de uso:
96
Listados
97
Formularios
Formularios
En esta sección vamos a ver como podemos cargar, guardar y validar los datos de un
formulario.
campos llamados "title" y "text" y a continuación se cargan los datos del registro "note".
noteEditor.setRecord( note );
El método " setRecord( data ) " recibe como parámetro una instancia de un modelo de
datos (ver sección Data Model), del cual cargará solamente los campos cuyos nombre
coincidan con los establecidos en el formulario. En este formulario tenemos dos campos:
" name: 'title' " y " name: 'text' ", si cargamos una instancia de un modelo de datos como
el descrito a continuación, solamente se cargarían los dos campos que coinciden.
98
Formularios
Ext.define('Note', {
extend: 'Ext.data.Model',
config: {
fields: [
{ name: 'id', type: 'int' },
{ name: 'date', type: 'date', dateFormat: 'c' },
{ name: 'title', type: 'string' },
{ name: 'text', type: 'string' }
]
}
});
noteEditor.setRecord( note );
99
Formularios
En tercer lugar tenemos que realizar el proceso de validación de los datos (explicado
en el siguiente apartado).
Y por último guardar los datos en el store correspondiente. Si tenemos una instancia
del almacén de datos creada (ver sección Data Store) podemos añadir los datos
llamando a su función add , de la forma:
notesStore.add(currentNote);
En el siguiente código de ejemplo se resumen los cuatro pasos que habría que seguir para
cargar los datos del formulario 'noteEditor' y guardarlos en el almacén 'notesStore'.
100
Formularios
// Realizar validaciones
// (ver siguiente apartado)
Más comúnmente nuestro almacén estará asociado con algún elemento que nos permita
visualizar los datos (como un listado o un Data View, ver secciones correspondientes). Si
por ejemplo fuera un listado deberíamos de obtener la instancia del almacén de datos
llamando a su método getStore() y posteriormente añadir los datos que habíamos
obtenido del formulario de la forma:
Opcionalmente podemos comprobar si los datos a añadir están repetidos. Para esto
tenemos que utilizar el método findRecord() del store (ver sección Data Store).
Para terminar con el ejemplo del listado, una vez añadidos los datos tendremos que
sincronizar su store, ordenarlo (si fuese necesario) y por último actualizar o refrescar la vista
del listado:
notesStore.sync();
notesStore.sort([{ property: 'date', direction: 'DESC'}]);
notesList.refresh();
Comprobar validaciones
101
Formularios
Para comprobar las validaciones de un formulario lo tenemos que hacer de forma manual
llamando a la función validate() del modelo de datos asociado. Para esto tienen que estar
definidas estas validaciones en el modelo. Continuando con el ejemplo del modelo de datos
"Note", vamos a añadir que los campos id , title y text sean requeridos:
Ext.define('User', {
extend: 'Ext.data.Model',
fields: [
{ name: 'id', type: 'int' },
{ name: 'date', type: 'date', dateFormat: 'c' },
{ name: 'title', type: 'string' },
{ name: 'text', type: 'string' }
],
validations: [
{ type: 'presence', field: 'id' },
{ type: 'presence', field: 'title',
message: 'Introduzca un título para esta nota' },
{ type: 'presence', field: 'text',
message: 'Introduzca un texto para esta nota' }
]
});
Los pasos a seguir para realizar la validación de un formulario son los siguientes:
Llamar a la función validate() del modelo de datos. Esta función comprueba que se
cumplan todas las validaciones que estén definidas en dicho modelo, devolviendo un
objeto del tipo Errors .
En caso de que existan errores tendremos que mostrar un aviso con los errores y no
realizar ninguna acción más.
Dado que pueden haber varios errores (guardados en el array items del objeto
Errors ), tenemos que iterar por los elementos de este array usando la función
sobre el que va a iterar y el segundo la función que se llamará en cada iteración. Esta
función recibe a su vez dos parámetros: el item de la iteración actual y el índice de ese
item en el array.
Para mostrar el aviso con los errores podemos usar un MessageBox (ver sección
correspondiente).
102
Formularios
// Realizar validaciones
var errors = currentNote.validate();
if(!errors.isValid())
{
var message="";
// Almacenar datos
notesStore.add(currentNote);
También podríamos haber creado un bucle para iterar entre los elementos del array de
errores, o haber llamado a la función errors.getByField('title')[0].getMessage() para
obtener directamente el mensaje de error de un campo en concreto.
103
Más información
Más información
Podemos consultar principalmente tres fuentes de información cuando tengamos alguna
duda:
http://docs.sencha.com/touch/2.4/2.4.1-apidocs/
http://www.sencha.com/learn/touch/
104
Ejercicios 1
Ejercicios 1
En la sección de ejercicios de Sencha Touch vamos a practicar creando una pequeña
aplicación móvil que nos permita gestionar un listado de notas. La pantalla principal
contendrá el listado de notas, con botones para crear nuevas notas y modificarlas. Al crear
una nueva nota nos aparecerá un formulario en el que podremos introducir los datos y
añadirlos al listado. Este mismo formulario también se utilizará para modificar las notas
existentes. Además desde la pantalla principal podremos abrir la ayuda con información
sobre la aplicación y el autor.
En todos los ejercicios se utilizará el mismo proyecto de Sencha Touch, el cual se irá
completando ejercicio tras ejercicio hasta tener la aplicación completa. Para la entrega
solamente será necesario enviar el resultado final.
En app.js es donde vamos a crear nuestra aplicación y los paneles que necesitamos.
Borramos el código que se incluye de ejemplo para empezar nuestra aplicación desde
cero, siguiendo estos pasos:
105
Ejercicios 1
Ext.application({
name: 'MisNotas',
launch: function() {
Ext.fly('appLoadingIndicator').destroy();
Ext.Viewport.add( /*TODO: referencia al panel principal*/ );
}
});
function crearPanelPrincipal() {
panelPrincipal = Ext.create('Ext.Panel', { ... });
return panelPrincipal;
}
Ext.application({
name: 'MisNotas',
launch: function() {
Ext.fly('appLoadingIndicator').destroy();
Ext.Viewport.add( crearPanelPrincipal() );
}
});
Nota: En las plantillas de los ejercicios se incluye el fichero app.js con un esqueleto de la
estructura a seguir.
106
Ejercicios 1
En este ejercicio vamos crear los paneles principales de la aplicación, para esto partiremos
del código del ejercicio anterior.
Como ya hemos explicado la aplicación consta de un panel para listar las notas, otro para
editar las notas y un tercero para mostrar la ayuda. Además vamos a necesitar un cuarto
panel que se utilizará como contenedor. A continuación se explican los pasos que debemos
seguir:
En app.js modificaremos el panel principal para asignarle un layout tipo card y una sección
items con referencias a los tres paneles de la aplicación mediante su nombre de variable,
Estos tres paneles los definiremos de la misma forma: un panel con layout tipo fit y un texto
html de prueba que utilizaremos para comprobar que el panel se visualiza correctamente.
Por último deberemos comprobar que todo funciona correctamente. Para visualizar cada
uno de los paneles de momento podemos cambiar su orden en la sección items del panel
contenedor o llamar al método panelPrincipal.setActiveItem(1); del panel.
Las barras las instanciaremos como un objeto separado y después las añadiremos a los
paneles:
Por último para el panel panelAyuda añadiremos una barra en su parte superior con el
título "Ayuda".
107
Ejercicios 1
108
Ejercicios 2
Ejercicios 2
En esta sesión vamos a continuar con el ejercicio del editor de notas de la sesión anterior, al
cual añadiremos el contenido de la ayuda, el panel con el formulario y definiremos las
transiciones entre paneles.
Al presionar el botón del panel panelAyuda realizaremos una transición hacia arriba
para cambiar al panel panelContenedorLista .
Por último, para los tres botones del panel panelFormulario asignaremos la misma
transición hacia la derecha para cambiar al panel panelContenedorLista .
Nota: para obtener las referencias a los paneles para crear las transiciones tenemos dos
opciones:
109
Ejercicios 2
El contenido HTML de cada uno de estos paneles del Carousel lo definiremos en una
variable independiente (llamadas var htmlAyuda1 y var htmlAyuda2 ), que posteriormente
asignaremos al elemento html del panel correspondiente. Recordad que para concatenar
cadenas tendremos que usar el símbolo más (+).
Para el HTML del primer panel del Carousel definiremos una capa (DIV) a la que
asignaremos el estilo ".ayuda". Dentro de esta capa colocaremos el título "Mis Notas" (de
tipo H1), seguido por un par de párrafos con el texto "Aplicación Web para la gestión de
notas realizada con Sencha Touch. Máster en Desarrollo de Aplicaciones para Dispositivos
Móviles". Y por último abajo colocaremos una imagen (logo.png) con un ancho de 100
píxeles (subcarpeta "imgs" de la plantilla).
Para el segundo panel utilizaremos también una capa (DIV) con la clase ".ayuda", en la que
colocaremos el titular "Autor", seguido por unos párrafos con los datos del autor.
Por último añadiremos los estilos CSS que hemos definido dentro de la sección de estilos
del fichero index.html:
.ayuda h1: la etiqueta H1 (cuando esté dentro de la clase "ayuda") la definiremos con
un color azul oscuro (color: navy), un tamaño de letra de 18 puntos y el estilo
"uppercase" para que aparezca siempre en mayúsculas (text-transform: uppercase).
de tipo formulario. Además tenemos que quitar también los campos layout: fit y el texto
HTML que teníamos puesto de prueba. En este formulario vamos a añadir dos campos al
array de items (además de las dos barras de herramientas que ya teníamos):
Un área de texto con nombre text , etiqueta "Texto:" y que también sea requerido.
110
Ejercicios 3
Ejercicios 3
En esta sesión vamos a continuar con los ejercicios anteriores del editor de notas.
Añadiremos los elementos necesarios para poder crear, editar, guardar y borrar notas, así
como visualizarlas en un listado.
Nuestro modelo de datos llamado ' Nota ' tendrá cuatro campos:
Además deberemos definir las siguientes validaciones: los campos 'id', 'title' y 'text' serán
requeridos, y para los campos 'title' y 'text' modificaremos el mensaje de error por defecto
por "Introduzca un título/texto".
Los modelos de datos se tienen que guardar por separado en la carpeta designada
para ello ( app/model ), además en la aplicación se tendrá que indicar al inicio los
modelos que tiene que cargar (revisar el apartado "Uso de modelos de datos en una
aplicación").
De forma temporal y para poder ver los resultados vamos a insertar datos en el store,
añadiendo las siguientes líneas al mismo:
111
Ejercicios 3
data: [
{ id: 1, date: new Date(), title: 'Test 1', text: 'texto de prueba' },
{ id: 2, date: new Date(), title: 'Test 2', text: 'texto de prueba' },
{ id: 3, date: new Date(), title: 'Test 3', text: 'texto de prueba' },
{ id: 4, date: new Date(), title: 'Test 4', text: 'texto de prueba' }
]
En su sección itemTpl indicamos que muestre el campo {title} dentro de una capa tipo
DIV con el estilo CSS "list-item-title", y que el campo {text} lo muestre a continuación en
otra capa tipo DIV con el estilo CSS "list-item-text".
Ahora tenemos que añadir esos estilos al fichero app.css. Para ambas clases ('list-item-title'
y 'list-item-text') definiremos los mismos estilos (ver código siguiente), salvo para el "list-
item-text" que además tendrá el texto en color gris.
En la función crearNuevaNota() en primer lugar nos guardaremos la fecha actual var now =
new Date(); , y a continuación obtendremos el identificador único del registro a crear var
112
Ejercicios 3
que obtenemos un número que no se repite que podemos usar como ID). A continuación
creamos un registro del modelo 'MisNotas.model.Nota' y lo cargamos en nuestro formulario
( panelFormulario.setRecord( note ); ).
Por último vamos a añadir la funcionalidad de editar las notas creadas. Para esto vamos
hasta el 'panelLista', y definimos su función onItemDisclosure . Esta función recoge un
parámetro (record) que tenemos que cargar en el 'panelFormulario'
( panelFormulario.setRecord( record ); ). A continuación realizaremos una transición de tipo
'slide' hacia la izquierda y con una duración de 1 segundo, para mostrar el
'panelFormulario'.
Una vez validado el registro obtenido procederemos a guardar los datos. Obtenemos el
store usado por el listado ( panelLista.getStore() ) y añadimos el registro solo si este
no está repetido (función findRecord() ).
113
Ejercicios 3
Por último actualizaremos los datos del Store ( sync() ) y refrescamos el listado
( refresh() ).
114