Gerardo Contijoch

Experiencias del día a día trabajando con .NET – ASP.NET, C#, ASP.NET MVC y demas…

Archive for the ‘Javascript’ Category

Acceder a variables de servidor desde Javascript

Posted by Gerardo Contijoch en junio 6, 2009

Un problema con el que nos solemos encontrar los desarrolladores web es el de acceder desde un script (en el cliente) a datos que se encuentran sólo disponibles en el servidor. No me refiero a recursos que están en el servidor, sino mas bien a valores o datos que pueden ser resultado (o formar parte) de cierto procesamiento y que no se exponen al cliente.

El caso más común con el que me encuentro yo es conocer el id de ciertos controles desde el lado del cliente. Sabemos bien que ASP.NET tiene la costumbre de renombrar nuestros controles con unos nombres bastante crípticos y esto es un gran problema si queremos acceder a ellos desde javascript. El verdadero nombre de estos controles (lo que se conoce como el ClientId) se encuentra accesible desde el código del lado del servidor, pero no desde el cliente. Y es por esto que Rick Strahl creo la clase wwScriptVariables que nos permite pasarle a nuestros scripts los valores que querramos desde el servidor.

Veamos un ejemplo con el cual vamos a trabajar. Imaginemos que tenemos una página en donde se ingresa un valor y como respuesta, se informa si ese valor es numérico o no. Esta es la parte interesante del código HTML:

   1: <div id="valor">Ingrese un valor: <asp:TextBox ID="txtValor" runat="server"></asp:TextBox>
   2:     <asp:Button ID="btnEnviar" runat="server" Text="Enviar" onclick="btnEnviar_Click" />
   3: </div>
   4: <div id="resultado">
   5:     <asp:Label ID="lblResultado" runat="server" Text=""></asp:Label>
   6: </div>

Y este es el código del lado del servidor:

   1: using System;
   2:
   3: public partial class _Default : System.Web.UI.Page
   4: {
   5:     protected void Page_Load(object sender, EventArgs e){}
   6:
   7:     protected void btnEnviar_Click(object sender, EventArgs e) {
   8:         if (EsNumero(this.txtValor.Text)) {
   9:             this.lblResultado.Text = "Es un número!";
  10:         } else {
  11:             this.lblResultado.Text = "No es un número!";
  12:         }
  13:     }
  14:
  15:     private bool EsNumero(string valor) {
  16:         int val = 0;
  17:         if (int.TryParse(valor, out val)) {
  18:             return true;
  19:         }
  20:         return false;
  21:     }
  22: }

Si ingresamos un valor numérico el resultado se vería así:

RegistrarVariablesServidor-respuesta-original

Como se ve puede ver, todo es muy sencillo, pero imaginemos que queremos aplicarle algún efecto a la página dependiendo del resultado. En mi caso voy a usar jQuery para esto y el efecto va a consistir simplemente en aplicarle un estilo a la respuesta (ya se que lo mismo se puede hacer desde el lado del servidor, pero la idea es modificarlo desde el lado del cliente como un ejemplo).

Normalmente uno agregaría algo similar a esto en la sección head de la página:

   1: <link href="style.css" rel="stylesheet" type="text/css" />
   2:
   3: <script src="jquery-1.2.6.min.js" type="text/javascript"></script>
   4:
   5: <script type="text/javascript" language="javascript">
   6:   $(document).ready(function() {
   7:
   8:     var lbl = $("#lblResultado");
   9:
  10:     lbl.addClass("claseResultado");
  11:
  12:     });
  13: </script >

Pero eso sólo funciona cuando nuestro control (en este caso el label ‘lblResultado’) no se encuentra dentro de un contenedor, como puede ser una grilla o una master page. Esto se debe a que ASP.NET modifica los Id de los elementos renderizados para evitar la colisión de nombres y por eso, nuestro ‘lblResultado’ se puede transformar en algo como ‘ctl00_ContentPlaceHolder1_lblResultado’ provocando un error en nuestro script. Lo que necesitamos es pasarle a nuestros scripts el verdadero nombre de los controles lo que se puede lograr con algunos code nuggets incrustados en medio de nuestro script:

   1: $(document).ready(function() {
   2:     var lbl = $("#<%= lblResultado.ClientId %>");
   3:     lbl.addClass("claseResultado");
   4: });

Pero esto tiene algunos problemas. Primero y principal, si nuestro script esta en un archivo externo y no embebido en la página, no se pueden usar code nuggets. Otro problema es que el valor que pasemos tiene que estar correctamente escapado para que javascript no lo malinterprete o falle. Sumado a esto, no es muy elegante la solución de incrustar código de servidor dentro de un script y no facilita para nada la reutilización de los scripts.

Ahora veamos como solucionamos este inconveniente con la ayuda de wwScriptVariables. Su uso es sencillísimo, solo hay que instanciarla y mediante el método Add() agregamos los valores que queremos pasarle a nuestros scripts. Luego, desde el lado del cliente, podemos acceder a estos valores desde una nueva variable que tendremos a nuestra disposición, la cual es generada (durante el renderizado) por la clase wwScriptVariables. Veamos como queda nuestro ejemplo con el uso de esta clase.

