Visual Studio Code: comentar y descomentar varias líneas

Llevo una temporadita que trabajo principalmente con Visual Studio Code (como sabréis por aquel meme) así que de vez en cuando puede que deje por aquí algún truquillo-consejo (que seguramente ya hayan comentado mil blogs más). Empecemos por este ¿Cómo se puede comentar rápidamente un montón de líneas?

Pues para comentar en Windows o en Linux primero seleccionamos las líneas que queremos comentar, después pulsamos la tecla CTRL y mientras la mantenemos pulsada también pulsamos primero la tecla K y luego la tecla C. En caso de que trabajéis con uno de esos equipos propiedad de Apple entonces tendréis que cambiar la tecla CTRL por la tecla CMD.

Pues para descomentar en Windows o en Linux es similar, primero seleccionamos las líneas que queremos comentar, después pulsamos la tecla CTRL y mientras la mantenemos pulsada pulsamos primero la tecla K y luego la tecla U. De nuevo en caso de que trabajéis con uno de esos equipos propiedad de Apple, tendréis que cambiar la tecla CTRL por la tecla CMD.

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.

Generar un fichero de EXCEL con ASP

Una entrada rápida: queremos que una página ASP en lugar de mostrarnos la información en el navegador lo que haga sea generar un fichero de EXCEL y que se descargue en nuestro equipo ¿Es posible? Sí.

Solo necesitamos añadir dos cabeceras al inicio del documento:

Response.ContentType="application/vnd.ms-excel"
Response.AddHeader "Content-Disposition", "attachment; filename=NombreFichero.xls"

PostgreSQL: Instrucciones básicas de gestión de tablas

Una entrada básica de SQL ¿Cuáles son las instrucciones básicas de gestión de tablas en el SGBD PostgreSQL? Veámoslas:

Crear tabla (para el ejemplo crearemos una tabla de de tres columnas, con un id autonumérico, una columna de texto que no admite nulos y una columna para números decimales con un valor por defecto de 0):

CREATE TABLE t (
  id SERIAL PRIMARY KEY,
  columna1 VARCHAR NOT NULL,
  columna2 NUMERIC(10,2) DEFAULT 0
);

Para borrar una tabla tenemos dos opciones, la primera de ella será hacerlo con el parámetro RESTRICT o sin ningún parámetro, pues esta es la opción por defecto. Si hay objetos que dependan de esa tabla, como vistas o claves externas, no borrará la tabla y nos devolverá un mensaje de error informándonos:

DROP TABLE laTablaQueSea RESTRICT;

La otra opción es borrar en casacada, con el parámetro CASCADE. En este caso borra la tabla y todos los objetos que dependan de la misma.

DROP TABLE laTablaQueSea CASCADE

Si lo que queremos es añadir una columna entonces tenemos que usar ALTER TABLE seguida de la instrucción ADD COLUMN especificando el nombre de columna, el tipo de datos y las restricciones que se aplicarán sobre la misma.

ALTER TABLE laTablaQueSea
ADD COLUMN nombreDeColumna tipoDeDatos constraint;

Si queremos añadir varias columnas solo tenemos que repetir la instrucción ADD COLUMN separada por comas:

ALTER TABLE laTablaQueSea
ADD COLUMN nombreDeColumna tipoDeDatos constraint,
ADD COLUMN nombreDeColumna2 tipoDeDatos constraint,
ADD COLUMN nombreDeColumna3 tipoDeDatos constraint;

En cambio si queremos borrar una columna tenemos que usar DROP COLUMN seguido del nombre de la columna:

ALTER TABLE laTablaQueSea DROP COLUMN nombreDeColumna;

Para deshabilitar los triggers vinculados a una tabla tenemos que usar también ALTER TABLE:

ALTER TABLE laTablaQueSea DISABLE TRIGGER ALL;

Para volver a habilitar estos triggers sería lo mismo pero con el parámetro ENABLE:

ALTER TABLE laTablaQueSea ENABLE TRIGGER ALL;

Para añadir alguna restricción a la tabla también usamos ALTER TABLE, en ese caso seguido de ADD y de la restricción que sea.

