Autor en Google+
Saltar al contenido

Artículos, tutoriales, trucos, curiosidades, reflexiones y links sobre programación web ASP.NET Core, MVC, Blazor, SignalR, Entity Framework, C#, Azure, Javascript... y lo que venga ;)

15 años online

el blog de José M. Aguilar

Inicio El autor Contactar

Artículos, tutoriales, trucos, curiosidades, reflexiones y links sobre programación web
ASP.NET Core, MVC, Blazor, SignalR, Entity Framework, C#, Azure, Javascript...

¡Microsoft MVP!
miércoles, 28 de noviembre de 2007
De vez en cuando reviso las palabras por las que se llega a Variable Not Found a través de los buscadores, más que nada porque suelen reflejar dudas o problemas comunes de los desarrolladores y pueden servir como inspiración sobre nuevos temas a tratar en el blog.

Pues bien, llevo tiempo observando que hay muchas personas que preguntan a Google algo similar a "anular postback en botón", es decir, se tienen dudas sobre cómo evitar el disparo del postback al pulsar un botón de un formulario ASP.Net. El tema me ha parecido tan interesante que intentaré dar una respuesta a la duda estudiando algunas de las distintas posibilidades que pueden darse. Eso sí, si echáis en falta algún caso, no dudéis en comentarlo y lo trataremos lo antes posible.


I. ¿Hablamos de botones con lógica exclusivamente en cliente?

La primera cuestión es preguntarse para qué queremos un botón que no realice un postback al ser pulsado. Pensad que si todas las acciones desencadenadas por la pulsación del botón se realizarán en cliente es posible que no necesitemos un control ASP.Net. Imaginemos, por ejemplo, un botón que queremos que haga un cálculo en el cliente y muestre un cuadro de alerta con el resultado... ¿es necesario que sea un control de servidor (runat="server")? Seguro que no.

Para este caso lo más apropiado es incluir en el .aspx un HTML INPUT de los de toda la vida:

<input id="Button1" type="button" value="button"
onclick="hacerAlgo();" />
 
Si eres de los que prefieren arrastrar y soltar, puedes hacer exactamente lo mismo utilizando los controles incluidos en la pestaña HTML de Visual Studio, en este caso el llamado Input(Button). El resultado visual será idéntico al de un Button (un botón de servidor) y estaremos aligerando tanto la página enviada al cliente como la carga del servidor.

II. Anular el postback en un botón de servidor

Otra posibilidad es que vayas a usar un control de servidor (un Button, por ejemplo) y sólo bajo determinadas circunstancias quieras anular el postback desde el cliente. Obviamente, estas circunstancias serían calculadas/obtenidas por script, por lo que podríamos aplicar un patrón como el siguiente:

...
<asp:Button ID="Button1" runat="server"
Text="Pulsa"
OnClientClick="return comprueba();" />
...
<script type="text/javascript">
function comprueba() {
return confirm("Confirme el postback");
}
</script>
 
Como se puede observar, se ha modificado la propiedad OnClientClick del botón para hacer que retorne el resultado de la llamada a la función comprueba(). Si ésta retorna cierto, se realizará el postback, mientras que si retorna falso, no lo hará. En el ejemplo anterior se permite al usuario decidir si desea enviar el formulario al servidor o no.

III. Anular la posibilidad de envíos múltiples

Hay otras ocasiones, sin embargo, en las que estamos utilizando un botón de servidor de forma normal y simplemente queremos evitar duplicidades en el envío del formulario, es decir, bloquear el botón correspondiente una vez el usuario lo haya pulsado la primera vez.

Esto es muy habitual debido a la falta de feedback: el usuario envía el formulario, se impacienta esperando la respuesta o no está seguro de haberlo enviado, y cual poseso, inicia un ataque implacable sobre el botón de envío. ¿A que os suena?

El comportamiento típico, adoptado por la mayoría de sitios web, es deshabilitar el botón y seguir con el postback. Sin embargo, esto que podría ser realmente sencillo mediante scripting, causa algún que otro problema cuando el botón es de tipo Submit, puesto que en determinados navegadores (por ejemplo IE6) no realizará el envío si lo está generando un botón deshabilitado. Una forma de evitar este problema es hacer que el botón no sea de tipo submit e incluir un pequeño script:

<asp:Button ID="Button2" runat="server"
Text="Pulsa"
OnClientClick="this.disabled=true"
UseSubmitBehavior="False"/>

