A continuación encontrarás traducido al Castellano el artículo escrito por Ricardo Cruz y publicado originalmente en el Blog oficial de Xojo.
Si te estás preguntando por qué deberías de utilizar un WebDataSource en tu WebListBox entonces este artículo te proporcionará los argumentos y consejos para que puedas tomar la decisión e implementarla.
¿Por qué necesito WebDataSource? ¿No es suficiente con AddRow?
Si estás creando un prototipo rápido o para pequeños conjuntos de datos que no van a aumentar en exceso, entonces sí: puedes simplemente utilizar un WebListBox en tu WebPage y rellenarlo usando AddRow. Si tu WebListBox va a mostrar una cantidad fija y reducida de datos (digamos que en torno a 500~1000 filas), entonces el uso de WebDataSource puede resultar excesivo.
Sin embargo, cuando se trata de mostrar en el WebListBox los datos procedentes de una base de datos de gran tamaño… entonces la cosa cambia. Estarías desperdiciando una valiosa cantidad de recursos del servidor duplicando en la memoria de tu app los mismos datos que tienes en tu base de datos; e incluso peor: los datos se duplicarían en cada sesión que estuviese utilizando dicho WebListBox.
Este es el WebListBox que crearemos en este tutorial:
Preparemos una base de datos con un millón de registros
En este ejemplo utilizaremos una humilde base de datos SQLite, pero recuerda que un WebDataSource puede ser cualquier otro objeto. Por ejemplo, podrías estar mostrando el resultado obtenido al llamar a una API externa, o bien al sistema de archivos de tu disco duro.
El siguiente SQL creará una tabla llamada “things” con tres columnas: “foo”, “bar” y “baz” con un millón de registros:
CREATE TABLE things ( id INTEGER PRIMARY KEY AUTOINCREMENT, foo TEXT, bar TEXT, baz TEXT ); -- Hora de poblar la tabla WITH RECURSIVE cnt(x) AS ( SELECT 1 UNION ALL SELECT x + 1 FROM cnt LIMIT 1000000 ) INSERT INTO things (foo, bar, baz) SELECT 'foo_' || x AS foo, 'bar_' || x AS bar, 'baz_' || x AS baz FROM cnt; -- Por último, crearemos algunos índices para que la ordenación de las columnas sea más rápida CREATE INDEX "idx_foo" ON things ( foo, id, bar, baz ); CREATE INDEX "idx_bar" ON things ( bar, id, foo, baz ); CREATE INDEX "idx_baz" ON things ( baz, id, foo, bar ); VACUUM;
Sólo hemos de almacenar este SQL en una constante de tipo String para que resulte más conveniente. Creemos una constante llamada kDatabaseSetupSQLite en nuestra clase Application y pega como su valor el anterior código SQL.
Configuración Inicial
La primera vez que ejecutemos la aplicación tendremos que asegurarnos de que nuestro archivo de base de datos esté creado y poblado con registros. No llevará mucho tiempo generar la base de datos pero, en cualquier caso, mejor hacerlo sólo una vez.
Crea un nuevo método en tu clase App llamado DBFile y que devuelva un FolderItem apuntando a la ubicación en la que queramos guardar la base de datos. Asegúrate de que su Scope (ámbito) sea Public (Público), dado que llamaremos posteriormente a dicho método desde el objeto Session. Introduce el siguiente código en el Editor de Código asociado con el nuevo método:
Return SpecialFolder.Desktop.Child("test-db.sqlite")
Nada del otro mundo; puedes utilizar cualquier otra ubicación que quieras. El archivo tendrá un tamaño en torno a los ~200 MB, de modo que recuerda eliminar dicho archivo cuando ya no lo necesites más.
Crea un nuevo método en el objeto App llamado SetupDatabase. Este será el encargado de crear y poblar sólo una vez la base de datos, de modo que si reinicias el servidor dicha información aun seguirá en su sitio:
Var db As New SQLiteDatabase db.DatabaseFile = DBFile db.WriteAheadLogging = True // El archivo ya existe, de modo que no tenemos que crearlo de nuevo If db.DatabaseFile.Exists Then Return End If db.CreateDatabase db.Connect db.ExecuteSQL(kDatabaseSetupSQLite)
Por último, implementa el evento App.Opening y añade una llamada al método SetupDatabase:
SetupDatabase
Con eso sería suficiente.
Prepara la Sesión
Es recomendado contar con una instancia de la base de datos por cada Sesión. Añade una nueva propiedad de ámbito Público llamada “Database” a tu objeto Session y cuyo tipo sea SQLiteDatabase.
Añade a continuación un manejador para el evento Opening que incluya el siguiente código:
Database = New SQLiteDatabase Database.DatabaseFile = App.DBFile Database.WriteAheadLogging = True Database.Connect
Cada vez que un usuario llegue a tu aplicación web, se creará una conexión SQLiteDatabase aislada.
Implementar la interface WebDataSource
Desde Xojo 2024r2 hemos reducido la cantidad de métodos necesarios a la hora de implementar WebDataSource.
Crea una nueva clase llamada DatabaseDataSource. Luego, en el panel Inspector, selecciona la interface WebDataSource:
Dicha acción incluirá los tres métodos requeridos por dicha interface de clase: ColumnData, RowCount y RowData.
ColumnData
En este método hemos de devolver un array de WebListBoxColumnData. Contamos con cuatro columnas en este ejemplo, de modo que el código necesario sería el siguiente:
Var result() As WebListBoxColumnData result.Add(New WebListBoxColumnData("ID", "id")) result.Add(New WebListBoxColumnData("Foo", "foo")) result.Add(New WebListBoxColumnData("Bar", "bar")) result.Add(New WebListBoxColumnData("Baz", "baz")) Return result
RowCount
Xojo necesita saber la cantidad de datos que tiene tu fuente de datos. Es suficiente con devolver un valor de tipo entero, pero en este caso consultaremos a nuestra base de datos:
Try Var rows As RowSet = Session.Database.SelectSQL("SELECT COUNT(*) AS counter FROM things") Return rows.Column("counter").IntegerValue Catch DatabaseException Return 0 End Try
RowData
Aquí es donde tendrás que devolver los contenidos propiamente dichos de cada fila. Como puedes ver dispones de los parámetros rowCount y rowOffset, de modo que no tendrás que devolver un millón de registros. El control sólo cargará un subgrupo de los mismos. La cantidad se calculará de forma dinámica y variará en función de la altura del WebListBox y también de la altura de las filas.
También se proporciona el parámetro sortColumns. Si permites que tus usuarios ordenen las columnas, tendrás que utilizar dicho parámetro para saber la columna y la dirección. Por fortuna en este ejemplo podremos ordenar las columnas, y este este es el código que se encargará de ello para cumplir con la interface WebDataSource:
Var sql As String = "SELECT id, foo, bar, baz FROM things" If sortColumns <> "" Then sql = sql + " ORDER BY " + sortColumns End If sql = sql + " LIMIT " + rowOffset.ToString + ", " + rowCount.ToString Var result() As WebListBoxRowData Var rows As RowSet = Session.Database.SelectSQL(sql) // Esto no es necesario, sólo es para demostrar cómo usar generadores de celdas Var style As New WebStyle style.Bold = True style.BackgroundColor = Color.Teal style.ForegroundColor = Color.White For Each row As DatabaseRow In rows Var newRowData As New WebListBoxRowData newRowData.PrimaryKey = row.Column("id").IntegerValue newRowData.Value("id") = row.Column("id") newRowData.Value("foo") = row.Column("foo") newRowData.Value("bar") = New WebListBoxStyleRenderer(style, row.Column("bar")) newRowData.Value("baz") = row.Column("baz") result.Add(newRowData) Next Return result
Diseñar la Interfaz de Usuario
Hemos llegado a la parte más sencilla. Crear la interfaz de usuario nos llevará menos de un minuto.
- Suelta un control DatabaseDataSource en tu WebPage
- Suelta un control WebListBox en tu WebPage
- Configura el DataSource en el evento Opening del WebListBox
Si has nombrado tu DataSource como “MyDataSource”, entonces esta será la línea de código requerida en el evento Opening del WebListBox:
Me.DataSource = MyDataSource
Este es un vídeo breve:
¡Eso es todo!
El control WebListBox combinado con un WebDataSource es una solución muy robusta y eficaz para mostrar un gran conjunto de datos.
Descarga el proyecto:
weblistbox-million-rows.xojo_binary_project
¡Feliz programación con Xojo!