ALTER TABLE laTablaQueSea ADD laRestriccionQueQuieras;

Para borrar una restricción sobre una tabla utilizamos ALTER TABLE, pero ahora con DROP:

ALTER TABLE laTablaQueSea DROP laRestriccionQueQuieras;

Para renombrar una tabla tenemos que usar RENAME después del ALTER TABLE:

ALTER TABLE laTablaQueSea RENAME TO otroNombreDeTabla;

Y para renombrar una columna es similar:

ALTER TABLE laTablaQueSea RENAME columna1 TO columna2 ;

Finalmente si queremos borrar todos los datos de una tabla sin borrar la tabla, nuestra opción es la instrucción TRUNCATE TABLE:

TRUNCATE TABLE laTablaQueSea CASCADE;

Búsqueda binaria o dicotómica

Durante el corto tiempo que tuve cuenta en CuriousCat (lo borré porque para recibir insultos anónimos ya me llega con Tweeter y los comentarios de este blog) alguien me pidió que escribiera sobre la diferencia entre una búsqueda binaria y una búsqueda dicotómica. Eso se lo contesté rápidamente allí: búsqueda dicotómica y búsqueda binaria son sinónimos, no hay diferencia.

¿En qué consiste la búsqueda binaria?

Cuando hablamos de búsqueda binaria hablamos de un algoritmo, una serie de instrucciones para que un programa informático realice una tarea. Concretamente de un algoritmo de búsqueda, es decir, la serie de instrucciones necesarias para encontrar un valor dentro de una colección de valores. La búsqueda binaria está pensada para buscar un elemento dentro de una colección ordenada. Vamos a explicar cómo funciona con un ejemplo: imagínate que tuvieras una lista con todos los habitantes de Barcelona ordenada por la primera letra de su apellido, tienes que buscar a alguien que se apellida «Martínez» ¿te pondrías a mirar la lista desde el principio hasta llegar a ese nombre? Son millones de comprobaciones. Ese procedimiento sería el de una búsqueda lineal: comprobar todos los valores hasta encontrar el deseado, funciona pero es muy lento, tiene utilidad cuando no queda más remedio (una colección desordenada) pero es demasiado ineficiente para una lista ordenada.

Aplicando una búsqueda binaria a ese ejemplo lo que haríamos sería encontrar primero el valor del medio de la lista, dividir la lista en dos mitades y comprobar si el valor intermedio es el que buscamos, es menor o es mayor. Si se diera la casualidad de que es el que buscamos la búsqueda ya estaría finalizada, si el valor intermedio es menor descartaríamos la mitad inferior de la lista y si es mayor descartaríamos la mitad superior. Ahora que localizamos en qué mitad tiene que estar nuestro valor repetimos el procedimiento sobre esa mitad, creando otras dos mitades y de nuevo repitiendo el procedimiento hasta dar con nuestro valor. En cada iteración descartamos la mitad de los datos que teníamos, lo que reduce el tiempo de búsqueda respecto a la lineal.

¿Se usa este algoritmo?

La mayoría de lenguajes de programación o frameworks ya tienen funciones de búsquea integradas muy bien optimizadas, así que no es habitual que alguien tenga que escribir una función de búsquea binaria, excepto en ejercicios académicos para aprender algoritmia. Yo personalmente he utilizado este método para buscar entre conjuntos de datos manualmente, cuando no disponía de un índice. Aquí abajo os dejo una implementación del algoritmo en Javascript:

function buscaDicotomica(valor, conjunto) {
    //valor es lo que vamos a buscar, cojunto es el array donde lo buscamos
    var ini = 0;    //inicio del array
    var fin = conjunto.length - 1;   //fin del array
    var pos = -1;
    var finaliza = false;
    var media;
 
    while (finaliza === false && ini<= fin) {
        media= Math.floor((ini+ fin)/2);
        if (conjunto[media] == valor) {
            finaliza = true;
            pos  = media;
        } else if (conjunto[media] > valor) {  //si está en la mitad inferior
            fin= media - 1;
        } else {  //si está en la mitad superior
            ini= media+ 1;
        }
    }
    return pos;
}