Si el botón debe ser obligatoriamente de tipo submit, se deben utilizar soluciones alternativas. Googleando un poco he encontrado varias, como deshabilitar el botón pasadas unas décimas de segundo mediante un timer, o retocar los estilos del botón para, simplemente, hacerlo desaparecer y mostrar en su lugar un mensaje informativo. La mejor, para mi gusto, consiste en capturar el evento onsubmit del formulario web, así:

...
<form id="form1" runat="server" onsubmit="deshabilita()">
...
<script type="text/javascript">
function deshabilita()
{
var btn = "<%= Button1.ClientID %>";
if (confirm("Confirme postback"))
{
document.getElementById(btn).disabled = true;
return true;
}
return false;
}
</script>
 
Este último ejemplo lo he aprovechado para, además, mostrar cómo es posible realizar la captura del envío justo antes de que se produzca, en el evento OnSubmit, e introducir las confirmaciones de envío y deshabilitado de botones.

Publicado en Variable not found.

Estos contenidos se publican bajo una licencia de Creative Commons Licencia Reconocimiento-No comercial-Compartir bajo la misma licencia 3.0 España de Creative Commons

martes, 27 de noviembre de 2007
Aunque he visto que es conocido desde hace tiempo, yo me he enterado ahora de que en http://www.connectionstrings.com/ hay una excelente recopilación de cadenas de conexión a fuentes de datos de todo tipo:
  • Motores de bases de datos SQL Server, SQL Server 2005, SQL Server 2005 Compact Edition, Oracle, MySQL, Interbase, IBM DB2, Sybase, Informix, Ingres, Mimer SQL, Lightbase, Postgre SQL, Paradox, Firebird, AS/400 (iSeries), Pervasive, SQLBase, Progress y Caché.

  • Archivos de datos Excel 2007, Excel, texto plano, Access 2007, Access, Visual FoxPro / FoxPro 2.x DBF / FoxPro, SQLite y Filemaker.

  • Otras fuentes, como MS Project, Active Directory, Exchange, Lotus Notes, DSN y UDL.

Seguro que algún despistado (como el menda, todo sea dicho), todavía no lo conocía. Y es que no hay nada como internet para tener a mano toda la información que necesitamos.

Publicado en: Variable not found

Estos contenidos se publican bajo una licencia de Creative Commons Licencia Reconocimiento-No comercial-Compartir bajo la misma licencia 3.0 España de Creative Commons

lunes, 26 de noviembre de 2007
Hasta hoy no sabía que la arroba, además de permitir la definición de constantes string literales y multilíneas (mi gran descubrimiento del mes pasado), también puede ser utilizado en C# para utilizar nombres reservados del lenguaje como identificadores.

Según la especificación del lenguaje C#, un identificador (de una variable, clase, propiedad...) puede ser una de las palabras clave del lenguaje (for, if, int, string...) si va precedida por una arroba, permitiendo aberraciones como la siguiente:

class @class
{
public static void @static(bool @bool)
{
if (@bool)
System.Console.WriteLine("true");
else
System.Console.WriteLine("false");
}
}
 
En fin, dudo mucho que vaya a utilizar esta posibilidad alguna vez, pero está bien saber que existe.

Publicado en: Variable Not Found.

Estos contenidos se publican bajo una licencia de Creative Commons Licencia Reconocimiento-No comercial-Compartir bajo la misma licencia 3.0 España de Creative Commons

domingo, 25 de noviembre de 2007
Si existieran los diez mandamientos del programador, seguro que uno de ellos sería "comenta tu código". Y es que está claro que la mantenibilidad de una aplicación o módulo es posible siempre que los profesionales encargados de ella sean capaces de entender perfectamente qué hace el software y cómo lo hace, y es aquí donde un código correctamente documentado puede facilitar enormemente la tarea.

Todos los lenguajes de programación facilitan la inclusión de texto libre, no estructurado, con objeto de que el programador explique a futuros mantenedores del software los principales aspectos del código fuente que está observando, el por qué de determinadas decisiones, o, en definitiva, aclarar lo que considere oportuno. Sin embargo, esta libertad y falta de normalización acarrea una serie de inconvenientes que deben ser tenidos en cuenta, y que comentó Chad Myer hace unas semanas.

En primer lugar, es importante ser conscientes de que estos textos están fuertemente ligados al código que comentan y, obviamente, al no ser comprobados por ningún sistema automático (como un compilador), es fácil que contengan errores:

