¿Cómo leer un fichero de Excel en ASP clásico?

Pues me encontraba estos días trabajando con una aplicación web antigua en ASP clásico que requería una nueva funcionalidad: importar datos que el cliente recibiría en una hoja de Excel, una hoja que en principio siempre tendrá el mismo formato. La duda era ¿cómo hago para leer el fichero de Excel con el viejo VBScript? Pues en este caso he usado el motor de Access 2010 (puede descargarse desde la web de Microsoft) y su conector OLEDB para tratar la hoja de Excel como si fuera una tabla SQL. Aquí os dejo un ejemplo del código:

Declare ExcelFile, Consulta, ExcelConnection, RSDatos
ExcelFile = "C:\Users\MiUsuario\MiCarpeta\MiExcel.xls"

Consulta= "SELECT [Campo1], [Campo2],[Campo3] FROM [Hoja1$]"
Set ExcelConnection = Server.createobject("ADODB.Connection")
ExcelConnection.Open "Provider=Microsoft.ACE.OLEDB.12.0;Data Source=" & ExcelFile & ";Extended Properties=""Excel 12.0 Xml;HDR=YES;IMEX=1"";"
SET RSDatos= Server.CreateObject("ADODB.Recordset")
RSDatos.Open Consulta, ExcelConnection

En resumen: creo una consulta para sacar los datos como si fuera un SQL, creo un objeto de conexión ADODB, me sirvo del motor de Access para pasarle como fuente de datos la ruta del fichero excel, creo un recordset y guardo los datos de la consulta en el mismo. Ahora ya puedo usar a mi gusto los datos almacenados en el recordset.

Javascript: sobreescribir el comportamiento del botón derecho del ratón en una web.

A veces, por lo que sea, en una aplicación web necesitamos sobreescribir el comportamiento standar del click derecho del ratón, que en lugar de abrirnos el menú contextual abra algo personalizado. Veamos lo básico ¿Cómo hacemos que en lugar del comportamiento por defecto ejecute el código que queramos? Pues recogiendo el evento y escribiendo nuestro propio código:

if (document.addEventListener) {
    document.addEventListener('contextmenu', function(e) {
        alert("NANAY DE ABRIR!!!!!"); //Aquí iría el código que quieres ejecutar en lugar del comportamiento por defecto
        e.preventDefault(); //esta línea evita el comportamiento por defecto
     }, false);
 } else {
    document.attachEvent('oncontextmenu', function() {
        alert("NANAY DE ABRIR"); ///aquí una alternativa
        window.event.returnValue = false;
    });
}

Abrir Google Chrome en modo aplicación

Hace un tiempo veíamos como abrir Google Chrome en modo kiosko, una forma de abrir el navegador ocupando toda la pantalla, pensado para ejecutar web-apps en pantallas táctiles como puedan ser puestos de información, sistemas de recogida de tickets, etc… ya que sin un teclado no es posible cerrar la aplicación. Si lo que queremos es abrir el Chrome simplemente sin marcos del navegador ni barras de herramientas, solo la web con su cabecera de ventana, entonces tendremos que usar el modo app. Tenemos dos opciones:

La primera opción es navegar hasta la página que queremos usar en modo app, irnos al menú y allí dependerá de si estamos usando la versión para Android o para PC.

  • En caso de Android pulsamos Añadir a Pantalla de Inicio y ya tendremos el acceso en nuestro teléfono como si fuera un app más.
  • En caso de un PC nos ponemos sobre Más Herramientas y seleccionamos Crear Acceso Directo, antes de aceptar marcamos la casilla de Abrir como Ventana.

Otra opción si trabajamos con un PC es pasarle parámetros en la llamada que abre Chrome. Por ejemplo, en caso de trabajar con Windows crearíamos un acceso directo al ejecutable de Chrome y justo después de la ruta añadiríamos estos parámetros:

--app=https://sitiowebquequeremosabrir.com --new-window

También podríamos crear un script que abra Chrome y ponerle esos mismos parámetros en la llamada, de esa forma por ejemplo podríamos hacer un script en Ubuntu que lo abra tal que así:

$ google-chrome --app=https://sitiowebquequeremosabrir.com

Crear una tabla HTML con cabecera fija, usando CSS3

Si bien los elementos table suenan a cosa del pasado, a aquellos tiempos en los que todo se hacía con tablas y de vez en cuando algo de flash o similar, todavía siguen utilizándose tablas en el mundo de la programación y diseño web, ya no como elemento central y básico de la maquetación, pero sí para por ejemplo presentar datos en ese formato de filas/columnas.

Si tenemos una tabla muy larga la solución tradicional ha sido siempre la paginación, que con el tiempo fue sustituída por elementos que van insertando nuevas filas de forma asíncrona mientras hacemos scroll. En estos casos puede pasar que hayamos bajado mucho, la cabecera ya no esté visible y no tengamos muy claro a qué corresponde cada columna. Una solución podría ser repetir la cabecera cada x filas, pero se puede hacer visualmente incómodo. Otra posibilidad es hacer fijas estas cabeceras de forma que siempre estén visibles en pantalla y bajen mientras hacemos scroll.