Del lado del servidor:

   1: using System;
   2:
   3: public partial class NuevaVersion : System.Web.UI.Page
   4: {
   5:     protected void Page_Load(object sender, EventArgs e){}
   6:
   7:     protected void btnEnviar_Click(object sender, EventArgs e) {
   8:
   9:         Westwind.Web.Controls.Controls.wwScriptVariables scriptVar = new Westwind.Web.Controls.Controls.wwScriptVariables(this, "variables");
  10:         scriptVar.Add("lblResultado", this.lblResultado.ClientID);
  11:
  12:         if (EsNumero(this.txtValor.Text)) {
  13:             scriptVar.Add("esNumero", "true");
  14:             this.lblResultado.Text = "Es un número!";
  15:         } else {
  16:             scriptVar.Add("esNumero", "false");
  17:             this.lblResultado.Text = "No es un número!";
  18:         }
  19:     }
  20:
  21:     private bool EsNumero(string valor) {
  22:         int val = 0;
  23:         if (int.TryParse(valor, out val)) {
  24:             return true;
  25:         }
  26:         return false;
  27:     }
  28: }

y del lado del cliente:

   1: $(document).ready(function() {
   2:     var lbl = $("#" + variables["lblResultado"]);
   3:
   4:     // Aplicamos distintas clases dependiendo del resultado
   5:     if (variables["esNumero"] == "true")
   6:     {
   7:         lbl.addClass("resultadoNumero");
   8:     } else {
   9:         lbl.addClass("resultadoTexto");
  10:     }
  11: });

El funcionamiento de la clase es muy sencillo, básicamente lo que hace es registrar los valores que queremos pasar al cliente y dentro del evento PreRender genera el siguiente script:

   1: <script type="text/javascript">
   2: //<![CDATA[
   3: var variables = {
   4:     "lblResultado": 'ctl00_ContentPlaceHolder1_lblResultado',
   5:     "esNumero": 'false'
   6: }
   7: //]]>
   8: </script >

Lo mejor de todo es que Rick pone a nuestra disposición el código completo de la clase para que la modifiquemos a gusto si así lo que deseamos.

La página donde pueden bajar esta clase es ésta, pero aclaro que esa es una versión vieja, hay una versión más nueva acá, la cual agrega nuevas funcionalidades como la posibilidad de actualizar el valor de las variables desde el cliente y pasárselas al servidor. Si no nos interesa esto último, les recomiendo usar la primer versión, ya que tiene menos dependencias y es mucho mas liviana.

¡Nos vemos en el próximo post!

Publicado originalmente en https://gerardocontijoch.wordpress.com.

Anuncios

Posted in ASP.NET, Desarrollo Web, Javascript | 1 Comment »

Tooltip simple con jQuery

Posted by Gerardo Contijoch en mayo 5, 2009

Hace unos días precisé agregar un tooltip a una tabla HTML y debido a que ya estaba usando jQuery, comencé a buscar plugins y scritps para esta librería. Lo que necesitaba era un tooltip simple, sencillo, como los clásicos de Windows y lo encontré en cssglobe.com. Éste es el post donde se presenta el script. El problema que le vi a ese script es que solo funcionaba con elementos <a> y se limita a leer el atributo title de los mismos, lo cual no me servía. Es por ello que lo modifiqué para hacerlo más genérico y poder aplicarlo a cualquier elemento.

Su uso es sencillísimo:

var unElemento = $("#unDiv");
AddTooltip(unElemento, "Este es el contenido del tooltip!");

En el ejemplo anterior, el tooltip se aplicaría sobre un elemento cuyo Id es unDiv y se ve de la siguiente manera:

tooltip-con-jquery-tooltip

De hecho, se puede poner cualquier fragmento de HTML, como una imagen, una tabla o lo que queramos.

Pueden bajar el script modificado junto con código de ejemplo desde aquí.

¡Nos vemos en el próximo post!

Publicado originalmente en https://gerardocontijoch.wordpress.com.

Posted in Desarrollo Web, Javascript, jQuery | Etiquetado: , | 1 Comment »

Ventajas y desventajas del uso de Google como host de librerías javascript

Posted by Gerardo Contijoch en abril 20, 2009

Hoy en día es muy común encontrar sites que dependen de librerías javascript populares como jQuery, prototype o script.aculo.us. Normalmente estos archivos se encuentran almacenados junto al resto de las páginas de un site, lo cual puede traer consecuencias negativas.

Una de las grandes desventajas de hacerlo así es que por cada site que visitemos (y que use alguna de estas librerías), vamos a estar bajando las mismas librerías una y otra vez. Esto se debe a que para nuestros browsers los archivos http://www.misite.com/scripts/jquery.js y http://www.otrosite.com/scripts/jquery.js son archivos diferentes y en realidad lo son, su contenido puede ser el mismo, pero son dos archivos diferentes. Es por ello que si visitamos ambos sites, vamos a estar bajando dos veces el mismo contenido.