// Retorna la suma de a y b
public int multiplica(int a, int b)
{
...
 
Tampoco puede asegurarse que los comentarios sean útiles; de hecho, parte de los comentarios que leemos son obviables y totalmente innecesarios, auténticos insultos a la inteligencia, del tipo:

if (a > 5) // Si a es mayor que cinco, ...
counter = 0; // ... ponemos el contador a cero
...
 
La inclusión de este tipo de comentarios no harían sino añadir "ruido" a un código perfectamente inteligible de forma directa.

Y hablando de comentarios prescindibles, es curioso ver su utilización como vía de escape y desfogue de algunos programadores, que no dudan en añadir todo tipo de artillería a sus creaciones, como las mostradas en el Contador de palabrotas del Kernel de Linux, arremetiendo contra los usuarios, colegas o todo aquél que pase por delante en el momento apropiado.

Otro hecho bastante frecuente es que los comentarios sean introducidos al crear un código pero que no sean actualizados de forma paralela a éste, lo que da lugar a textos obsoletos y sin sentido en su contexto. Y no hablo sólo de actualizaciones tiempo después de su creación, también las realizadas minutos u horas después de la programación inicial como consecuencia de optimizaciones y refactorizaciones, incluso realizadas por el mismo desarrollador.

// Si x es true, retorna a+b
// Si no, retorna a-b
public int calcula(int a, int b)
{
...
 
A pesar de todos estos problemas, normalmente generados por falta de rigor durante su creación y actualización, los comentarios son absolutamente necesarios para el futuro mantenimiento de un sistema y deben tratarse como parte fundamental en una pieza de código y prestarle la misma atención que a éste. Recordemos la famosa frase de Martin Fowler:
"Cualquier tonto puede escribir código que entienden las computadoras. Los buenos programadores escriben código que entienden las personas"


Para ello, como dice Bernhard Spuida en su magnífico documento The Fine Art of Commenting, hay que luchar contra actitudes negativas como:
  • el orgullo del programador ("no me hacen falta comentarios para entender lo que hace cualquier código", "yo escribo código que entiende cualquiera"). No te engañes, un código medianamente complejo te llevará tiempo entenderlo en cuanto hayan pasado unos meses incluso a tí mismo, así que imagina a otro desarrollador.
  • la pereza y procrastinación ("ya comentaré el código más adelante"). No te engañes, no lo harás; además, se estima que el tiempo necesario para añadir comentarios útiles a un código se duplica (como mínimo) si no se realiza en el momento de la codificación.
  • la excusa del deadline ("el proyecto va demasiado apurado como para ponerme a comentar el código"). No te engañes, los minutos de tardarás en comentar apenas afectarán a los plazos, y sí añadirán mantenibilidad al sistema.

Y es que, citando a Ryan Campbell en su post Successful Strategies for Commenting Code,
"comentar el código es como limpiar el cuarto de baño; nadie quiere hacerlo, pero el resultado es siempre una experiencia más agradable para uno mismo y sus invitados"


Publicado en: Variable Not Found.

Estos contenidos se publican bajo una licencia de Creative Commons Licencia Reconocimiento-No comercial-Compartir bajo la misma licencia 3.0 España de Creative Commons

miércoles, 14 de noviembre de 2007
Pues no, esta entrada no trata de los diseños líquidos habituales en el mundo de la maquetación web, aunque podría parecer lo contrario. El término interfaz se utiliza en su acepción relativa a la orientación a objetos, y la fluidez se refiere a la continuidad en el movimiento de instancias entre distintas llamadas a métodos.

En pocas palabras, el uso de interfaces fluidos es un estilo cada vez más frecuente de programación, también llamado a veces "encadenamiento de métodos" (method chaining), que promueve la eliminación de código innecesario y engorroso para la realización de tareas frecuentes, sustituyéndolo por una secuencia natural, intuitiva y fluida de instrucciones que se encadenan de forma casi mágica.

Pero veámoslo con un ejemplo. Y ojo, que aunque lo codificaremos en C#, el concepto es perfectamente válido para otros lenguajes orientados a objetos, salvo por los aspectos sintácticos.

Imaginemos la clase Calculator, con un diseño como el mostrado a la izquierda, que es capaz de mantener un único valor (entero) y que soporta cinco operaciones: Get (para obtener el valor actual de la calculadora), Set (para establecerlo), y Add, Substract y Multiply (para sumarle, restarle y multiplicarle un valor respectivamente).

La forma habitual de operar con esta calculadora para obtener el resultado de un cálculo sería algo parecido a:
  Calculator calc = new Calculator();
calc.Set(0);
calc.Add(10);
calc.Multiply(2);
calc.Subtract(4);
Console.WriteLine("Resultado : " + calc.Get());
 
Como podemos observar, para realizar unas operaciones muy simples hemos necesitado demasiadas líneas de código, y además bastante repetitivas.

Veamos ahora cómo utilizando la técnica de interfaces fluidos podemos dejar este código en:
  Console.WriteLine("Resultado: " +
new Calculator().Set(0).Add(10).Multiply(2)
.Substract(4).Get()
);
 
El truco consiste en hacer que métodos que normalmente no devolverían ningún valor (métodos void, como los de la clase Calculator anterior) retornen referencias hacia el propio objeto, es decir, acaben con un return this;. De esta forma, la llamada a la función podrá ir seguida de otra llamada sobre el mismo objeto, que a su vez lo devolverá para el siguiente y así sucesivamente. Y de ahí el término "fluido": fijaos en el ejemplo anterior como la instancia de Calculator que es retornada por el constructor se utiliza directamente en la llamada Set(0), que a su vez la devuelve y es utilizada por Add(10)... ¿no es eso fluir, aunque el término suene algo poético?

Otro ejemplo, donde se utiliza la misma técnica, pero esta vez combinando instancias de distintas clases (fijaos que nada impide en la mayoría de lenguajes dividir las líneas de código como se muestra, facilitando así la lectura):

new Surface()
.Clear()
.Fill("white")
.CreateText()
.SetMessage("Hi, all!")
.SetColor("blue")
.Draw()
.CreateCircle(100, 100, 10)
.Fill("green")
.Draw()
.Border("red")
.Draw()
 
Este código muestra tres aspectos importantes. Primero, que es posible crear un pseudo-lenguaje de programación utilizando esta técnica, de hecho los fluent interfaces son muy utilizados en los DSL (Lenguajes Específicos de Dominio). En el ejemplo se puede intuir un lenguaje para el dibujo de figuras sobre una superficie.

Segundo, que la devolución de los métodos no tiene por qué limitarse a una única clase. En el código anterior, los métodos CreateText() y CreateCircle() retornan referencias a nuevas instancias de clases que representan estas figuras, que también hacen uso de interfaces fluidos. Los métodos Draw() de ambas retornan de nuevo una referencia a la instancia de la superficie (Surface) sobre la que están dibujando.

Tercero, se deja entrever la dificultad de desarrollar clases que permitan utilizar esta técnica, pues cada método debe devolver una referencia al objeto oportuno para facilitar el encadenamiento con el método posterior. Una elección incorrecta de la clase de devolución hará que la fluidez se rompa.

Finalmente, decir que los interfaces fluidos no son algo nuevo. Hay quien habla de su uso en SmallTalk antes de la década de los noventa (!), aunque el concepto se extendió a partir de la inclusión de una entrada en la Bliki de Martin Fowler comentándolo hace un par de años.

Hay también quien opina que los interfaces fluidos son algo más que el encadenamiento de métodos, que también existe en un código como name.Trim().ToUpper(), pues aporta una interfaz comprensible e intuitiva.

En cualquier caso, hace algún tiempo que esta técnica me llamó la atención y cada vez lo veo utilizado en más ocasiones, especialmente en contextos de frameworks y para implementar DSLs (como Quaere).

Estos contenidos se publican bajo una licencia de Creative Commons Licencia Reconocimiento-No comercial-Compartir bajo la misma licencia 3.0 España de Creative Commons

domingo, 11 de noviembre de 2007
Las primeras versiones de la plataforma .Net introdujeron el PostBack como el mecanismo de recarga de una página gracias al cual era posible la persistencia del estado de controles y la interacción con ellos de forma muy sencilla y potente en el lado del servidor, modificando la filosofía de programación de webs que habíamos usado hasta entonces (ASP clásico, CGIs...).

Sin embargo, si querías usar los controles de servidor en todo su esplendor te veías obligado a meter tareas completas dentro de una misma página; así, aquellas que presentaban funcionalidades relativamente complejas se convertían en batiburrillos donde se incluían formularios de entrada de datos, código de validaciones, formularios de salida de información, etc. Por ejemplo, podíamos tener en un mismo Webform paneles de introducción de criterios de búsqueda, paneles de información o errores, paneles con un grid para mostrar los resultados e incluso otro panel para mostrar una vista ampliada de un registro concreto, y jugar con las visibilidades para mostrar unos u otros en función de los pasos dados por el usuario.

Desde ASP.Net 2.0 es posible realizar los llamados "Cross Page PostBacks", o PostBacks hacia páginas distintas de la inicial, permitiéndonos separar funciones y hacer nuestro código un poco más legible, estructurado y reutilizable. El ejemplo anterior podría ser diseñado en tres páginas independientes, separando las distintas vistas, una solución mucho más limpia:
  • en la primera estaría el formulario de introducción de criterios.
  • en la segunda podríamos simplemente tomar los parámetros de la anterior y mostrar un listado con los resultados obtenidos, o bien un mensaje de información si no hay coincidencias.
  • en la tercera podríamos montar una ficha con los datos detallados de un elemento, que habría sido seleccionado por el usuario en la página anterior.
¿Y cómo se hace? Sencillo.

En primer lugar, hay que modificar el comportamiento de los botones de acción (o similar, de hecho también podría ser ImageButtons o LinkButtons) para que realicen el envío a otra página, indicándole su URL en la propiedad PostBackUrl. Eso hará que, tras su pulsación, el control sea enviado inmediatamente al archivo .aspx que se defina en la misma.

Un aspecto muy a tener en cuenta es el término "inmediatamente": el control es transferido a la nueva página sin ejecutar posibles validaciones en servidor, por lo que podrían llegar gazapos a la segunda. Sin embargo, si los validadores son en cliente, se ejecutarán con normalidad (si el usuario no tiene desactivado Javascript, of course!).

Ya en la página destino, hay varias opciones para recuperar el valor de los controles de la página invocadora. La primera de ellas, la más sencilla, consiste en usar la propiedad PreviousPage de la clase Page para obtener una referencia hacia la página de origen y obtener sus controles de la siguiente forma:

if (PreviousPage != null)
{
TextBox txt =
(TextBox)PreviousPage.FindControl("TextBox1");
Label1.Text = txt.Text; // Por ejemplo
}

Sin embargo, no es una solución aconsejable, puesto que un simple cambio de nombre del control en la página origen provocaría un error en tiempo de ejecución, puesto que FindControl no sería capaz de encontrarlo con el nombre "TextBox1".

Hay otra forma mucho más potente, que consiste en especificar en la página destino un tipado fuerte para la de origen usando en su archivo .aspx la siguiente directiva:

<%@ PreviousPageType VirtualPath="~/PaginaOrigen.aspx" %>

De esta forma, cualquier referencia a PreviousPage en la página destino se considerará automáticamente del tipo de la página de origen, pudiendo acceder a sus propiedades de forma directa, sin necesidad de castings ni nada parecido. Sin embargo, todo sea dicho, debido a que el nivel de visibilidad de los controles de un Webform son protected, no podemos acceder directamente a su contenido, debemos usar accesores o propiedades públicas para ello.

Por ejemplo, podríamos definir en la clase (codebehind) de la página de origen una propiedad como la siguiente como accesor al contenido de un control TextBox llamado txtTexto:

public string Texto
{
get {return txtTexto.Text; }
}

Y en la página destino, una vez introducida la directiva descrita anteriormente especificando la URL de la página de origen, podríamos hacer lo siguiente:

if (PreviousPage != null)
{
Label1.Text = PreviousPage.Texto; // Por ejemplo
}

En resumen, esta técnica permite estructurar mucho mejor nuestras aplicaciones web, aunque sea a cambio de realizar un sobreesfuerzo en el desarrollo. En mi opinión puede valer la pena su uso en páginas que generen demasiadas vistas diferentes para ser introducidas en un único Webform sin comprometer la mantenibilidad, o cuando resulte interesante de cara a la reutilización de páginas.

No es conveniente, a mi entender, su utilización indiscriminada en formularios simples, pues precisamente los PostBacks fueron introducidos en ASP.Net para evitar la proliferación innecesaria de páginas sueltas (.aspx) y facilitar el mantenimiento del estado en la inherentemente desconectada tecnología Web.

Estos contenidos se publican bajo una licencia de Creative Commons Licencia Reconocimiento-No comercial-Compartir bajo la misma licencia 3.0 España de Creative Commons