Script de PowerShell para enviar un correo con Gmail

Un compañero necesitaba un script de PowerShell que enviase un correo con un texto concreto desde su dirección de email a otra dirección fija, pero no lograba que funcionase correctamente utilizando su cuenta de Gmail. En su día por aquí, ya hace años, hablamos sobre cómo enviar correos desde una cuenta de Gmail usando PHPMailer, así que durante la hora de comer le he echado una mano y he hecho algo sirviéndome del cmdlet Send-MailMessage:

$username   = 'tucorreo@gmail.com'
$password   = 'tupassword'
$secstr     = New-Object -TypeName System.Security.SecureString
$password.ToCharArray() | ForEach-Object {$secstr.AppendChar($_)}

$brocolharum = @{
    from       = "tucorreo@gmail.com"
    to         = "correoquerecibe@gmail.com"
    subject    = "Danger Danger, high voltage!"
    smtpserver = "smtp.gmail.com"
    port       = "587"
    body       = "Si te estás leyendo esto me debes un capuccino con licor de avellana"
    credential = New-Object -typename System.Management.Automation.PSCredential -argumentlist $username, $secstr
    usessl     = $true
    verbose    = $true
    DeliveryNotificationOption = "OnFailure"
}

Send-MailMessage @brocolharum

Se trata de un ejemplo muy básico, después podéis adaptarlo a vuestras necesidades.

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();
}

Sobreescribir la función javascript window.alert() nativa del navegador por una personalizada

Resulta me veía con una cuestión trabajosa: en una aplicación web tenía que hacer una ventana de alerta personalizada que sustituyera al window.alert() nativo del navegador. La parte de diseñar la nueva ventana no era tan laboriosa, la labor pesada y repetitiva venía del tener que localizar todas las ocurrencias de window.alert() diseminadas por el código y cambiarlas por la nueva función. Y ahí pensé «¿y si pudiera cambiar el window.alert() por la nueva función?«. Hay un fichero de javascript que está presente en todas las pantallas, así que solo tendría que introducirlo allí.

¿Y cómo reescribimos la función window.alert()? Pues simplemente así:

window.alert = function(){}

Asignando una nueva función a window.alert() sobreescribe su comportamiento nativo por el indicado. Veámoslo de nuevo con más o menos todo el código que lleva:

//alert personalizado, recibe el mensaje a mostrar y el elemento sobre el que poner el foco si es un mensaje de error
window.alert = function(msg,el,isError){
	var left = parseInt((screen.width - 750) / 2);
	var top = parseInt((screen.height-300)/2);
	var ventana = window.open("AvisoNuevo.asp?e="+isError+"&m="+msg,"modal","width=750,height=300,top="+top+",left="+left+",scrollbars=auto,resizable=no,menubar=no,toolbar=no");	
	if(isError && el!=null && el!="" && el!=false){			
		try {
		  document.getElementById(el).focus();
	    }
	     catch (e) { return; }
	}
}

Electron.js: Error «require is not defined» y conflictos entre require y jQuery.

Aquí sigo trabajando en mi primera aplicación corriendo sobre Electron.js y me están saliendo canas por diversos errores que me aparecen a la hora de integrar. Uno de ellos ha sido recibir constantemente el error «require is not defined«. Una de las causas de este error suele ser que la integración con NodeJS está deshabilitada, por seguridad este parámetro viene deshabilitado por defecto para evitar ataques que puedan, por ejemplo, borrar ficheros de nuestro equipo remotamente si cargamos una página no segura. En caso de que estemos cargando una aplicación local podemos ir al main.js y activarlo desde allí configurando las preferencias de nuestro ventana web:

 webPreferences: {
            contextIsolation: false,
            nodeIntegration: true,
            nodeIntegrationInWorker: true,                                                  
            enableRemoteModule: true                                                                     
 }

Es posible que esta configuración nos genere un nuevo conflicto entre jQuery y NodeJS ya que ambos intentan sobreescribir la función require(). Esto puede solucionarse añadiendo esta línea antes de la carga de jQuery:

<script>
		window.nodeRequire = require;
		delete window.require;
		delete window.exports;
		delete window.module;
</script>