Para solucionar este problema (que puede no serlo, en ciertos casos este comportamiento es deseable), podemos recurrir a la ayuda de Google, quien hostea algunas de las librerías javascript más populares. Siguiendo con el ejemplo planteado, para referenciar a la librería de jQuery (versión 1.2.6 en este ejemplo) simplemente tenemos que usar la URL http://ajax.googleapis.com/ajax/libs/jquery/1.2.6/jquery.min.js (URL de la versión comprimida de jQuery, podemos quitar el .min para referenciar a la versión estándar, lo cual no sugiero).

De este modo, si www.misite.com y www.otrosite.com usan la misma referencia, nuestro browser va a bajar solo una vez el archivo .js, aumentando la velocidad de carga de los sites (debido a que ya vamos a tener en el caché el archivo bajado).

Y eso no es todo, ya a que si estamos bajando el archivo desde un server diferente al que hostea nuestro site, los browsers modernos pueden bajar las librerías paralelamente a los archivos de nuestro site gracias al multithreading. Si los archivos se encontraran en el mismo server, la cantidad de bajadas simultáneas se vería reducida para evitar la sobrecarga del mismo.

Otro beneficio de usar Google como host es que los archivos que bajemos de ahí, los vamos a estar bajando de servers posiblemente más veloces y en redes con mayor ancho de banda que los nuestros.

Siempre hay un pero

Usar esta técnica tiene sus desventajas también, como lo muestra Derek Allard en este post. La primera de ella es ¿qué sucede si hay un problema con los servidores de Google? Pues simplemente no vamos a tener acceso a las librerías de las cuales depende nuestro site. Derek propone una solución a esto en su post, pero a mi no me funcionó. Imagino que su solución tampoco es válida cuando tenemos plugins u otro tipo de dependencias ya que la carga de las mismas depende de que se haya cargado con anterioridad la librería en cuestión.

Otra de las desventajas es que Google es quien tiene control sobre el código de las librerías, lo que significa que tienen acceso a nuestro código y datos (cuando usamos las librerías, les estamos pasando nuestra información a ellas para que la procesen y las mismas pueden haber sido modificadas para registrar esa información). Si bien no hay que ponerse paranoicos, es un factor a tener en cuenta. También es verdad que muy posiblemente estén guardando información sobre el trafico de nuestro site (contando la cantidad y origen de descargas de los archivos).

Leí en algún comentario de algún blog (el cual no encuentro) que algunos antivirus bloquean la bajada de scripts de otros severs, lo cual tiene sentido desde el punto de vista de la seguridad si uno lo piensa un poco.

Casos especiales

Hay otros casos en los que simplemente nos conviene hostear nosotros mismos esos archivos.

No tiene sentido buscar las librerías en servidores externos durante la etapa de desarrollo, estaríamos haciendo que la carga de las mismas sea más lenta ya que durante el desarrollo de un site, normalmente trabajamos de forma local y siempre va a ser más rápido cargarlas de esa forma.

Otro caso en donde no es viable el uso de esta técnica es cuando el site que usa las librerías es un site interno, es decir, se utiliza en una Intranet y no se publica en internet. Una vez mas, es posiblemente más rápido descargar los scripts desde un servidor en la red local que desde internet.

¡Nos vemos en el próximo post!

Referencias:

Publicado originalmente en https://gerardocontijoch.wordpress.com.

Posted in Desarrollo Web, Javascript | Etiquetado: , , | Leave a Comment »

Sys.ScriptLoadFailedException al intentar cargar un script dinámicamente con ScriptManager

Posted by Gerardo Contijoch en diciembre 16, 2008

Hoy estaba trabajando en un site que usa ASP.NET AJAX y me encontré con el siguiente error:

Sys.ScriptLoadFailedException: The script ‘http://localhost:49573/scripts/Script.js&#8217; could not be loaded.

Ese es un script que yo cargo dinámicamente mediante el ScriptManager de la siguiente manera:

ScriptManager.RegisterClientScriptInclude(pagina, typeof(Page), "miScript", "scripts/Script.js");

Busqué códigos de ejemplos en internet y todos eran iguales al mío, no encontraba el error hasta que me topé con este post de Bill Robertson.

Resulta que ASP.NET AJAX carga los scritps registrados de esta manera de forma asincrónica y es necesario notificar la finalización de la carga de cada script para que todo funcione como debería. Para realizar la notificación simplemente hay que agregar el siguiente código al final del archivo js al que hagamos referencia:

   1: if(typeof(Sys) != "undefined" && typeof(Sys.Application) != "undefined") {
   2:    Sys.Application.notifyScriptLoaded();
   3: }

La validación de la línea 1 no es necesaria en realidad, pero si usamos este archivo en un site donde no se usa ASP.NET AJAX, la línea 2 va a fallar (debido a que puede no existir el objeto Sys).

Una vez hecho eso, el site comenzó a funcionar como siempre y el script se cargó correctamente.

¡Nos vemos en el próximo post!

Publicado originalmente en https://gerardocontijoch.wordpress.com.

Posted in ASP.NET, Desarrollo Web, Javascript | Etiquetado: , , , | 2 Comments »