Autor en Google+
Saltar al contenido

Variable not found. Artículos, noticias, curiosidades, reflexiones... sobre el mundo del desarrollo de software, internet, u otros temas relacionados con la tecnología. C#, ASP.NET, ASP.NET MVC, HTML, Javascript, CSS, jQuery, Ajax, VB.NET, componentes, herramientas...

el blog de José M. Aguilar

Inicio El autor Contactar

Artículos, noticias, curiosidades, reflexiones... sobre el mundo del desarrollo
de software, internet, u otros temas relacionados con la tecnología

¡Microsoft MVP!
domingo, 20 de mayo de 2007
Hace unos días publicaba un paso a paso donde mostraba cómo invocar servicios web desde el cliente utilizando Javascript y las magníficas extensiones ASP.NET 2.0 AJAX, y pudimos ver lo sencillo que resultaba introducir en nuestras páginas web interacciones con el servidor impensables utilizando el modelo tradicional petición-respuesta de página completa.

Sin embargo, la infraestructura Ajax nos brinda otra forma de invocar funcionalidades de servidor sin necesidad de crear servicios web de forma explícita: los métodos de página (Page Methods). El objetivo: permitirnos llamar a métodos estáticos de cualquier página (.aspx) desde el cliente utilizando Javascript y, por supuesto, sin complicarnos mucho la vida. Impresionante, ¿no?

En este post vamos a desarrollar un ejemplo parecido al anterior, en el que tendremos una página web con un cuadro de edición para introducir nuestro nombre y un botón, cuya pulsación mostrará por pantalla el resultado de la invocación de un método del servidor, y todo ello, por supuesto, sin provocar recargas de página completa. El resultado vendrá a ser algo así:


Ah, para que no se diga que soy partidista ;-) esta vez vamos a desarrollarlo en Visual Basic .NET (aunque la traducción a C# de la porción de servidor es directa), y de nuevo con Visual Studio 2005.

En primer lugar, hemos de crear la solución (Visual Studio 2005) basándonos en la plantilla "ASP.NET Ajax-Enabled web application" que habrá aparecido en nuestro entorno tras descargar e instalar las extensiones AJAX. Una vez elegido el nombre, se creará un proyecto con todas las referencias necesarias, el web.config adaptado, y una página (Default.aspx) que viene ya preparada con el ScriptManager listo para su uso.

En el archivo code-behind (Default.aspx.vb) añadimos el siguiente código, que será la función de servidor a la que invocaremos desde el cliente:


<WebMethod()> _
Public Shared Function Saludame(ByVal Nombre As String) As String
Return "Hola a " + Nombre _
+ " desde el servidor, son las " _
+ DateTime.Now.ToString("hh:mm:ss")
End Function


Obsérvese que:
  • El método es público y estático. Lo primero es obvio, puesto que el ScriptManager necesitará mapearlo al cliente y para ello deberá acceder mediante reflexión a sus propiedades, y lo segundo también, puesto que las invocaciones al mismo no se efectuarán desde instancias de la clase, imposibles de conseguir en cliente.

  • Está adornado con un atributo WebMethod, que indica al ScriptManager que el cliente podrá invocarlo utilizando Javascript.

Sólo falta un detalle. Hay que indicar al ScriptManager de la página que se encargue de procesar todos los métodos públicos estáticos de la clase, que además estén marcados con el atributo anteriormente citado, y genere la infraestructura que necesitamos para utilizar de forma directa esta funcionalidad. Esto se consigue estableciendo la propiedad EnablePageMethods a true desde el diseñador.

Estableciendo EnablePageMethods a true
Una vez hecho esto, introducimos en el aspx de la página los elementos de interfaz (el cuadro de texto, el botón y un label para mostrar los resultados). El código del formulario completo queda así:


<form id="form1" runat="server">
<input type="text" id="nombre" />
<input id="Button1" type="button"
value="¡Pulsa!" onclick="llamar();" />
<br />
<strong>Mensajes obtenidos:</strong>
<br />
<label id="lblMensajes" />
<asp:ScriptManager ID="ScriptManager1"
runat="server" EnablePageMethods="True" />
</form>


Podemos ver, como en el post anterior, que todos los controles salvo el ScriptManager son de cliente, no tenemos el runat="server" en ninguno de ellos puesto que no realizaremos un envío de la página completa (postback) en ningún momento, todo se procesará en cliente y actualizaremos únicamente el contenido del label.

Por último, añadimos el script que realizará las llamadas y mostrará los resultados. La función llamar(), invocada a partir de la pulsación del botón y la de notificación de respuesta. El código es el siguiente:

<script type="text/javascript">
function llamar()
{
PageMethods.Saludame($get("nombre").value , OnOK);
}
function OnOK(msg)
{
var etiqueta = $get("lblMensajes");
etiqueta.innerHTML += msg + "<br />";
}
</script>

Primero, fijaos en la forma de llamar a Saludame(). El ScriptManager ha creado una clase llamada PageMethods a través de la cual podemos invocar a todos los métodos de la página de forma directa.

El primer parámetro de la función es $get("nombre").value. Ya vimos el otro día que $get("controlID") es un atajo para obtener una referencia hacia el control cuyo identificador se envía como parámetro. Por tanto, $get("nombre").value obtendrá el texto introducido en el cuadro de edición.

Después de los parámetros propios del método llamado, se indica la función a la que se pasará el control cuando obtenga el resultado desde el servidor. OnOK recibirá como parámetro la respuesta de éste, el string con el mensaje de saludo personalizado, y actualizará el contenido de la etiqueta para mostrarlo.

En resumen, los PageMethods son una interesantísima característica del framework AJAX de Microsoft, y nos permiten llamar a funciones estáticas definidas directamente sobre las páginas aspx, evitando el engorro de definir servicios web (asmx) de forma independiente, y sobre todo, ayudando a mantener en un único punto el código relativo a una página concreta ¡y qué mejor sitio que la propia página!

(Puedes descargar el proyecto completo.)

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

23 Comentarios:

Anónimo dijo...

He probado el ejemplo, pero no funciona, ya que me arroja un error en javascript, es decir, el explorador avisa un error en el javascript, intenté crearlo en c#, pero aún asi no me funciona. ¿Alguna idea?

José María Aguilar dijo...

Hola.

¿Has instalado las extensiones AJAX? Si no lo has hecho, puedes descargarlas desde http://ajax.asp.net.

Si ya las has instalado, comprueba en las propiedades del proyecto si tienes una referencia al ensamblado System.Web.Extensions.

Verifica asimismo que tengas activados los scripts, no sea que desde el propio navegador o algún antivirus (o similar) los esté bloqueando.

También puedes probar el sitio con otro navegador (Firefox, por ejemplo, si estás usando IE) a ver si se trata de un problema local del software.

Espero haberte ayudado con esto. De todas formas, si sigues teniendo problemas, publica qué error te aparece exactamente.

Saludos.

Gustavo dijo...

googleando para hallar la solucion a un problema llegue a tu blog, pero aun no logro solucionar mi problema, quizas me puedas ayudar:
estoy trabajando con el control "ModalPopupExtender" para mostrar un pequeño formulario que contiene textbox del cual pretendo obtener unos datos, mi problema es que no logro hacer funcionar el click del boton que esta dentro del formulario y como solucion quiero implementar un javascript que llame a través de "OnOkScript" a un metodo ubicado dentro de la clase .aspx.cs, siguiendo tus pasos no puedo declarar "WebMethod()" o [WebMethod] (creo q asi se declara en c#) por lo q no puedo continuar con los siguientes pasos, propones alguna solucion, da igual como se implemente... de antemanos gracias...
gustavo

Rosario dijo...

hola sr. Aguilar, es la primera vez que leo su blogger y me ha parecido muy interesante, en verdad, pocas paginas en español esplica con claridad lo que en usted admiro, y lo mejor de todo que pude encontrar como llamar a procedimientos o funciones del lado del cliente sin necesidad de utilizar un engorros webservice, bueno he probado su esplicacion, y funciona a la maravilla, pero me tope con una duda, que al parecer cuando en la funcion regreso un DataSet marca error, he estado investigando y no doy con la solicion, y me atrevo a escribirle para ver si usted me puede ayudar??, si ha tenido el mismo problema, o localizo un articulo al respecto, si se puede o no??, bueno le doy las gracias por tu atencion y de atemano lo felicito, es un blogger muy constructivo, espero su respuesta, gracias, atte. Rosario Camargo

José M. Aguilar dijo...

Hola, Rosario.

La verdad es que no conozco la respuesta a esta cuestión. Sin embargo, me comprometo a estudiarlo y responderle en unos días.

Gracias por participar en Variable not found!

rosario camargo dijo...

hola, estare muy al pendiete....gracias por tu pronta respuesta, saludos.

José M. Aguilar dijo...

Rosario, ahí va un post en exclusiva sobre este tema:
Retornar datasets desde PageMethods

Saludos.

Juan Quijano dijo...

Me imagino que funcionará lo que comentas, pero en mi caso partícular tengo la declaración del scriptmanager en la masterpage y el updatepanel y los controles en una página hija.

Y me dá un error de javascript en donde me dice que no está declarado el tipo PageMaster.

¿Tienes alguna idea hacerca de este problemilla?

Juan Quijano dijo...

Perdón, en vez de PageMaster, me refería a PageMethods

José M. Aguilar dijo...

Hola, Juan.

Ese error de script aparece cuando tienes declarado el scriptmanager con la propiedad EnablePageMethods=false. Si lo pones a true, debería funcionarte.

En caso contrario, dame algunas pistas más, a ver si puedo ver lo que te ocurre.

Gracias por comentar!

Ofion dijo...

Hola Jose Maria
Mi comentario es para felicitarte por las ganas de explicar y responder a todas las dudas.

Ahora viene mi duda, estoy intentando utilizar tu ejemplo copiando y pegando en otro sitio web clasico con el web.config bien parametrizado y las referencias a los ensamblados, pero cuando quiero llamar al WebMethod, simplemente me dice que no hay una referencia a esa clase. Aclaro que tengo las extensiones AJAX y las referencias.

Te agradezco cualquier ayuda que puedas darme

Saludos

Hernan

José M. Aguilar dijo...

Hola, Hernán. Ante todo, gracias por comentar.

Si el error de clase inexistente te aparece en cliente (desde Javascript), comprueba si se trata de un problema con los namespaces (mira este post). En estos casos yo suelo utilizar Firebug, que me permite ver de forma muy sencilla qué ha llegado al cliente y dónde están los problemas.

Si no es así, revisa las referencias del proyecto y comprueba que las librerías estén todas correctamente vinculadas. Puede ayudarte partir de un proyecto Ajax que funcione (p.e., el del post) e ir comparando las configuraciones.

Espero que te sirva de ayuda. De todas formas, si el problema persiste, no dudes en contactar conmigo y seguimos viéndolo.

Un saludo.

Anónimo dijo...

Hola, gran post, el código funciona perfectamente salvo si es invocado desde una masterpage. Me explico, tengo una web basada en masterpage, en esta tengo el menú, y es desde este menú desde donde tengo que invocar el pagemethod, me da error indicado que el pagemethod no existe. Insisto, copiar y pegar a una página normal y funciona perfecto. ¿Alguna idea?
Gracias

José M. Aguilar dijo...

Hola.

Anónimo, un PageMethod es un "método de página", por lo que no podría encontrarse en una maestra. Los proxies que genera automáticamente el ScriptManager sólo están disponibles sobre la página en cuestión, por lo que sería complicado hacerlo desde la master, que es ajena a la página actual.

Probablemente te sea más sencillo programar la parte cliente (scripts) utilizando jquery, donde puedes especificar la página y método al que quieres llamar muy fácilmente, sin usar scriptmanager ni nada parecido.

Espero que te sea de ayuda. De todas formas, si necesitas más info, aquí me tienes.

Saludos.

Anónimo dijo...

Muchas gracias

ana sofia dijo...

Hola, estoy haciendo un proyecto donde tengo que llamar a metodos desde mi cliente js al servidor asp, he provado hacerlo con ajaxPro, ajax y ahora tu ejemplo, en cierto tiempo funcino perfectamente, pero ahora me sale un error en tiempo de ejecucion: "thispage" no esta defenido o PageMethods no esta definido. Gracias de antemano por la ayuda que me puedas dar!!

José M. Aguilar dijo...

Hola, Ana.

En comentarios anteriores tienes algunas respuestas a preguntas parecidas... básicamente, debes revisar si has incluido correctamente las referencias, el scriptmanager, si la propiedad enablepagemethods está a true, etc.

Y en cualquier caso, si el ejemplo te ha funcionado correctamente, vuelve a descargarlo e intenta adaptar el código a tu caso concreto.

Saludos.

Krantz dijo...

Padrisimo blog - tienes un nuevo fan ;)

Estoy atorado en este tema:

Tengo un objeto que almaceno en el Session["obj"], el cual tiene todo el avance del usuario en el sistema; Lo que quiero es que, cuando presione el usuario el input (que no causa PostBack y que debe realizar la funcion de grabar y salir), yo serialice el objeto "obj" y lo grabe a un archivo de texto en el servidor para ser consultado despues.

Usando el atributo de WebMethod() en el codebehind, me manda el error CS0120, porque la variable Session["obj"] no es estatica.

Tienes alguna idea de como podria yo hacerle?

Gracias!

José M. Aguilar dijo...

Hola, Krantz, gracias por tu comentario.

Dado que el webmethod es estático, sólo puedes acceder a miembros estáticos. Afortunadamente, para acceder a las variables de sesión, puedes usar HttpContext.Current.Session.

Saludos.

Luis dijo...

Magnifico aporte, solo que estoy atorado en un tema, como puedo acceder a los objetos de la pagina en la que invoco el Webmethod y obtener sus valores, por ejemplo un input de tipo textbox, o algun otro metodo de la misma asp, alguna idea de como hacerlo ?

José M. Aguilar dijo...

Hola, Luis!

Fíjate que en el ejemplo se accede al contenido del input desde javascript, utilizando $get("nombre").value.

Espero que sea lo que buscas :-)

Luis dijo...

Agradezco tu respuesta, lo que pretendo hacer es lo siguiente, en el Webmethod llamar a otro metodo que obtenga los datos capturados por el usuario en varios textbox y despues de hacer algunos calculos guardarlos en una base de datos, sin embargo en el metodo estatico no tengo acceso a la instancia actual, espero haberme explicado y agradecere tus comentarios

José M. Aguilar dijo...

Hola, Luis!

El webmethod no tiene acceso a ningún elemento de la página. Todos los datos que necesite para realizar la tarea (calcular y guardar en la bdd, por ejemplo) los debe recibir como parámetros.

En el ejemplo del post, fíjate que el webmethod recibe un parámetro de tipo string, que se obtiene del textbox que hay en el formulario. Sólo tienes que seguir el mismo patrón:
- añadir al webmethod los parámetros que necesites.
- en la función de script "llamar", obtener (con $get) los valores de los textboxes e introducirlos como parámetros en la invocación del webmethod.

Saluds.