Parece que lo lo lógico sería que pudiésemos hacer fijos los elementos thead o tr, ya que queremos que toda la línea quede fijada, pero aunque nos parezca lo elemental no será posible. Para fijar un elemento de esta forma tenemos que ponerle position:relative y esta característica no se aplica a esos elementos, esto implica que tendremos que usarlo obligatoriamente sobre las celdas, sobre los elementos th o td.

Supongamos que tenemos una tabla como esta (bueno, con cientos de registros para que tenga sentido la cabecera fija):

<table>
  <thead>
    <tr>
       <th>Nombre</th>
       <th>Edad</th>
       <th>Salario</th>
       <th>Rango</th>
       <th>Area</th>
     </tr>
    </thead>
   <tbody>
    <tr>
       <td>Manuela Z.</td>
       <td>43</td>
       <td>24000</td>
       <td>Analista</td>
       <td>Big Data</td>
     </tr>
  </tbody>
</table>

Si queremos fijar la cabecera el CSS, lo más básico posible, sería tal que así:

table {
  position: relative;
  border-collapse: collapse; 
}
th, td {
  padding: 6%;
}

th {  
  position: sticky;
  top: 0; 
}

Hay tres puntos clave en ese CSS: el position:relative de la tabla, el position:sticky del elemento th y la posición top:0 también del elemento th. De esta forma le decimos que todas las celdas de la cabecera se queden fijas en la parte superior de la tabla, así al bajar el scroll estas bajarán con él.

HTML: Hacer que un campo file solo acepte ficheros XML.

Una entrada cortita sobre programación, que hace tiempo que no pongo nada. Hoy estaba modificando un formulario desde el que se sube un fichero XML, controlando mediante javascript que no se intente subir un ficheron con otra extensión. El problema viene cuando los usuarios intentan subir un fichero .rar o un .pdf, les salta el mensaje de error y en lugar de leer el mensaje cogen el teléfono para llamar a atención técnica. ¿Podría limitar que en el selector de ficheros ya solo aparezca ficheros xml y evitar esos errores? Sí, se puede.

Básicamente en el campo imput nos serviremos de la propiedad accept para definir esto, tal que así:

<input type="file" name="SubirXML" id="SubirXML" accept="text/xml" />

Poniendo el valor text/xml en la propiedad accept ya solo nos mostrará en el selector ficheros xml o carpetas por las que seguir navegando.

¿Se pueden aplicar otros formatos? Pues claro, en este enlace tienes la lista de todos los tipos MIME que se pueden definir.

Javascript: cerrar una ventana automáticamente cuando se cierre el diálogo de impresión.

Veamos el caso práctico que me encontré: una aplicación web que para imprimir un documento abre un popup con la previsualización del mismo, cuando pulsas el botón de imprimir debería abrirse el diálogo de impresión y después de aceptar el popup debería cerrarse… pero resulta que si bien en el Safari y el Internet Explorer lo hace correctamente, en el Chrome o en el Firefox se cierra la ventana antes de que se abra el diálogo de impresión. ¿Cómo hacemos para que se cierre en todos los navegadores automáticamente esa ventana tras confirmar el diálogo de impresión?

Pues como el objeto window en Javascript es capaz de reconocer el evento de que se ha cerrado la ventana de impresión (evento onafterprint), bastará con que programemos que se ejecute window.close() cuando se de dicho evento. El código sería:

window.onafterprint = function () {
    window.close();
}

Memoization: Acelerando funciones recursivas. Ejemplo con Javascript.

En su día hablamos aquí sobre la recursividad y sus «peligros«. Las funciones recursivas son muy costosas a nivel de recursos pues al llamarse a si mismas repiten varias veces el mismo paso ¿hay una solución?

Memoization (memorización) es una técnica consistente en almacenar los resultados de funciones muy costosas para devolverlos directamente en lugar de volver a calcularlos.

Por ejemplo, la siguiente función que ya vimos en un artículo anterior nos permitiría calcular un factorial con javascript:

function factorial(num)
{
    if (num < 0) {
        return 0;
    }
    else if (num == 0) {
        return 1;
    }
    else {
        return (num * factorial(num - 1));
    }
}

¿Podemos optimizar mucho esta función cacheando los datos? Podemos:

