Introducción
La finalidad del articulo es mostrar como utilizando puramente jquery se puede vincular dos dropdownlist de forma tal que al cambiar la selección de uno de ellos se recargue el contenido del siguiente.
En el articulo se hará uso de una invocación a un Page Method, para exponer la información y poder recuperar de forma dinámica.
Inicialización de las listas
En el evento Page_Load se proceder a la carga inicial de los combos, esto será útil para que el usuario visualice una selección por defecto
protected void Page_Load(object sender, EventArgs e) { if (!IsPostBack) { ddlPaises.DataSource = GetPaises(); ddlPaises.DataTextField = "Pais"; ddlPaises.DataValueField = "Cod"; ddlPaises.DataBind(); ddlCiudades.DataSource = GetCiudadesByPais(Convert.ToInt32(ddlPaises.SelectedValue)); ddlCiudades.DataTextField = "descripcion"; ddlCiudades.DataValueField = "cod"; ddlCiudades.DataBind(); } } [WebMethod] public static List<CiudadEntity> GetCiudadesByPais(int pais) { var query = from item in GetCiudades().AsEnumerable() where Convert.ToInt32(item["CodPais"]) == pais select new CiudadEntity { cod = Convert.ToInt32(item["Cod"]), descripcion = Convert.ToString(item["Ciudad"]) }; return query.ToList<CiudadEntity>(); }
Es importante remarcar como uno de los métodos tiene definido el atributo [WebMethod] y además se ha definido como static, este método será clave en la invocación desde el cliente con jQuery.
Cambio de selección desde el cliente, con jQuery
La definición del html es muy simple, solo son 2 DropDownList.
<form id="form1" runat="server"> <table> <tr> <td>Paises:</td> <td> <asp:DropDownList ID="ddlPaises" runat="server"> </asp:DropDownList> </td> </tr> <tr><td colspan="2"></td></tr> <tr> <td>Ciudades:</td> <td> <asp:DropDownList ID="ddlCiudades" runat="server"> </asp:DropDownList> </td> </tr> </table> </form>
La definición de script seria la siguiente:
<script language="javascript" type="text/javascript"> $().ready(function() { $("#<%=ddlPaises.ClientID%>").change(function() { // armo el objeto que servira de parametro, para ello utilizo una libreria de JSON //este parametro mapeara con el definido en el web service var params = new Object(); params.pais = $("#<%=ddlPaises.ClientID%>").val(); params = JSON.stringify(params); $.ajax({ type: "POST", url: "Default.aspx/GetCiudadesByPais", data: params, contentType: "application/json; charset=utf-8", dataType: "json", async: false, success: LoadCiudades, error: function(XMLHttpRequest, textStatus, errorThrown) { alert(textStatus + ": " + XMLHttpRequest.responseText); } }); }); $("#<%=ddlCiudades.ClientID%>").change(function() { alert("Ha seleccionado: " + $("#<%=ddlCiudades.ClientID%> :selected").text()); }); }); function LoadCiudades(result) { //quito los options que pudiera tener previamente el combo $("#<%=ddlCiudades.ClientID%>").html(""); //recorro cada item que devuelve el servicio web y lo añado como un opcion $.each(result.d, function() { $("#<%=ddlCiudades.ClientID%>").append($("<option></option>").attr("value", this.cod).text(this.descripcion)) }); } </script>
En el $().ready() se asocian a los combos dos eventos para poder atrapar un cambio de selección, es por eso que se usa el change().
Ante el cambio de selección de un país se procede en primer instancia al armado de parámetro que será enviado como filtro el Web Method, el cual se ha visto en el código anterior.
Para esta operación se hará uso de una librería de nombre json2.js, esta permitirá armar con objetos de javascript la información de una forma simple, y mediante el método JSON.stringify() convertir esta en json para ser enviada como parámetro.
En la operación también se toma el valor seleccionado en el combo usando:
$("#<%=ddlPaises.ClientID%>").val()
también se podría haber usado:
$(this).val()
ya que quien lanza el evento es el propio combo de países.
Es importante remarcar en este punto como se seleccionan los controles de asp.net usando en el selector de jquery, como asp.net renombran el atributo id de los controles es necesario usar el ClientID para tener el nombre correcto.
El siguiente paso es la invocación al método del servicio, para lo cual se utiliza el $.ajax(), hay algunas propiedades importantes que deben tenerse en cuenta, una de ellas es la definición de la url a invoca:
url: "Default.aspx/GetCiudadesByPais"
como se vera define directamente la propia pagina web donde se encuentra y luego el nombre del Web Method, pero podría ponerse los método del servicio en otro pagina si se requiere.
En el parámetro
data: params,
se define la variable del paso previo, en esta estará contenida el json que será enviado el web method.
También es importante definir si la invocación será asíncrona o no
async: true
en este caso no se espera a que el servicio web responda para continuar. Aquí no afecta si es es asíncrona o no la invocación ya que no hay código que continúe en la función javascript, pero puede que sea necesario a veces esperar una repuesta para poder tomar una decisión.
También se definen eventos en caso de una ejecución satisfactoria o un error
success: LoadCiudades,
hay varias formas de hacer esto, aquí se decidió usar una función nueva para procesar el contenido resultante de la invocación. Este método recibirá por parámetro de nombre “result” en donde se alojara el json de resultado.
Carga del combo de Ciudades
Es interesante analizar la función responsable de la carga de las ciudades proveniente de la invocación
function LoadCiudades(result) { //quito los options que pudiera tener previamente el combo $("#<%=ddlCiudades.ClientID%>").html(""); //recorro cada item que devuelve el servicio web y lo añado como un opcion $.each(result.d, function() { $("#<%=ddlCiudades.ClientID%>").append($("<option></option>").attr("value", this.cod).text(this.descripcion)) }); }
Esta recibe el resultado, y la primer operación será limpiar el combo, para ello es que lo selecciona y quita el html dentro de este, o sea quita los tag <option> que posea.
Luego toma cada valor devuelto en el json y lo recorre:
$.each(result.d, function() {..}
algo que seguro notaran extraño es el uso de result.d, este “d” es una propiedad del objeto result y allí contiene la información devuelta en la invocación, en este caso la lista de ciudades.
Se se pone un breakpoint en el código javascript, se podrá analizar claramente el valor devuelto en el json
Por supuesto el this dentro de la función representa cada ítem en el loop, y por tratarse de json este esta tipado con los mismas propiedades definida en la clase CiudadEntity devuelta por el web method.
[C#]
|
[VB.NET]
|
Hola Leandro, una muy buena entrada. Me sirvió mucho. Gracias!!
ResponderEliminarBuenas Leandro, la librería json2.js que mencionas, la has sacado de acá http://www.json.org/ ?? , si la respuesta es si me podrías indicar de donde exactamente??
ResponderEliminarhola
ResponderEliminarclaro de ese link
veras al final del mismo muchos links busca la seccion que dice: "JavaScript", justo alli
te lleva a
https://github.com/douglascrockford/JSON-js
saludos
Buenos días Leandro, en primer lugar muchas gracias por tu aporte ayuda a comprender como funciona el Ajax de Jquery a la perfección, pero al implementarlo en mi aplicación me indica el siguiente error "devolución de llamadas no valido", buscando en la Web encontré que hay que agragar EnableEventValidation="false" en el Page, pero ahora no puedo obtener el valor de la dropdownlist ddlCiudades cuando cambio de país, podrias orientarme?
ResponderEliminarhola Nesoft
ResponderEliminarte has encontrado con el eterna incompatibilidad entre asp.net y javascript
resulta que si desde el cliente cambias los items de un control de asp.net este difiere con la info que la pagina tiene en el viewstate por lo tanto asume el contenido se altero y ya no puedes confiar en tomar el valor como lo venias realizando
usar jquery apunta a volver a los controles de html, dejando de la los eventos y todo lo que plantea asp.net
es mas no se si leiste algo de asp.net mvc, bueno las vistas se desarrollan usando los controels de html simples y jquery, ya no existe mas evento o viewstate, ni nada de eso
si quieres seguir con asp.net quizas debas usar algo como ser
CascadingDropDown
saludos
Muchas gracias por tu recomendación
ResponderEliminarMuchas gracias por el artículo
ResponderEliminarTutti... sos una maquina de magia... siempre lo dije y siempre lo voy a seguir diciendo! Gracias por el artículo... me vino de pelos ;-)
ResponderEliminarQue buen post, me sirvio para todo, Leandro una duda en la variable result en el lado de ajax cargas la matriz de datos, la desc y el value, que cantidad de datos puedes cargar de esta forma? es posible cargar unos 5000 a 50000 registros? Esto para armar bajo esta logica una tabla en vez de un dropdown.
ResponderEliminarPrimero felicidades y gracias por la ecepcional ayuda que tus blogs nos ofrece, ahora bien mi duda, es en base a la pregunta de Edalo, sobre la libreria json2, yo implemente tu ejemplo un proyecto, y por olvido no hice referencia a esta libreria y funciono correctamente, para que exactamente me sirve esta libreria?
ResponderEliminarGracias
hola IvanKike
ResponderEliminaresa libreria la usarias cuando quieres converir objetos de javascript a json para quizas trabjarlo como json, o como en estos casos enviarla como informacion a un webmethjod y que este mapee directo con clases de .net
si en tu caso no envias informacion compleja a los webmethods
o si armas el json directo como un string esta libreria no la usarias
hay muchos que el json lo armas concatenando un string en javascript, eso tambien es valido, pero con la libreria se hace mas simple
saludos
Buen post!, gracias!!
ResponderEliminarSe ve muy interesante,ahora mismo me pongo a entenderlos.Gracias :)
ResponderEliminarHola, genial el post, me funciona casi perfecto, el problema que tengo es que cuando estoy llenado el dropdownlist y al llegar al item 1500 ya produce error.
ResponderEliminarTe ha paso ?
Sabes si recibir los datos en javascript tiene algun limite maximo ?
Gracias
hola Javier
ResponderEliminarla verdad no me ha pasaso, pero tampoco soy de cargar tantos items en un combo, por lo general implemento otros metodos de selecion si los items son tan numerosos
puede que estes teniendo algun problema con el limite de informacion que puedas enviar en un servicio web, estos por defecto tienen un limite establecido y tambien un timeout
que error es el que recibes?
saludos
Hola Leandro, muy bueno tu ejemplo. Lo he implementado en un dialog con un drop dependiente de otros dos y funciona perfectamente.
ResponderEliminar´Por rizar más el rizo, ¿has hecho algo con un gridview que permita eliminación de filas por "RowCommand" y que esté dentro de un dialog de jquery?. He llegado hasta la eliminación de la fila pero para actualizar el grid no veo como hacerlo sin un postback. Si pudiera rellernarlo con $.ajax...pero no se me ocurre como. Si se te ocurre algo me orientas, gracias de antemano
Por cierto, Javier Rojas comentó el error por que cargaba demasiados items, si es algo como "...La longitud de la cadena supera el valor establecido en la propiedad maxJsonLength...", simplemnte hay que añadir en el webconfig el tamaño que deseamos, por ejemplo:
ResponderEliminaren la etiqueta system.web.extensions
dentro de ella en scripting
y dentro de ella en webServices
y dentro de ella: jsonSerialization maxJsonLength="1000000"
hola Bonaerge
ResponderEliminarpara eliminar de un grid que este dentro de jquery dialog podrias tomar dos caminos, o lo haces con $.ajax o sino suas el UpdatePanel
con este ultimo podrias seguir usando eventos sin que se actualice toda la pagina
con $.ajax ya no usarias el RowCommand, sino que vas a definir un boton o link de html y a este le adjuntas un click de jquery para que realice la accion
en realidad si vas a este punto lo mejor es dejar de usar el GridView y usas el jQGrid es muhco mejor control y esta preparado para este tipo de acciones
saludos
Gracias po rtu consejo Leandro, no quería usar updatepanel porque el ScrptManager me ralentiza uno ssegundos la carga de la página y no le gusta al cliente, asi que evito usar las herraminetas Ajax de Visual Studio y las Ajax Toolkit. Para eliminar el registro que me presenta el grid efectivamente lo hago sin problemas con $.ajax, pero obviamente eso no me refresca el grid del dialog, necesitaría recargarlo un poco como haces en los dd anidados con el segundo dropdown. jgrid no lo he usado, lo estudiare para ver si puedo aplicarlo. Gracias por tus consejos y cualquier nueva sugerencia sera bienvenida. Saludos
ResponderEliminarhola Bonaerges
ResponderEliminarimagino cuando dices grid es que usas el GridView, el tema es que este retorna el render de la tabla como html, y no permite volver a recargarlo solo en esa seccion.
quizas si usaras el grid dentro de un div y por medio de $.get podrias invocar el render del gridview para cargarlo con .html()
igual lo veo dificil porque en asp.net no existe el concepto de vista parcial como si esta en asp.net mvc, por lo que hacer esto implicaria que el viewstate los tag de html para el body y head volverian aparecer lo cual no seria util.
el jqGrid seria la mejor opcion, pero tambien esta el
http://www.datatables.net/
podria ser otra alternativa
saludos
muchas gracias leando me has ayudado mucho, te lo agradezco, saludos!
ResponderEliminarHola Leandro buenas noches, nuevamente molestandote y a la vez agradeciendote por compartir conocimientos...
ResponderEliminarBueno lo que pretendo hacer es devolver desde un procedimiento almacenado de sql server una consulta por medio de for xml raw ó for xml auto; bueno devolverlo es facil, ahora el tema es recibirlo desde la capa datos o web service con XmlReader pero dicho procedimiento devolvera una cadena (string) para ser transformado a json para de ahi procesarlo a mi gusto..
Estoy tratando de hacer ese juego de la conversion xml a json pero no hallo la manera, agradecere mucho tu apoyo
Estoy usando ahora ultimo el SqlXml SP3.0 pero aun no hallo la manera!!
Gracias...
hola Franky
ResponderEliminarpero porque desde un procedure tienes que devolver un xml, porque no conviertes los datos usando un datareader a una entidad o list definiendo una clase
para asi poder usar Json.net y convertir la lista a json
esto es mucho ams simple de lograr y no necesitas pasar por xml
saludos
Hayayay...al fin una luz de esperanza jeje. Leandro lo que pasa es que me dijeron que si devuelvo desde sql en formato xml (for xml raw) mis consultas serian mas rapidas, por eso buscaba esa opcion; favor de aclararme si el tiempo de respuesta con las listas genericas son las mismas.. Gracias
ResponderEliminarhola Franky
ResponderEliminarno veo que usar xml sea mas rapido que devolver registros simples y volcar los datos a entidades de negocio
aqui
jqGrid – Listar Orden Compra (Maestro-Detalle)
defino la capa de datos devolviendo entidades que luego se convirten para serializarlas a xml
en tu caso quizas no necesites convertir la entidad como si lo requiere jqgrid, pero si podras ver como volcar los datos de una query a entidades usando un datareader
saludos
Hola Leandro, mira que otra vez molestando, esta vez tengo un ModalPopUpExtender que contiene un panel el cual a su vez tiene un mantenimiento completo, todo eso ok!! pero el problema surge cuando cierro esa ventana modal y por estar dentro de un updatepanel no actualiza un dropdownlist que esta fuera con los nuevos datos, me podrias indicar si hay alguna manera de poder hacerlo aunque sea con javascript!! Gracias
ResponderEliminarhola Franky
ResponderEliminarel tema es que si los demas controles que quieres actualizar estan fuera del updatepanel vas a tener que actualizarlo usando javascript
aqui explico sobre el tema
[ASP.NET] PopUp Edición - Usando Ajax Toolkit ModalPopupExtender
saludos
Muy buen aporte, me sirvio bastante Gracias...
ResponderEliminarMuy buen aporte me sirvio bastante, Gracias!!!
ResponderEliminarHola Leandro, buenas noches, me parece muy interesante tu propuesta pero necesito saber de donde viene el objecto query que utilizas, ya que yo quiero devolver algo como esto para el combo return query.ToList();
ResponderEliminarPero no tengo el objeto query
Muchas Gracias.
Hola Leandro, buenas noches, me parece muy interesante tu propuesta pero necesito saber de donde viene el objecto query que utilizas, ya que yo quiero devolver algo como esto para el combo return query.ToList();
ResponderEliminarPero no tengo el objeto query
Muchas Gracias.
Hola Leandro ya tengo el metodo web montado tal como explicas ahora lo que necesito es llamarlo desde cualquier parte de la aplicacion.
ResponderEliminar¿Me podrias indicar como hacer la llamada en el parametro url del $.ajax o bien como podria incluir en un fichero de C# y hacer la llamada...?
Muchas Gracias.
hola leandro...
ResponderEliminaruna pregunta...
la carga de paises.. tambien podria utilizar webmethod ?
habria alguna diferencia :
-cargaria mas rapido ?
hola Maicol8k
ResponderEliminarsi tambien podria realizarse, es mas podrias no cargar nada en el Load de la pagina y ni bien se carga la pagina en el browser tener codigo jquery en el ready() para que realice estas invocaciones y cargue los combos
es cuestion de gustos, quizas para editar es mas simple tener los combos cargados desde codigo servidor asi se puede seleccionar el valor que se recupera de la db y luego la iteraccion con el usuario si realizarla dinamicamente
pero tambien se podria realizar todos desde el browser
saludos
Justo lo que andaba buscando, gracias por publicarlo.
ResponderEliminarSaludos.
Leandro qué tal, es grandioso tu post y de mucha ayuda...
ResponderEliminarMira tengo una duda, espero y puedas ayudarme, sino, de ante mano muchas gracias, lo que tengo es un dropdownlis, el cual es llenado de una BD, el método de llenado está en el codebehind; lo que necesito es obtener el valor o el id, al seleccionar un item de éste, obvio, lo quiero obtener para usarlo en Jquery, de igual froma uso un webmethod.
Ya he intendo varias formas de obtner el valor y pues ninguna me resulta.
¡Saluods!
hola Angel
ResponderEliminarpodrias usar
var id = $("#<%=dropdownlist1.ClientID%>").val();
o sino
var id = $("[id*='dropdownlist1']").val();
usando jquery y tomar el valor
saludos
Leandro que tal, me ocurre que al momento de ejecutar este post los eventos de los demás controles no funcionan, a que crees que se debe que el evento click por ejemplo no funcione?
ResponderEliminarGracias.
hola
EliminarPartamos de la base que si usas jquery para cargar lso items usando esta tecnica no deberias usar evento de asp.net.
Ahora para que los eventos funcionen deberias asignar el AutoPostBack = true del DropDownList
saludos
Gracias por responder, pero ya lo he intentado y no funciona, aunque si hace al parecer postback, pero no llega al código del control.
EliminarEsto es lo que tengo:
$(document).ready(function () {
$("#<%= SelectGrupo.ClientID%>").on('change', function () {
var idGrupo = document.getElementById('<%=SelectGrupo.ClientID%>').value;
var Parametros = { Grupo: idGrupo };
$.ajax({
type: "POST",
url: "/carpeta/default.aspx/LlenaComboCursos",
data: JSON.stringify(Parametros),
contentType: "application/json; charset=utf-8",
dataType: "Json",
success: function (response) {
$('#<%=SelectCurso.ClientID%>').html(response.d);
}
});
});
No me funciona el evento del botón ni el del dropdownlist,
Que crees que pueda estar sucediendo?
hola
EliminarNo entiendo, cual es el objetivo que buscas, si alli usas ajax con jquery cual es la necesidad que el evento realiza post al servidor en su evento ?
Otro detalle, si usas jquery puede seleccionar sin el getElementById(), podrias usar
var idGrupo = $(this).val();
en este caso this es el control al cual le adjuntas el evento
saludos
hola, recuerda que en una pagina tengo de todo, botones, caja de texto , dropdownlist, etc. pero por el momento solo necesito llenar el dropdownlist con el método que tu muestras mas arriba, el asunto esta en que luego de implementar tu método, los botones no funcionan al dar clic, no se ejecuta su evento correspondiente.
Eliminarhola
EliminarPuede que se este generando un error de javascript, no validaste con la Developer Tools (a la cual accedes con F12) usando la solapa de consola podrias ver si un error en el codigo cliente anula los eventos
Igualmente hay que remarcar que asp.net no se lleva muy bien con jquery cuando alteras los items de los controles, lo ideal es que si usas esta tecnica no uses evento, sino que las operaciones las realices con $.ajax de jquery invocando webmethods
saludos
Hola Leandro
ResponderEliminarcuando cambio el pais y la ciudad que se carga por primera vez me pasa este error:
Invalid postback or callback argument. Event validation is enabled using in configuration or <%@ Page EnableEventValidation="true" %> in a page. For security purposes, this feature verifies that arguments to postback or callback events originate from the server control that originally rendered them. If the data is valid and expected, use the ClientScriptManager.RegisterForEventValidation method in order to register the postback or callback data for validation.
hola
EliminarDe casualidad estos combos los trabajas con javascript o jquery ? porque este mensaje suele aparecer cuando trabjas con los controles de asp.net con codigo cliente
Es una validacion que aplica asp.net cuando compara los datos del control control el viewstate
saludos
Hola Leandro, el llamado de una funcion o metodo me funciona perfectamente dentro no tengo que invocar ningun control u objeto previamente definido. Tengo un caso en el que tengo que llenar un gridview y el datasource lo obtengo accediendo a una clase que me devuelve un datatable. Si convierto en estatico el metodo que realiza esta tarea, no puedo acceder ni al gridview ni al objeto que permite consumir los metodos de la clase.
ResponderEliminarprivate void CargarUsuarios()
{
try
{
DataTable dtUsuarios = OBJMensajes.CargarUsuarios(Convert.ToInt32(HttpContext.Current.Session["UsuarioActual"].ToString()));
gvUsuarios.DataSource = dtUsuarios;
gvUsuarios.DataBind();
}
catch (Exception ex)
{
throw ex;
}
}
Como puedo solucionar este problema? Muuchas gracias.