xonecode 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +167 -0
- package/dist/agent/authEnDisco.js +56 -0
- package/dist/agent/authEnDisco.js.map +1 -0
- package/dist/agent/configEnDisco.js +109 -0
- package/dist/agent/configEnDisco.js.map +1 -0
- package/dist/agent/crearProyecto.js +34 -0
- package/dist/agent/crearProyecto.js.map +1 -0
- package/dist/agent/entorno.js +124 -0
- package/dist/agent/entorno.js.map +1 -0
- package/dist/agent/guionizado.js +53 -0
- package/dist/agent/guionizado.js.map +1 -0
- package/dist/agent/instantanea.js +129 -0
- package/dist/agent/instantanea.js.map +1 -0
- package/dist/agent/interrupts.js +83 -0
- package/dist/agent/interrupts.js.map +1 -0
- package/dist/agent/mensajes.js +24 -0
- package/dist/agent/mensajes.js.map +1 -0
- package/dist/agent/modelos.js +42 -0
- package/dist/agent/modelos.js.map +1 -0
- package/dist/agent/normalizar.js +20 -0
- package/dist/agent/normalizar.js.map +1 -0
- package/dist/agent/perfiles.js +103 -0
- package/dist/agent/perfiles.js.map +1 -0
- package/dist/agent/proyecto.js +107 -0
- package/dist/agent/proyecto.js.map +1 -0
- package/dist/agent/puente.js +142 -0
- package/dist/agent/puente.js.map +1 -0
- package/dist/agent/resumenDeTool.js +48 -0
- package/dist/agent/resumenDeTool.js.map +1 -0
- package/dist/agent/skills.js +65 -0
- package/dist/agent/skills.js.map +1 -0
- package/dist/agent/textoDeTool.js +113 -0
- package/dist/agent/textoDeTool.js.map +1 -0
- package/dist/agent/turnoReal.js +173 -0
- package/dist/agent/turnoReal.js.map +1 -0
- package/dist/agent/verificador.js +65 -0
- package/dist/agent/verificador.js.map +1 -0
- package/dist/agent/xoneAgent.js +106 -0
- package/dist/agent/xoneAgent.js.map +1 -0
- package/dist/bin.js +9 -0
- package/dist/bin.js.map +1 -0
- package/dist/cli/aprobar.js +82 -0
- package/dist/cli/aprobar.js.map +1 -0
- package/dist/cli/config.js +133 -0
- package/dist/cli/config.js.map +1 -0
- package/dist/cli/consola.js +346 -0
- package/dist/cli/consola.js.map +1 -0
- package/dist/cli/describe.js +53 -0
- package/dist/cli/describe.js.map +1 -0
- package/dist/cli/doctor.js +45 -0
- package/dist/cli/doctor.js.map +1 -0
- package/dist/cli/main.js +520 -0
- package/dist/cli/main.js.map +1 -0
- package/dist/cli/markdown.js +90 -0
- package/dist/cli/markdown.js.map +1 -0
- package/dist/cli/run.js +164 -0
- package/dist/cli/run.js.map +1 -0
- package/dist/cli/spinner.js +89 -0
- package/dist/cli/spinner.js.map +1 -0
- package/dist/cli/stdio.js +193 -0
- package/dist/cli/stdio.js.map +1 -0
- package/dist/cli/tema.js +40 -0
- package/dist/cli/tema.js.map +1 -0
- package/dist/cli/verify.js +62 -0
- package/dist/cli/verify.js.map +1 -0
- package/dist/core/bitacora.js +30 -0
- package/dist/core/bitacora.js.map +1 -0
- package/dist/core/config.js +236 -0
- package/dist/core/config.js.map +1 -0
- package/dist/core/contextos.js +56 -0
- package/dist/core/contextos.js.map +1 -0
- package/dist/core/deps.js +51 -0
- package/dist/core/deps.js.map +1 -0
- package/dist/core/diff.js +79 -0
- package/dist/core/diff.js.map +1 -0
- package/dist/core/esqueleto.js +366 -0
- package/dist/core/esqueleto.js.map +1 -0
- package/dist/core/events.js +2 -0
- package/dist/core/events.js.map +1 -0
- package/dist/core/modelos.js +85 -0
- package/dist/core/modelos.js.map +1 -0
- package/dist/core/notify.js +106 -0
- package/dist/core/notify.js.map +1 -0
- package/dist/core/ports.js +114 -0
- package/dist/core/ports.js.map +1 -0
- package/dist/core/turno.js +127 -0
- package/dist/core/turno.js.map +1 -0
- package/dist/vendor/hitl.js +103 -0
- package/dist/vendor/hitl.js.map +1 -0
- package/dist/vendor/skillLoaders/catalog.js +74 -0
- package/dist/vendor/skillLoaders/catalog.js.map +1 -0
- package/dist/vendor/tokenTracking.js +58 -0
- package/dist/vendor/tokenTracking.js.map +1 -0
- package/package.json +41 -0
- package/skills/xone-debugging/SKILL.md +52 -0
- package/skills/xone-debugging/references/faq.md +561 -0
- package/skills/xone-debugging/references/troubleshooting-y-glosario.md +429 -0
- package/skills/xone-development/SKILL.md +105 -0
- package/skills/xone-development/references/anti-patrones.md +94 -0
- package/skills/xone-development/references/css/atributos-por-categoria.md +509 -0
- package/skills/xone-development/references/css/buenas-practicas-y-parser.md +419 -0
- package/skills/xone-development/references/css/dinamicos-cascada-y-componentes.md +550 -0
- package/skills/xone-development/references/css/patrones-material-y-temas.md +1140 -0
- package/skills/xone-development/references/css/propiedades-y-herencia.md +885 -0
- package/skills/xone-development/references/css/selectores-unidades-colores.md +840 -0
- package/skills/xone-development/references/datos/appdata-referencia-ampliada.md +457 -0
- package/skills/xone-development/references/datos/appdata.md +611 -0
- package/skills/xone-development/references/datos/http-sqlmanager-y-crypto.md +720 -0
- package/skills/xone-development/references/datos/http.md +366 -0
- package/skills/xone-development/references/datos/oauth2-y-replica.md +156 -0
- package/skills/xone-development/references/device/biometria-imagedrawing-y-otros.md +544 -0
- package/skills/xone-development/references/device/objetos-de-dispositivo.md +583 -0
- package/skills/xone-development/references/device/systemsettings-referencia-ampliada.md +396 -0
- package/skills/xone-development/references/device/systemsettings-y-permisos.md +342 -0
- package/skills/xone-development/references/fundamentos/conceptos-clave.md +695 -0
- package/skills/xone-development/references/fundamentos/configuracion-app-xml-ini-mappings.md +668 -0
- package/skills/xone-development/references/fundamentos/errores-comunes.md +312 -0
- package/skills/xone-development/references/fundamentos/navegacion-convenciones-y-primer-proyecto.md +576 -0
- package/skills/xone-development/references/fundamentos/plataforma-y-anatomia-de-proyecto.md +387 -0
- package/skills/xone-development/references/indice-completo.md +77 -0
- package/skills/xone-development/references/javascript/coleccion-error-y-usuario.md +260 -0
- package/skills/xone-development/references/javascript/debugging-y-best-practices.md +245 -0
- package/skills/xone-development/references/javascript/metodos-de-los-controles.md +556 -0
- package/skills/xone-development/references/javascript/metodos-nativos-de-la-vista.md +114 -0
- package/skills/xone-development/references/javascript/motor-js-y-contexto-de-ejecucion.md +319 -0
- package/skills/xone-development/references/javascript/objeto-ai-llm-en-dispositivo.md +358 -0
- package/skills/xone-development/references/javascript/objetos-creables-a-m.md +527 -0
- package/skills/xone-development/references/javascript/objetos-creables-n-z.md +385 -0
- package/skills/xone-development/references/javascript/patrones-criticos-seguridad-y-rendimiento.md +595 -0
- package/skills/xone-development/references/javascript/patrones-de-navegacion-datos-y-codigo.md +751 -0
- package/skills/xone-development/references/javascript/patrones-de-ui-voz-integracion-y-seguridad.md +701 -0
- package/skills/xone-development/references/javascript/plantillas-y-funciones-utilitarias.md +647 -0
- package/skills/xone-development/references/javascript/self-y-dataobject.md +449 -0
- package/skills/xone-development/references/javascript/singletons-globales.md +361 -0
- package/skills/xone-development/references/javascript/ui-catalogo-de-metodos.md +464 -0
- package/skills/xone-development/references/javascript/ui-gps-camara-y-multimedia.md +506 -0
- package/skills/xone-development/references/javascript/ui-navegacion-mensajes-y-vista.md +422 -0
- package/skills/xone-development/references/resumen-js-datos-dispositivo.md +80 -0
- package/skills/xone-development/references/resumen-xml-y-css.md +74 -0
- package/skills/xone-development/references/tipos-de-prop.md +28 -0
- package/skills/xone-development/references/xml-ui/asfilter-visibilidad-eventos-y-macros.md +576 -0
- package/skills/xone-development/references/xml-ui/atributos-coll-group-frame.md +235 -0
- package/skills/xone-development/references/xml-ui/atributos-method-macro-script-event-app.md +244 -0
- package/skills/xone-development/references/xml-ui/atributos-prop.md +439 -0
- package/skills/xone-development/references/xml-ui/contents-y-macros.md +392 -0
- package/skills/xone-development/references/xml-ui/errores-comunes-xml.md +245 -0
- package/skills/xone-development/references/xml-ui/estructura-y-nodo-coll.md +326 -0
- package/skills/xone-development/references/xml-ui/eventos-ciclo-de-vida-e-interaccion.md +704 -0
- package/skills/xone-development/references/xml-ui/eventos-sistema-login-y-personalizados.md +944 -0
- package/skills/xone-development/references/xml-ui/layouts-herencia-y-buenas-practicas.md +618 -0
- package/skills/xone-development/references/xml-ui/mapas.md +685 -0
- package/skills/xone-development/references/xml-ui/mappings-y-colecciones-separadas.md +148 -0
- package/skills/xone-development/references/xml-ui/nodos-group-y-frame.md +666 -0
- package/skills/xone-development/references/xml-ui/patrones-de-pantalla.md +643 -0
- package/skills/xone-development/references/xml-ui/prop-atributos-y-condiciones.md +298 -0
- package/skills/xone-development/references/xml-ui/prop-tipos-basicos.md +493 -0
- package/skills/xone-development/references/xml-ui/prop-tipos-combos-y-controles.md +664 -0
- package/skills/xone-development/references/xml-ui/prop-tipos-listas-y-mapas.md +412 -0
- package/skills/xone-plan-builder/SKILL.md +183 -0
- package/skills/xone-plan-builder/agents/openai.yaml +5 -0
- package/skills/xone-plan-builder/references/TASKS-FORMAT.md +132 -0
- package/skills/xone-project-generator/SKILL.md +111 -0
- package/skills/xone-project-generator/references/canonical-sizes.md +292 -0
- package/skills/xone-project-generator/references/colecciones-base.md +16 -0
- package/skills/xone-project-generator/references/convenciones-de-nombres.md +16 -0
- package/skills/xone-project-generator/references/ejemplos-por-sector-y-prohibiciones.md +140 -0
- package/skills/xone-project-generator/references/fase-3-estilos-css.md +597 -0
- package/skills/xone-project-generator/references/fase-6-colecciones.md +762 -0
- package/skills/xone-project-generator/references/fase-7-asfilter-e-integraciones.md +142 -0
- package/skills/xone-project-generator/references/fase-7-entidades-y-estructura-de-pantalla.md +494 -0
- package/skills/xone-project-generator/references/fase-7-plantilla-consola.md +477 -0
- package/skills/xone-project-generator/references/fase-7-plantillas-de-pantalla.md +302 -0
- package/skills/xone-project-generator/references/fase-7-viewmodes-graficos-y-listas.md +681 -0
- package/skills/xone-project-generator/references/fase-7-viewmodes-mapa-y-calendario.md +479 -0
- package/skills/xone-project-generator/references/fases-0-2-analisis-y-modelo-de-datos.md +350 -0
- package/skills/xone-project-generator/references/fases-10-12-readmes-y-validacion.md +194 -0
- package/skills/xone-project-generator/references/fases-4-5-estructura-y-configuracion.md +587 -0
- package/skills/xone-project-generator/references/fases-8-9-eventos-y-javascript.md +861 -0
- package/skills/xone-project-generator/references/flujo-de-generacion.md +67 -0
- package/skills/xone-project-generator/references/plantillas-estandar.md +98 -0
- package/skills/xone-project-generator/references/tamanos-canonicos.md +49 -0
- package/skills/xone-review/SKILL.md +189 -0
- package/skills/xone-spec-builder/SKILL.md +235 -0
- package/skills/xone-spec-builder/agents/openai.yaml +5 -0
- package/skills/xone-spec-builder/references/ADR-FORMAT.md +55 -0
- package/skills/xone-spec-builder/references/CONTEXT-FORMAT.md +42 -0
- package/skills/xone-spec-builder/references/PLAN-FORMAT.md +120 -0
package/skills/xone-development/references/javascript/patrones-de-navegacion-datos-y-codigo.md
ADDED
|
@@ -0,0 +1,751 @@
|
|
|
1
|
+
# XOne — Patrones de navegación, datos y código
|
|
2
|
+
|
|
3
|
+
> Fuente: `xone/v2/xone-help-docs/topics/05-events-patterns-faq.md` §9–§10A. Referencia de la skill; el índice está en [../../SKILL.md](../../SKILL.md).
|
|
4
|
+
|
|
5
|
+
Contenido: §9 patrones de navegación · §10 patrones de datos · §10A patrones críticos de código
|
|
6
|
+
|
|
7
|
+
---
|
|
8
|
+
|
|
9
|
+
## 9. Patrones de Navegación
|
|
10
|
+
|
|
11
|
+
### 9.1 Pantalla de Login
|
|
12
|
+
|
|
13
|
+
Plantilla mínima de coll de login (el framework la muestra automáticamente al arrancar la app cuando no hay sesión activa; tras un `appData.logout()` también vuelve a esta pantalla).
|
|
14
|
+
|
|
15
|
+
```xml
|
|
16
|
+
<coll name="Login" title="Iniciar Sesion"
|
|
17
|
+
notab="true" show-toolbar="false">
|
|
18
|
+
|
|
19
|
+
<create>
|
|
20
|
+
<script>
|
|
21
|
+
self.MAP_EMAIL = "";
|
|
22
|
+
self.MAP_PASSWORD = "";
|
|
23
|
+
</script>
|
|
24
|
+
</create>
|
|
25
|
+
|
|
26
|
+
<group name="grpPrincipal" id="1" class="groupNoTab">
|
|
27
|
+
<frame name="frmFormulario" width="100%" bgcolor="#FFFFFF">
|
|
28
|
+
<prop name="MAP_EMAIL" type="T" visible="7"
|
|
29
|
+
width="90%" height="56p" align="center"
|
|
30
|
+
hint="tu@email.com"/>
|
|
31
|
+
<prop name="MAP_PASSWORD" type="X" visible="7"
|
|
32
|
+
width="90%" height="56p" align="center"
|
|
33
|
+
hint="Tu contraseña"/>
|
|
34
|
+
<prop name="btnLogin" type="B" visible="7"
|
|
35
|
+
width="90%" height="56p" align="center" tmargin="30p"
|
|
36
|
+
title="Iniciar Sesion"
|
|
37
|
+
onclick="realizarLogin();" />
|
|
38
|
+
</frame>
|
|
39
|
+
</group>
|
|
40
|
+
|
|
41
|
+
<onback>
|
|
42
|
+
<script>
|
|
43
|
+
cerrarPantalla();
|
|
44
|
+
</script>
|
|
45
|
+
</onback>
|
|
46
|
+
</coll>
|
|
47
|
+
```
|
|
48
|
+
|
|
49
|
+
### 9.2 Lista -> Detalle (maestro-detalle)
|
|
50
|
+
|
|
51
|
+
Patron para navegar de una lista a los detalles de un registro seleccionado.
|
|
52
|
+
|
|
53
|
+
**Coleccion lista con selecteditem:**
|
|
54
|
+
|
|
55
|
+
```xml
|
|
56
|
+
<coll name="ListaProductos" sql="SELECT * FROM ##PREF##Productos"
|
|
57
|
+
loadall="true">
|
|
58
|
+
<group name="General" id="1">
|
|
59
|
+
<prop name="MAP_NOMBRE" type="T" visible="4"/>
|
|
60
|
+
<prop name="MAP_PRECIO" type="N2" visible="4"/>
|
|
61
|
+
</group>
|
|
62
|
+
|
|
63
|
+
<selecteditem refresh="false" show-wait-dialog="false">
|
|
64
|
+
<action name="runscript">
|
|
65
|
+
<script language="javascript">
|
|
66
|
+
ui.openEditView(self);
|
|
67
|
+
</script>
|
|
68
|
+
</action>
|
|
69
|
+
</selecteditem>
|
|
70
|
+
</coll>
|
|
71
|
+
```
|
|
72
|
+
|
|
73
|
+
**Content embebido en la pantalla padre:**
|
|
74
|
+
|
|
75
|
+
```xml
|
|
76
|
+
<prop name="@listaProductos" type="Z" contents="listaProductos"
|
|
77
|
+
width="100%" height="70%" viewmode="recyclerview"/>
|
|
78
|
+
<contents name="listaProductos" src="ListaProductos"/>
|
|
79
|
+
```
|
|
80
|
+
|
|
81
|
+
### 9.3 Menu con tarjetas
|
|
82
|
+
|
|
83
|
+
Patron de menu principal con tarjetas de acceso rápido.
|
|
84
|
+
|
|
85
|
+
```xml
|
|
86
|
+
<coll name="MenuPrincipal" special="true" notab="true">
|
|
87
|
+
<group name="grpPrincipal" id="1" class="groupNoTab">
|
|
88
|
+
<frame name="frmHeader" width="100%" height="120p" bgcolor="#1565C0">
|
|
89
|
+
<prop name="lblTitulo" type="L" visible="7"
|
|
90
|
+
width="100%" height="40p" align="center"
|
|
91
|
+
forecolor="#FFFFFF" fontsize="20" title="Menu Principal"/>
|
|
92
|
+
</frame>
|
|
93
|
+
|
|
94
|
+
<frame name="frmTarjetas" width="100%" scroll="true" tmargin="10p">
|
|
95
|
+
<!-- Tarjeta 1 -->
|
|
96
|
+
<frame name="frmTarjeta1" width="47%" height="150p"
|
|
97
|
+
lmargin="2%" tmargin="10p" bgcolor="#FFFFFF"
|
|
98
|
+
border-corner-radius="12" framebox="true"
|
|
99
|
+
onclick="javascript:abrirModulo('Inventario');">
|
|
100
|
+
<prop name="imgTarjeta1" type="IMG" visible="7"
|
|
101
|
+
width="48p" height="48p" align="center" tmargin="20p"
|
|
102
|
+
src="./icons/ic_inventory.png"/>
|
|
103
|
+
<prop name="lblTarjeta1" type="L" visible="7"
|
|
104
|
+
width="100%" height="30p" align="center" tmargin="10p"
|
|
105
|
+
fontsize="14" title="Inventario"/>
|
|
106
|
+
</frame>
|
|
107
|
+
|
|
108
|
+
<!-- Tarjeta 2 -->
|
|
109
|
+
<frame name="frmTarjeta2" width="47%" height="150p"
|
|
110
|
+
lmargin="2%" tmargin="10p" newline="false"
|
|
111
|
+
bgcolor="#FFFFFF" border-corner-radius="12" framebox="true"
|
|
112
|
+
onclick="javascript:abrirModulo('Pedidos');">
|
|
113
|
+
<prop name="imgTarjeta2" type="IMG" visible="7"
|
|
114
|
+
width="48p" height="48p" align="center" tmargin="20p"
|
|
115
|
+
src="./icons/ic_orders.png"/>
|
|
116
|
+
<prop name="lblTarjeta2" type="L" visible="7"
|
|
117
|
+
width="100%" height="30p" align="center" tmargin="10p"
|
|
118
|
+
fontsize="14" title="Pedidos"/>
|
|
119
|
+
</frame>
|
|
120
|
+
</frame>
|
|
121
|
+
</group>
|
|
122
|
+
</coll>
|
|
123
|
+
```
|
|
124
|
+
|
|
125
|
+
### 9.4 Navegación con pestanas (group-swipe)
|
|
126
|
+
|
|
127
|
+
```xml
|
|
128
|
+
<coll name="PantallaConTabs" special="true" group-swipe="true">
|
|
129
|
+
<group name="HEADER" id="10" class="groupfixed_header">
|
|
130
|
+
<frame name="frmtitulo" class="frmsuperior">
|
|
131
|
+
<prop name="MENU" type="L" title="MI PANTALLA"/>
|
|
132
|
+
</frame>
|
|
133
|
+
</group>
|
|
134
|
+
|
|
135
|
+
<group name="Tab1" id="1" onfocus="ExecuteNode(onfocusgrupo(1))">
|
|
136
|
+
<!-- Contenido pestana 1 -->
|
|
137
|
+
</group>
|
|
138
|
+
|
|
139
|
+
<group name="Tab2" id="2" onfocus="ExecuteNode(onfocusgrupo(2))">
|
|
140
|
+
<!-- Contenido pestana 2 -->
|
|
141
|
+
</group>
|
|
142
|
+
|
|
143
|
+
<group name="Tab3" id="3" onfocus="ExecuteNode(onfocusgrupo(3))">
|
|
144
|
+
<!-- Contenido pestana 3 -->
|
|
145
|
+
</group>
|
|
146
|
+
|
|
147
|
+
<onfocusgrupo show-wait-dialog="false">
|
|
148
|
+
<action name="runscript">
|
|
149
|
+
<param name="index"/>
|
|
150
|
+
<script language="javascript">
|
|
151
|
+
self.MAP_GROUP = index;
|
|
152
|
+
</script>
|
|
153
|
+
</action>
|
|
154
|
+
</onfocusgrupo>
|
|
155
|
+
|
|
156
|
+
<before-edit>
|
|
157
|
+
<action name="runscript">
|
|
158
|
+
<script language="javascript">
|
|
159
|
+
self.MAP_GROUP = 1;
|
|
160
|
+
self.MAP_TOTAL_PAGES = 3;
|
|
161
|
+
</script>
|
|
162
|
+
</action>
|
|
163
|
+
</before-edit>
|
|
164
|
+
</coll>
|
|
165
|
+
```
|
|
166
|
+
|
|
167
|
+
### 9.5 Drawer lateral
|
|
168
|
+
|
|
169
|
+
```xml
|
|
170
|
+
<coll name="PantallaConDrawer" notab="true"
|
|
171
|
+
ondraweropened="onDrawerOpened(e);"
|
|
172
|
+
ondrawerclosed="onDrawerClosed(e);">
|
|
173
|
+
|
|
174
|
+
<group name="Drawer" id="999"
|
|
175
|
+
width="60%" drawer-orientation="left" bgcolor="#FFFFFF">
|
|
176
|
+
<frame name="header_drawer" width="100%" height="25%"
|
|
177
|
+
bgcolor="#2B3E51" align="bottom">
|
|
178
|
+
<prop name="lblUsuario" type="L" visible="7"
|
|
179
|
+
forecolor="#FFFFFF" title="Nombre Usuario"/>
|
|
180
|
+
</frame>
|
|
181
|
+
<prop name="btnOpcion1" type="B" visible="7"
|
|
182
|
+
width="100%" height="60p" title="Inventario"
|
|
183
|
+
onclick="javascript:abrirModulo('Inventario');"/>
|
|
184
|
+
<prop name="btnOpcion2" type="B" visible="7"
|
|
185
|
+
width="100%" height="60p" title="Pedidos"
|
|
186
|
+
onclick="javascript:abrirModulo('Pedidos');"/>
|
|
187
|
+
</group>
|
|
188
|
+
|
|
189
|
+
<group name="Contenido" id="1">
|
|
190
|
+
<!-- Contenido principal -->
|
|
191
|
+
</group>
|
|
192
|
+
</coll>
|
|
193
|
+
```
|
|
194
|
+
|
|
195
|
+
### 9.6 Volver atrás con confirmacion
|
|
196
|
+
|
|
197
|
+
```xml
|
|
198
|
+
<onback show-wait-dialog="false">
|
|
199
|
+
<action name="runscript">
|
|
200
|
+
<script language="javascript">
|
|
201
|
+
var ok = ui.msgBox("Desea salir sin guardar?", "Confirmar", 4);
|
|
202
|
+
if (ok == 6) {
|
|
203
|
+
ui.getView(self).exit();
|
|
204
|
+
}
|
|
205
|
+
</script>
|
|
206
|
+
</action>
|
|
207
|
+
</onback>
|
|
208
|
+
```
|
|
209
|
+
|
|
210
|
+
---
|
|
211
|
+
|
|
212
|
+
## 10. Patrones de Datos
|
|
213
|
+
|
|
214
|
+
### 10.1 CRUD completo (crear, leer, actualizar, eliminar)
|
|
215
|
+
|
|
216
|
+
**Crear:**
|
|
217
|
+
|
|
218
|
+
```javascript
|
|
219
|
+
var coll = appData.getCollection("Productos");
|
|
220
|
+
var obj = new Productos({
|
|
221
|
+
NOMBRE: "Producto nuevo",
|
|
222
|
+
PRECIO: 29.99,
|
|
223
|
+
ACTIVO: 1,
|
|
224
|
+
FECHA_ALTA: new Date()
|
|
225
|
+
});
|
|
226
|
+
coll.addItem(obj);
|
|
227
|
+
obj.save();
|
|
228
|
+
ui.openEditView(obj); // Abrir en edición
|
|
229
|
+
```
|
|
230
|
+
|
|
231
|
+
**Leer (lista con selecteditem + openEditView):**
|
|
232
|
+
|
|
233
|
+
```xml
|
|
234
|
+
<selecteditem refresh="false" show-wait-dialog="false">
|
|
235
|
+
<action name="runscript">
|
|
236
|
+
<script language="javascript">
|
|
237
|
+
ui.openEditView(self);
|
|
238
|
+
</script>
|
|
239
|
+
</action>
|
|
240
|
+
</selecteditem>
|
|
241
|
+
```
|
|
242
|
+
|
|
243
|
+
**Actualizar (formulario con save):**
|
|
244
|
+
|
|
245
|
+
```javascript
|
|
246
|
+
// Dentro de un evento custom "guardar"
|
|
247
|
+
self.NOMBRE = self.MAP_NOMBRE;
|
|
248
|
+
self.PRECIO = self.MAP_PRECIO;
|
|
249
|
+
self.FECHA_MOD = new Date();
|
|
250
|
+
self.save();
|
|
251
|
+
ui.showToast("Guardado correctamente");
|
|
252
|
+
ui.getView(self).exit();
|
|
253
|
+
```
|
|
254
|
+
|
|
255
|
+
**Eliminar (confirmar + deleteItem):**
|
|
256
|
+
|
|
257
|
+
```xml
|
|
258
|
+
<eliminar show-wait-dialog="false" refresh="true">
|
|
259
|
+
<action name="runscript">
|
|
260
|
+
<script language="javascript">
|
|
261
|
+
var ok = ui.msgBox("Desea eliminar el registro seleccionado?",
|
|
262
|
+
"Aviso", 4);
|
|
263
|
+
if (ok === 6) {
|
|
264
|
+
var CollCal = appData.getCollection("ContentdatosCalendario");
|
|
265
|
+
CollCal.deleteItem(self.MAP_IDTAREASELECTED.toString());
|
|
266
|
+
self.MAP_IDTAREASELECTED = 0;
|
|
267
|
+
CollCal = null;
|
|
268
|
+
ui.showToast("Elemento borrado correctamente.");
|
|
269
|
+
} else {
|
|
270
|
+
ui.showToast("Se ha cancelado la acción.");
|
|
271
|
+
}
|
|
272
|
+
</script>
|
|
273
|
+
</action>
|
|
274
|
+
</eliminar>
|
|
275
|
+
```
|
|
276
|
+
|
|
277
|
+
### 10.2 Maestro-detalle con contents
|
|
278
|
+
|
|
279
|
+
**XML del maestro con content embebido:**
|
|
280
|
+
|
|
281
|
+
```xml
|
|
282
|
+
<frame name="c1" width="98%" height="78%"
|
|
283
|
+
framebox="true" border-corner-radius="10" lmargin="1%">
|
|
284
|
+
<prop name="MAP_content1" height="96%" type="Z"
|
|
285
|
+
contents="content1" forceonchange="true" bgcolor="#FFFFFF"
|
|
286
|
+
onchange="refresh(@content1)"/>
|
|
287
|
+
<contents name="content1" src="ContentDatos"/>
|
|
288
|
+
</frame>
|
|
289
|
+
```
|
|
290
|
+
|
|
291
|
+
**Agregar item al content (ejemplo real de EspecialContents):**
|
|
292
|
+
|
|
293
|
+
```xml
|
|
294
|
+
<nuevo show-wait-dialog="false" refresh="false">
|
|
295
|
+
<action name="runscript">
|
|
296
|
+
<script language="javascript">
|
|
297
|
+
var coll = self.getContents("content1");
|
|
298
|
+
var obj = coll.createObject();
|
|
299
|
+
obj.setValue("ID", MAP_COUNTER_EXT);
|
|
300
|
+
obj.NOMBRE = "Hola " + MAP_COUNTER_EXT.toString();
|
|
301
|
+
obj.DIRECCION = "dirección " + MAP_COUNTER_EXT.toString();
|
|
302
|
+
obj.IMAGEN = "campanon.jpg";
|
|
303
|
+
MAP_COUNTER_EXT = MAP_COUNTER_EXT + 1;
|
|
304
|
+
let view = ui.getView(self);
|
|
305
|
+
view.MAP_content1.addItem(obj);
|
|
306
|
+
</script>
|
|
307
|
+
</action>
|
|
308
|
+
</nuevo>
|
|
309
|
+
```
|
|
310
|
+
|
|
311
|
+
### 10.3 Filtrado dinámico de listas
|
|
312
|
+
|
|
313
|
+
**Ejemplo real** (del proyecto EspecialContents):
|
|
314
|
+
|
|
315
|
+
```xml
|
|
316
|
+
<buscar show-wait-dialog="false" refresh="false">
|
|
317
|
+
<action name="runscript">
|
|
318
|
+
<param name="param"/>
|
|
319
|
+
<script language="javascript">
|
|
320
|
+
if (param == "1") {
|
|
321
|
+
if (self.MAP_FILTRO.length == 0) {
|
|
322
|
+
self.getContents("content4").setFilter("");
|
|
323
|
+
} else {
|
|
324
|
+
self.getContents("content4").setFilter(
|
|
325
|
+
"NOMBRE like '%" + self.MAP_FILTRO.toString()
|
|
326
|
+
+ "%' OR DIRECCION like '%"
|
|
327
|
+
+ self.MAP_FILTRO.toString() + "%'"
|
|
328
|
+
);
|
|
329
|
+
}
|
|
330
|
+
}
|
|
331
|
+
self.getContents("content4").unlock();
|
|
332
|
+
self.getContents("content4").loadAll();
|
|
333
|
+
self.getContents("content4").lock();
|
|
334
|
+
ui.getView(self).refresh("@content4");
|
|
335
|
+
</script>
|
|
336
|
+
</action>
|
|
337
|
+
</buscar>
|
|
338
|
+
```
|
|
339
|
+
|
|
340
|
+
**Filtro en XML con macros de campo:**
|
|
341
|
+
|
|
342
|
+
```xml
|
|
343
|
+
<contents name="ContentDatosFiltroMultiseleccion"
|
|
344
|
+
src="ContentDatosFiltroMultiseleccion"
|
|
345
|
+
filter="((t1.MARCADO=1 AND 1=##FLD_MAP_BUSCAR_MARCADOS##)
|
|
346
|
+
OR (t1.MARCADO=0 AND 1=##FLD_MAP_BUSCAR_NOMARCADOS##))
|
|
347
|
+
AND (ifnull(t1.NOMBRE,'') LIKE ##FLD_MAP_BUSCAR_TEXT##
|
|
348
|
+
OR ifnull(t1.DIRECCION,'') LIKE ##FLD_MAP_BUSCAR_TEXT##)"/>
|
|
349
|
+
```
|
|
350
|
+
|
|
351
|
+
### 10.4 Busqueda en tiempo real con ontextchanged
|
|
352
|
+
|
|
353
|
+
```xml
|
|
354
|
+
<prop name="MAP_BUSCAR_TEXT"
|
|
355
|
+
ontextchanged="javascript:FiltraMarcados(e);"
|
|
356
|
+
labelwidth="0" text-border="true" type="T"
|
|
357
|
+
width="98%" height="60p" tooltip="Texto a buscar"/>
|
|
358
|
+
```
|
|
359
|
+
|
|
360
|
+
```javascript
|
|
361
|
+
function FiltraMarcados(e) {
|
|
362
|
+
self.MAP_BUSCAR_TEXT = e.newText;
|
|
363
|
+
self.executeNode("applyfilter");
|
|
364
|
+
}
|
|
365
|
+
```
|
|
366
|
+
|
|
367
|
+
```xml
|
|
368
|
+
<applyfilter>
|
|
369
|
+
<action name="runscript">
|
|
370
|
+
<script language="javascript">
|
|
371
|
+
self.getContents("ContentDatosFiltroMultiseleccion").clear();
|
|
372
|
+
self.getContents("ContentDatosFiltroMultiseleccion").loadAll();
|
|
373
|
+
ui.refresh("@ContentDatosFiltroMultiseleccion");
|
|
374
|
+
</script>
|
|
375
|
+
</action>
|
|
376
|
+
</applyfilter>
|
|
377
|
+
```
|
|
378
|
+
|
|
379
|
+
### 10.5 Ordenamiento ASC/DESC
|
|
380
|
+
|
|
381
|
+
```javascript
|
|
382
|
+
if (self.MAP_ORDEN == "ASC") {
|
|
383
|
+
self.MAP_ORDEN = "DESC";
|
|
384
|
+
self.MAP_BTORDEN = "sortZA.png";
|
|
385
|
+
self.MAP_BTORDENCLICK = "sortZA_click.png";
|
|
386
|
+
} else {
|
|
387
|
+
self.MAP_ORDEN = "ASC";
|
|
388
|
+
self.MAP_BTORDEN = "sortAZ.png";
|
|
389
|
+
self.MAP_BTORDENCLICK = "sortAZ_click.png";
|
|
390
|
+
}
|
|
391
|
+
self.getContents("content4").sort = "NOMBRE " + self.MAP_ORDEN.toString();
|
|
392
|
+
self.getContents("content4").unlock();
|
|
393
|
+
self.getContents("content4").loadAll();
|
|
394
|
+
self.getContents("content4").lock();
|
|
395
|
+
ui.getView(self).refresh("@content4,BTORDENAR");
|
|
396
|
+
```
|
|
397
|
+
|
|
398
|
+
### 10.6 Multiseleccion en lista
|
|
399
|
+
|
|
400
|
+
**XML con checkbox en cada fila:**
|
|
401
|
+
|
|
402
|
+
```xml
|
|
403
|
+
<prop name="MAP_BUSCAR_MARCADOS" title="Done" labelwidth="5"
|
|
404
|
+
type="NC" width="49%" height="60p"/>
|
|
405
|
+
<prop name="MAP_BUSCAR_NOMARCADOS" title="Not done" labelwidth="8"
|
|
406
|
+
type="NC" width="49%" height="60p" newline="false"/>
|
|
407
|
+
|
|
408
|
+
<prop name="MAP_DONE_PENDIENTES_BT" title="Marcar seleccionados"
|
|
409
|
+
method="Executenode(checkAll(1))" type="B" width="48%"/>
|
|
410
|
+
<prop name="MAP_NOTDONE_PENDIENTES_BT" title="Desmarcar seleccionados"
|
|
411
|
+
method="Executenode(checkAll(0))" type="B" width="48%" newline="false"/>
|
|
412
|
+
```
|
|
413
|
+
|
|
414
|
+
### 10.7 Paginación
|
|
415
|
+
|
|
416
|
+
XOne carga datos de forma paginada por defecto. Para controlar la paginación:
|
|
417
|
+
|
|
418
|
+
```xml
|
|
419
|
+
<!-- Desactivar paginacion (cargar todo) -->
|
|
420
|
+
<coll name="MiColeccion" page-limit-off="1" loadall="true">
|
|
421
|
+
|
|
422
|
+
<!-- O usar loadAll desde script -->
|
|
423
|
+
```
|
|
424
|
+
|
|
425
|
+
```javascript
|
|
426
|
+
var coll = self.getContents("miContent");
|
|
427
|
+
coll.clear();
|
|
428
|
+
coll.loadAll();
|
|
429
|
+
```
|
|
430
|
+
|
|
431
|
+
---
|
|
432
|
+
|
|
433
|
+
## 10A. Patrones Críticos de Código
|
|
434
|
+
|
|
435
|
+
### 10A.1 Patron lock/unlock para escritura en colecciones
|
|
436
|
+
|
|
437
|
+
**Regla fundamental:** Siempre usar `unlock()` antes de modificar y `lock()` en un bloque `finally` para garantizar que la coleccion se bloquea incluso si hay error.
|
|
438
|
+
|
|
439
|
+
```javascript
|
|
440
|
+
function agregarItem(nombreColeccion, datos) {
|
|
441
|
+
var coll = appData.getCollection(nombreColeccion);
|
|
442
|
+
try {
|
|
443
|
+
coll.unlock();
|
|
444
|
+
var newObj = coll.createObject();
|
|
445
|
+
for (var key in datos) {
|
|
446
|
+
if (datos.hasOwnProperty(key)) {
|
|
447
|
+
newObj[key] = datos[key];
|
|
448
|
+
}
|
|
449
|
+
}
|
|
450
|
+
coll.addItem(newObj);
|
|
451
|
+
newObj.save();
|
|
452
|
+
return true;
|
|
453
|
+
} catch(error) {
|
|
454
|
+
ui.showToast("Error: " + error.message);
|
|
455
|
+
return false;
|
|
456
|
+
} finally {
|
|
457
|
+
coll.lock();
|
|
458
|
+
}
|
|
459
|
+
}
|
|
460
|
+
```
|
|
461
|
+
|
|
462
|
+
**Para contents embebidos:**
|
|
463
|
+
|
|
464
|
+
```javascript
|
|
465
|
+
var content = self.getContents("content4");
|
|
466
|
+
try {
|
|
467
|
+
content.unlock();
|
|
468
|
+
content.loadAll();
|
|
469
|
+
// ... modificar datos ...
|
|
470
|
+
} finally {
|
|
471
|
+
content.lock();
|
|
472
|
+
}
|
|
473
|
+
ui.getView(self).refresh("@content4");
|
|
474
|
+
```
|
|
475
|
+
|
|
476
|
+
### 10A.2 Patron startBrowse/endBrowse para iteracion
|
|
477
|
+
|
|
478
|
+
**Regla:** `startBrowse()`/`endBrowse()` solo son necesarios cuando vas a **iterar** la coleccion (recorrer registros con `moveNext()`/`movePrevious()`). Abren un cursor en BD que hay que cerrar siempre en un `finally`.
|
|
479
|
+
|
|
480
|
+
**Cómo se itera correctamente:**
|
|
481
|
+
|
|
482
|
+
- `startBrowse()` ya deja el cursor en el **primer** item — no hace falta `moveFirst()` después.
|
|
483
|
+
- `getCurrentItem()` devuelve el `dataobject` de la fila actual.
|
|
484
|
+
- `moveNext()` devuelve `boolean`: `true` si pudo avanzar, `false` cuando ya no hay más filas. **No existe `coll.eof()`** en el JS API.
|
|
485
|
+
- **El objeto devuelto por `getCurrentItem()` es efímero**: el mismo objeto se reutiliza en el siguiente `moveNext()`, su contenido se reemplaza. No guardes la referencia en una variable para usarla más tarde; lee lo que necesites en la iteración o haz una copia (p.ej. con `JSON.stringify`/`JSON.parse` o copiando campo a campo en un objeto plano).
|
|
486
|
+
- `coll.MAP_XXX` **no** lee del item actual: las colecciones no exponen campos del objeto; siempre acceder vía `coll.getCurrentItem().MAP_XXX`.
|
|
487
|
+
|
|
488
|
+
```javascript
|
|
489
|
+
function listarUsuariosActivos() {
|
|
490
|
+
var coll = appData.getCollection("Usuarios");
|
|
491
|
+
var lista = [];
|
|
492
|
+
coll.startBrowse();
|
|
493
|
+
try {
|
|
494
|
+
var obj = coll.getCurrentItem();
|
|
495
|
+
while (obj != null) {
|
|
496
|
+
// Copia el campo (no guardes obj — se reutiliza en el siguiente moveNext)
|
|
497
|
+
lista.push(obj.MAP_LOGIN);
|
|
498
|
+
if (!coll.moveNext()) break;
|
|
499
|
+
obj = coll.getCurrentItem();
|
|
500
|
+
}
|
|
501
|
+
} finally {
|
|
502
|
+
coll.endBrowse();
|
|
503
|
+
}
|
|
504
|
+
return lista;
|
|
505
|
+
}
|
|
506
|
+
```
|
|
507
|
+
|
|
508
|
+
**No requieren `startBrowse`:**
|
|
509
|
+
|
|
510
|
+
- `findObject(criteria)` y `findAllObjects(criteria)` — ejecutan su propia SQL independiente del cursor.
|
|
511
|
+
- `get(index)` / `getItem(index)` / `getItem(field, value)` — acceso directo sin cursor.
|
|
512
|
+
- `loadAll()` seguido de `getCount()` + `get(i)` — `loadAll` carga la lista en memoria y se accede por índice.
|
|
513
|
+
|
|
514
|
+
```javascript
|
|
515
|
+
// CORRECTO: findObject NO necesita startBrowse/endBrowse
|
|
516
|
+
function obtenerUsuario(userId) {
|
|
517
|
+
var coll = appData.getCollection("Usuarios");
|
|
518
|
+
var escapado = cstr(userId).replace(/'/g, "''");
|
|
519
|
+
return coll.findObject("LOGIN='" + escapado + "'");
|
|
520
|
+
}
|
|
521
|
+
```
|
|
522
|
+
|
|
523
|
+
### 10A.3 Patron filtro con restauracion
|
|
524
|
+
|
|
525
|
+
Siempre guardar y restaurar el filtro original en un bloque `finally`:
|
|
526
|
+
|
|
527
|
+
```javascript
|
|
528
|
+
function procesarDatosFiltrados(coll, filtro) {
|
|
529
|
+
var filtroOriginal = coll.getFilter();
|
|
530
|
+
try {
|
|
531
|
+
coll.setFilter(filtro);
|
|
532
|
+
coll.loadAll();
|
|
533
|
+
var count = coll.count();
|
|
534
|
+
// ... procesar datos filtrados ...
|
|
535
|
+
return count;
|
|
536
|
+
} finally {
|
|
537
|
+
coll.setFilter(filtroOriginal);
|
|
538
|
+
}
|
|
539
|
+
}
|
|
540
|
+
```
|
|
541
|
+
|
|
542
|
+
### 10A.4 Patron GPS: iniciar, leer, detener
|
|
543
|
+
|
|
544
|
+
```javascript
|
|
545
|
+
function obtenerPosicionGPS() {
|
|
546
|
+
ui.startGps();
|
|
547
|
+
|
|
548
|
+
var lat = ui.getGpsLatitude();
|
|
549
|
+
var lng = ui.getGpsLongitude();
|
|
550
|
+
|
|
551
|
+
if (lat != 0 && lng != 0) {
|
|
552
|
+
self.MAP_LATITUD = lat;
|
|
553
|
+
self.MAP_LONGITUD = lng;
|
|
554
|
+
ui.refresh("MAP_LATITUD,MAP_LONGITUD");
|
|
555
|
+
}
|
|
556
|
+
}
|
|
557
|
+
```
|
|
558
|
+
|
|
559
|
+
**GPS con callback para tracking continuo:**
|
|
560
|
+
|
|
561
|
+
```javascript
|
|
562
|
+
function iniciarTracking() {
|
|
563
|
+
var jsParams = {
|
|
564
|
+
nodeName: "callbackgps",
|
|
565
|
+
timeBetweenUpdates: 10000,
|
|
566
|
+
minimumMetersDistanceRange: 10,
|
|
567
|
+
foreground: true,
|
|
568
|
+
title: "Mi App GPS",
|
|
569
|
+
text: "Rastreando ubicación..."
|
|
570
|
+
};
|
|
571
|
+
ui.startGps(jsParams);
|
|
572
|
+
}
|
|
573
|
+
|
|
574
|
+
function detenerTracking() {
|
|
575
|
+
ui.stopGps();
|
|
576
|
+
}
|
|
577
|
+
```
|
|
578
|
+
|
|
579
|
+
**GPS con variables de empresa para compartir coordenadas:**
|
|
580
|
+
|
|
581
|
+
```javascript
|
|
582
|
+
function GetPosGPS(tipo, auxcollobj) {
|
|
583
|
+
var latitud = 0, longitud = 0;
|
|
584
|
+
ui.startGps();
|
|
585
|
+
|
|
586
|
+
var collGPS = appData.getCollection("ContentConectarGPS");
|
|
587
|
+
collGPS.startBrowse();
|
|
588
|
+
var x = collGPS.getCurrentItem();
|
|
589
|
+
if (typeof x !== "undefined" && x !== null) {
|
|
590
|
+
if (x.STATUS == 1 && x.HGPS.length > 0) {
|
|
591
|
+
if (x.LATITUD !== "") latitud = parseFloat(x.LATITUD);
|
|
592
|
+
if (x.LONGITUD.length > 0) longitud = parseFloat(x.LONGITUD);
|
|
593
|
+
}
|
|
594
|
+
}
|
|
595
|
+
|
|
596
|
+
// Guardar en variable global
|
|
597
|
+
appData.getCurrentEnterprise().setVariable("LATITUD", latitud);
|
|
598
|
+
appData.getCurrentEnterprise().setVariable("LONGITUD", longitud);
|
|
599
|
+
}
|
|
600
|
+
```
|
|
601
|
+
|
|
602
|
+
### 10A.5 Patron de chat/mensajeria (resumen)
|
|
603
|
+
|
|
604
|
+
El chat en XOne se implementa con 4 colecciones: pantalla principal, lista de conversaciones, usuarios y mensajes. Patron clave:
|
|
605
|
+
|
|
606
|
+
```javascript
|
|
607
|
+
function enviarMensaje(obj, tipo) {
|
|
608
|
+
if (obj.MAP_MENSAJE.length > 0 || tipo > 0) {
|
|
609
|
+
var contentChatear = obj.getContents("ContentChatear");
|
|
610
|
+
try {
|
|
611
|
+
contentChatear.lock();
|
|
612
|
+
var msgObj = contentChatear.createObject();
|
|
613
|
+
contentChatear.addItem(msgObj);
|
|
614
|
+
msgObj.IDCHAT = obj.MAP_IDCHAT;
|
|
615
|
+
msgObj.USUARIO = appData.getGlobalMacro("##MACRO##");
|
|
616
|
+
msgObj.TIPO = tipo;
|
|
617
|
+
msgObj.FECHA = new Date();
|
|
618
|
+
if (tipo === 0) {
|
|
619
|
+
msgObj.MENSAJE = obj.MAP_MENSAJE;
|
|
620
|
+
obj.MAP_MENSAJE = "";
|
|
621
|
+
}
|
|
622
|
+
msgObj.save();
|
|
623
|
+
} finally {
|
|
624
|
+
contentChatear.unlock();
|
|
625
|
+
}
|
|
626
|
+
ui.refresh("ContentChatear", "MAP_MENSAJE");
|
|
627
|
+
}
|
|
628
|
+
}
|
|
629
|
+
```
|
|
630
|
+
|
|
631
|
+
**Atributo clave para chat:** `start-from-bottom="true"` en el prop del content para que los mensajes se muestren desde abajo (estilo WhatsApp).
|
|
632
|
+
|
|
633
|
+
### 10A.6 Patron de dialogo modal (coleccion como modal)
|
|
634
|
+
|
|
635
|
+
Abrir una coleccion como dialogo modal usando `ui.openEditView()`:
|
|
636
|
+
|
|
637
|
+
```javascript
|
|
638
|
+
function abrirDialogoModal() {
|
|
639
|
+
var coll = appData.getCollection("MiDialogo");
|
|
640
|
+
var obj = new MiDialogo({ MAP_TITULO: "Titulo del dialogo", MAP_MENSAJE: "Contenido" });
|
|
641
|
+
coll.addItem(obj);
|
|
642
|
+
ui.openEditView(obj);
|
|
643
|
+
}
|
|
644
|
+
```
|
|
645
|
+
|
|
646
|
+
**Alternativa con frame flotante y modal:**
|
|
647
|
+
|
|
648
|
+
```xml
|
|
649
|
+
<frame name="frmModal"
|
|
650
|
+
animation-in-delay="250" animation-out-delay="250"
|
|
651
|
+
animation-in="##RIGHT_IN##" animation-out="##LEFT_OUT##"
|
|
652
|
+
disablevisible="MAP_VERMODAL=0"
|
|
653
|
+
bgcolor="#ffffff" modal="true" floating="true"
|
|
654
|
+
top="0" left="0" width="100%" height="100%">
|
|
655
|
+
<!-- Contenido del modal -->
|
|
656
|
+
<prop name="btnCerrar" type="B" title="Cerrar"
|
|
657
|
+
onclick="self.MAP_VERMODAL=0; ui.refresh('frmModal');"/>
|
|
658
|
+
</frame>
|
|
659
|
+
```
|
|
660
|
+
|
|
661
|
+
### 10A.7 Patron custom msgbox (coleccion como msgbox)
|
|
662
|
+
|
|
663
|
+
Usar una coleccion como cuadro de dialogo personalizado en lugar del `ui.msgBox()` nativo:
|
|
664
|
+
|
|
665
|
+
```javascript
|
|
666
|
+
function userMsgBox(title, msg, type) {
|
|
667
|
+
var collMsgBox = appData.getCollection("EspecialMsgbox").createClone();
|
|
668
|
+
var objMsgBox = collMsgBox.createObject();
|
|
669
|
+
collMsgBox.addItem(objMsgBox);
|
|
670
|
+
objMsgBox.MAP_TITULO = title;
|
|
671
|
+
objMsgBox.MAP_MENSAJE = msg;
|
|
672
|
+
objMsgBox.MAP_TIPO = type;
|
|
673
|
+
var nResult = ui.msgBox(objMsgBox);
|
|
674
|
+
return nResult;
|
|
675
|
+
}
|
|
676
|
+
```
|
|
677
|
+
|
|
678
|
+
**La coleccion del msgbox usa atributos especiales:**
|
|
679
|
+
- `cancelable="false"` - No se puede cerrar pulsando fuera
|
|
680
|
+
- `cancelable-outside="false"` - No se puede cerrar tocando fuera
|
|
681
|
+
- `button-option="10"` en el botón OK, `button-option="1"` en SI, `button-option="2"` en NO
|
|
682
|
+
- `height="-2"` para altura automática basada en contenido
|
|
683
|
+
|
|
684
|
+
### 10A.8 Patron wizard (formulario multi-paso con groups)
|
|
685
|
+
|
|
686
|
+
Usar `group-swipe="true"` y grupos numerados para crear un asistente paso a paso:
|
|
687
|
+
|
|
688
|
+
```xml
|
|
689
|
+
<coll name="FormularioWizard" special="true" group-swipe="true">
|
|
690
|
+
<group name="HEADER" id="10" class="groupfixed_header">
|
|
691
|
+
<frame name="frmtitulo" class="frmsuperior">
|
|
692
|
+
<prop name="MENU" type="L" title="Formulario"/>
|
|
693
|
+
<prop name="MAP_LAST" type="B" img="atras.png"
|
|
694
|
+
method="ExecuteNode(ir(-1))"
|
|
695
|
+
disablevisible="MAP_GROUP=1"/>
|
|
696
|
+
<prop name="MAP_NEXT" type="B" img="siguiente.png"
|
|
697
|
+
method="ExecuteNode(ir(1))"
|
|
698
|
+
disablevisible="MAP_GROUP=MAP_TOTAL_PAGES"
|
|
699
|
+
newline="false"/>
|
|
700
|
+
</frame>
|
|
701
|
+
</group>
|
|
702
|
+
|
|
703
|
+
<group name="Paso1" id="1" onfocus="ExecuteNode(onfocusgrupo(1))">
|
|
704
|
+
<!-- Datos personales -->
|
|
705
|
+
</group>
|
|
706
|
+
<group name="Paso2" id="2" onfocus="ExecuteNode(onfocusgrupo(2))">
|
|
707
|
+
<!-- Dirección -->
|
|
708
|
+
</group>
|
|
709
|
+
<group name="Paso3" id="3" onfocus="ExecuteNode(onfocusgrupo(3))">
|
|
710
|
+
<!-- Confirmación -->
|
|
711
|
+
</group>
|
|
712
|
+
|
|
713
|
+
<before-edit>
|
|
714
|
+
<action name="runscript">
|
|
715
|
+
<script language="javascript">
|
|
716
|
+
self.MAP_GROUP = 1;
|
|
717
|
+
self.MAP_TOTAL_PAGES = 3;
|
|
718
|
+
</script>
|
|
719
|
+
</action>
|
|
720
|
+
</before-edit>
|
|
721
|
+
|
|
722
|
+
<onfocusgrupo show-wait-dialog="false">
|
|
723
|
+
<action name="runscript">
|
|
724
|
+
<param name="index"/>
|
|
725
|
+
<script language="javascript">
|
|
726
|
+
self.MAP_GROUP = index;
|
|
727
|
+
</script>
|
|
728
|
+
</action>
|
|
729
|
+
</onfocusgrupo>
|
|
730
|
+
|
|
731
|
+
<ir show-wait-dialog="false">
|
|
732
|
+
<action name="runscript">
|
|
733
|
+
<param name="direccion"/>
|
|
734
|
+
<script language="javascript">
|
|
735
|
+
let nuevo = parseInt(self.MAP_GROUP) + parseInt(direccion);
|
|
736
|
+
if (nuevo >= 1 && nuevo <= self.MAP_TOTAL_PAGES) {
|
|
737
|
+
ui.showGroup(nuevo);
|
|
738
|
+
}
|
|
739
|
+
</script>
|
|
740
|
+
</action>
|
|
741
|
+
</ir>
|
|
742
|
+
</coll>
|
|
743
|
+
```
|
|
744
|
+
|
|
745
|
+
**Elementos clave del patron wizard:**
|
|
746
|
+
- `group-swipe="true"` habilita deslizar entre pasos
|
|
747
|
+
- `MAP_GROUP` rastrea el paso actual
|
|
748
|
+
- `MAP_TOTAL_PAGES` almacena el número total de pasos
|
|
749
|
+
- `disablevisible="MAP_GROUP=1"` oculta el botón "Atrás" en el primer paso
|
|
750
|
+
- `disablevisible="MAP_GROUP=MAP_TOTAL_PAGES"` oculta "Siguiente" en el último paso
|
|
751
|
+
|