function factorial(num) {
    // inicializamos si es necesario
    if (!factorial.cache) {
        factorial.cache = {};
    }
    if (num == 0) {
        return 1;
    } else if (num in factorial.cache) {
    // si ya tenemos en cache el factorial de num, lo devolvemos
        return factorial.cache[num];
    } else {
    factorial.cache[num] = num * factorial(num - 1);
    return factorial.cache[num];
}
 

De esta forma nos ahorramos varias llamadas a la función pues simplemente devolvemos un valor almacenado en memoria. Esta no es una técnica exclusiva de Javascript, podemos utilizarla con otros lenguajes de programación. Javascript sí tiene la particularidad de tratar las funciones como un objeto más, eso nos permite por ejemplo definirle la propiedad cache en el ejemplo de arriba (en otros lenguajes deberíamos pasarle como parámetro a la función el array de valores o declararlo como variable global), esto también nos permite hacer que una función reciba como parámetro una función o devuelva como resultado otra función, lo que se conoce como higher-order function o función de orden superior. Sirviéndonos de esto podríamos declarar una función genérica para nuestro memoization:

const memoization = function(func){
    const cache = {};
    return (...args) => {
        const key = [...args].toString();
        return key in cache ? cache[key] : (cache[key] = func(...args));
    }
}

De esta forma podríamos definir nuestra función factorial() tal que así:

var factorial = memoization(function(num) {		
    return (num <= 1) ? 1 : num * factorial(num-1);
})

Añadir un PDF a un sitio web usando una etiqueta de HTML5

Existen varias formas de insertar un PDF dentro de un sitio web, una de ellas es usar alguna de estas etiquetas:

  • embed
  • object
  • iframe

Antaño solo teníamos la opción de usar la etiqueta iframe, dado que embed y object se añadieron para la especificación HTML5. En todo caso el procedimiento con las tres es el mismo: ponemos la URL del fichero PDF como valor del atributo src y definimos el tamaño mediante los atributos width y height:

<!--Ejemplos varios-->
<iframe src="http://rutaamipdf/fichero.pdf" width="95%" height="100%"></iframe>

<embed src="http://rutaamipdf/fichero.pdf" width="95%" height="100%"></embed>

<object src="http://rutaamipdf/fichero.pdf" width="95%" height="100%"></object>

¿Problemas? Pues sí, que con este método dependemos de que el navegador que está usando el usuario tenga incorporado algún visualizador de PDF. En algunos casos tiene que ser mediante un plugin externo, como en Chrome, en otros ya viene incluído por defecto en el propio navegador.

Emular el funcionamiento de un operador ternario ?: en ASP clásico con VBScript

Hemos hablado sobre el operador ternario ?: en Java o como emularlo en Python. Pero ¿Existe en el viejo VBScript? Pues no. ¿Qué soluciones tenemos?

Realmente cuando trabajamos en ASP Clásico el VBScript nos permite hacer un if-else en una sola línea tal que así:

If diaDeLaSemana < 6 Then seTrabaja = True Else seTrabaja = false

Por otra parte podríamos crear una función que emule su funcionamiento. Recibiría como parámetros una expresión que nos devuelva un valor booleano, una cadena que devolver en caso de que la expresión sea cierta y otra para el caso de que sea falsa:

Function IfT(bExpr, rVerdadera, rFalsa)
    If CBool(bExpr) Then
        IfT = rVerdadera
    Else 
        IfT = rFalsa
    End If
End Function

Veamos un ejemplo:

HoyQueHago = IfT(diaDeLaSemana < 6, "Currar", "Descansar")

Si el día de la semana es menor que seis (es decir, de lunes a viernes) devolvería «Currar» y si es mayor o igual (sábado o domingo) devolvería «Descansar«.

Javascript: funciones querySelector() y querySelectorAll()

Las funciones querySelector() y querySelectorAll() existen en Javascript como métodos del objeto document. Ambas nos permiten acceder a elementos del DOM utilizando un selector de CSS.

La función querySelector() nos devolverá el primer elemento que coincida con el selector que le pasemos. Si es un id no habría problema puesto que deberían ser únicos en el documento, pero si se tratase de una clase o un elemento HTML entonces nos devolvería solo la primera ocurrencia:

//Si es un id nos devolverá ese elemento
var i = document.querySelector('#Contenedor');

//pero si es un elemento que existe varias veces
//entonces solo tendremos la primera ocurrencia
var x = document.querySelector('p');

//esto pondría el fondo gris al primer p que haya 
//en nuestro documento
x.style.backgroundColor = "#d9d9d9";

¿Y si queremos todos los elementos?

En ese caso tenemos que usar querySelectorAll(). El funcionamiento es igual que el de querySelector(), pero en este caso nos devolverá un objeto Nodelist que contiene todos los elementos que coincidan con el selector que hemos buscado. En este caso no tendría sentido usarlo con id, ya que debería ser un elemento único.

//Aquí tendríamos una lista estática con todos los elementos p
var x = document.querySelector('p');

¿Y cómo accedemos a los elementos del Nodelist?

Al ser una lista podemos acceder a los elementos contenidos en el Nodelist que nos devuele querySelectorAll() mediante un índice. Podemos conocer el tamaño de la lista accediendo a la propiedad length e iterar todos los elementos dentro de un bucle:

//Aquí tendríamos una lista estática con todos los elementos p
var x = document.querySelector('p');

//ahora los recorremos y, para el ejemplo, cambiaremos el color
//de fondo por un gris
var i;
for (i = 0; i < x.length; i++) {
  x[i].style.backgroundColor = "#d9d9d9";
}