domingo, 29 de abril de 2012

[ASP.NET] PopUp Filtro – Usando Ajax Toolkit ModalPopupExtender

 

Introducción

En esta oportunidad analizaremos como implementar un filtro usando los controles de ajax tookit, mas concretamente el control ModalPopupExtender

Se hará uso de una lista de productos la cual será filtrada por proveedores, desplegados en el popup.

Algo que notaran en el ejemplo será dos formas de implementar la selección del proveedor:

- en una se contara con al ayuda de ToolkitScriptManager para registrar la información seleccionada, será necesario un .js (ListProducts.js), encargado de tomar la info y asignarla a los controles

- la otra será una asignación directa usando jquery (esta ultima seria la recomendable, porque requiere menos objetos involucrados en su implementación)

 

Abrir ventana de búsqueda

La apertura de la ventana de filtro será realizada por medio de un evento asp.net, pero para poder hacerlo es necesario deshabilitar el javascript que asocia el botón con el ModalPopupExtender.

Para lograrlo es que se asigna la propiedad TargetControlID a un objeto hidden, como muestra la imagen

Esto permite que desde el botón se pueda cargar los ítems en el gridview que formara parte del popup de filtro, si se hubiera asignado directo el botón al ModalPopupExtender, este deshabilita los evento de asp.net, por lo que la carga del grid no se hubiera podido llevar a cabo.

 protected void btnOpenSupplierSearch_Click(object sender, EventArgs e)
 {
     LoadGridSuppliers();

     ModalPopupExtender1.Show();

 }

private void LoadGridSuppliers()
 {
     gvSuppliers.DataSource = NorthwindData.GetAllSuppliers();
     gvSuppliers.DataBind();
 }

El uso del Show() sobre el ModalPopupExtender abrirá la ventana, luego de cargar el grid que contiene.

 

Selección ítem en el popup

La selección del proveedor será implementada por medio de un botón de imagen de asp.net, el cual lanzara el evento del gridview, es por eso que se asocia el CommandName=”Select”

La acción de este evento podría ser implementada de dos formas:

Usando al funcionalidad de ToolkitScriptManager

En la implementación de esta opción puede implicar varios puntos a tener en cuenta, el primero involucra hacer uso del ToolkitScriptManager para asignar la información que se quiere devolver como resultado de la selección

protected void gvSuppliers_SelectedIndexChanging(object sender, GridViewSelectEventArgs e)
{
    string id = Convert.ToString(gvSuppliers.DataKeys[e.NewSelectedIndex].Value);
    string name = gvSuppliers.Rows[e.NewSelectedIndex].Cells[2].Text;

    //
    // asigno la informacion a los controles que estan fuera de updatepanel
    //
    ToolkitScriptManager1.RegisterDataItem(txtId, id);
    ToolkitScriptManager1.RegisterDataItem(lblSupplierName, name);


    //
    // Cargo grid de productos con el dato seleccionado
    //
    LoadGridProducts(id);

    //
    // cierro el popup
    //
    ModalPopupExtender1.Hide();
}

 

Pero además implica hacer uso de JavaScript para poder tomar la información enviada en el RegisterDataItem(), es por eso que se asocia un .js que cumplirá esta función

<asp:ToolkitScriptManager ID="ToolkitScriptManager1" runat="server">
    <Scripts>
        <asp:ScriptReference Path="~/ListProducts.js" />
    </Scripts>
</asp:ToolkitScriptManager>

El contenido del .js implementa el siguiente código

Sys.WebForms.PageRequestManager.getInstance().add_pageLoading(SupplierFilterHandler);

function SupplierFilterHandler(sender, args) {
    var datos = args.get_dataItems();

    //obtenermos el id geenrado por asp.net, o sea el ClientID
    var txtId = $("[id*='txtId']").attr('id');
    var lblSupplierName = $("[id*='lblSupplierName']").attr('id');
    

    if (datos[txtId] == undefined)
        return;

    $("[id*='txtId']").val(datos[txtId]);
    $("[id*='lblSupplierName']").text(datos[lblSupplierName]);

}

La idea es poder tomar la información enviada desde el código .net desde el cliente y asignarla a los controles todo ello desde código cliente, sin mezclar parte de .net con parte javascript, separando cada responsabilidad

Se usa el *= ya que el id del control es generado por asp.net (el cual puede cambiar del asignado desde el diseñador), por eso se ubica el nombre real generado, ya que no se disponer del ClientID para tomarlo desde el .js

 

Usando directamente jquery

Este seria el camino directo para asignar la información del filtro en la pagina principal

protected void gvSuppliers_SelectedIndexChanging(object sender, GridViewSelectEventArgs e)
{
    string id = Convert.ToString(gvSuppliers.DataKeys[e.NewSelectedIndex].Value);
    string name = gvSuppliers.Rows[e.NewSelectedIndex].Cells[2].Text;

    //
    // asigno la informacion a los controles que estan fuera de updatepanel
    //
    string script = @" $(""[id*='txtId']"").val('{0}');
                       $(""[id*='lblSupplierName']"").text('{1}');";

    script = string.Format(script, id, name.Encode());

    ScriptManager.RegisterStartupScript(this, typeof(Page), "filterinfo", script, true);

    //
    // Cargo grid de productos con el dato seleccionado
    //
    LoadGridProducts(id);

    //
    // cierro el popup
    //
    ModalPopupExtender1.Hide();
}

 

En esta implementación solo es necesario definir el script que se encargara de asignar los datos a los controles y enviarlo al cliente por medio del ScriptManager.RegisterStartupScript().

Esto es mucho mas directo y sin vueltas que el paso anterior, aunque requiere generar código javascript (ayudado con jquery) inserto en medio del con código .net en el servidor, a veces puede gustar, a veces no, mezclar este tipo de código, pero si hay que reconocer que es el camino directo en su implementación.

 

Código
[C#]
 

13 comentarios:

  1. Bueno como siempre muy buenos tus artículos.
    Tengo una consulta , cuando hice mi propia versión a manera de practica, en la parte del evento SelectedIndexChanging del gvSuppliers, preferí crear una función en el lado del cliente(para aprovechar que allí si puedo acceder directamente a los ID's de los controles)

    function ProveedorFiltrado(id, name) {
    $("#<%=txtId.ClientID %>").val(id);
    $("#<%=lblSupplierName.ClientID %>").text(name);

    }
    Y ya en el lado del servidor usaba algo parecido a lo que hiciste:

    Dim script As String = "ProveedorFiltrado('" + id + "','" + name + "');"
    ScriptManager.RegisterClientScriptBlock(Me, GetType(Page), "filterinfo", script, True)

    Lo que no me gusta es la forma en la que paso las variables (no me quiero imaginar si tuviera que pasar mas de 6 parámetros) Me preguntaba si no había una forma de pasarlos como array???(creo haber visto antes una forma pero no recuerdo )

    ResponderEliminar
  2. hola Antonio

    en javascript existen los array

    Arrays en Javascript

    JavaScript: funciones básicas para arrays

    podrias a la funcion pasarle un array con item por cada valor

    en realidfad mejor seria un json, pero si es para datos simples quizas seria complicarlo mucho

    saludos

    ResponderEliminar
  3. Mi duda iba más a como pasar el arreglo desde VB , pero creo que por más que arme el array igual terminaría indicando uno a uno los elementos del array (las parámetros de la función)

    ResponderEliminar
  4. Hola Leandro, disculpa la molestia pero tengo una duda sobre tu clase Util.cs, bueno es más sobre el Gridview y su comportamiento por defecto de codificar el html de las columnas(BoundFields).

    Entiendo que uses Util.cs para codificar el apostrofe e impedir que en el javascript el apostrofe sea tomado como fin de cadena cosa que produciría un error:

    $("[id*='lblSupplierName']").text('Mayumi's');

    Hasta allí todo entendido, pero aquí viene mi problema(no sé si solo me pase a mi)

    Esta linea en lugar de retornar : "Mayumi's"
    Dim name As String = gvSuppliers.Rows(e.NewSelectedIndex).Cells(2).Text

    retorna esta cadena donde el apostrofe esta codificado : "Mayumi& #39;s".

    Sé que eso no representa mayor problema porque con agregarle HtmlEncode="false" a la definición de esa columna se soluciona el problema. Luego viene el uso de tu extensión : "Encode" , pasas el valor al javascript, pero allí viene el otro detalle. Al estar el apostrofe codificado la cadena que mostrará el label : lblSupplierName será : "Mayumi& #39;s", siendo necesario decodificar la cadena antes de mostrarla(también ya solucionado http://stackoverflow.com/questions/10715801/javascript-decoding-html-entities).

    Ya antes había recuperado valores de columnas de un griview y puede que sea porque NO contenían caracteres especiales, que nunca había tenido que lidiar con estas procesos de codificación/de-codificación y me llamó la atención que no lo señalaras en tu artículo. Por eso quisiera saber si es que solo me ocurre a mi que el valor de la celda le regresa codificado y luego al mostrar el el label el valor también le aparece codificado("Mayumi& #39;s").

    Disculpa si pregunto mucho :S

    Saludos

    ResponderEliminar
  5. Very good information, a greeting from http://www.systemdeveloper.info

    ResponderEliminar
  6. Bunos días Leandro, gracias por tus buenos aportes, aprovecho para preguntar lo siguiente: estoy realizando una consulta a una base de datos Oracle cuya consulta capturo con una variable tipo DataTable, esta almacena las filas de la consulta. Para cargar estos datos recuperados a un GridView lo que hago es:

    grid1.DataSource= variable;//variable es tipo datatable
    grid1.DataBind();

    estoy en lo correcto?, he visto mucha información respecto al tema y se supone esa es la solución, pero da la casualidad que a mi no me funciona.. a que se debe. lo mismo he realizado para cargar datos en un drooplist.datasource pero no me funciona.. que me recomiendas, Saludos.

    ResponderEliminar
  7. hola Byron

    el camino que describes es el correcto, pero cuando dices no funciona se debe a un error o simplemente no muestra ningun registro

    la query que defines tiene algun parametro? quizas para el valor que asignas no hay ninguna coincidencia, podrias probar de lanzar la query sin ningun filtro para ver si muestra informacion

    saludos

    ResponderEliminar
  8. Hola Leandro, porque cada vez que pulso enter en un textbox siempre se habre mi panel como controlo eso ?

    ResponderEliminar
    Respuestas
    1. hola
      podria ser que este actuando el default button definido en la pagina asp.net
      Setting Default Button in ASP.Net Web Page
      como veas al presionar enter la pagina define un boton para accionar
      saludos

      Eliminar
  9. Hola leandro intente con lo del default button y si pasando lo mismo, es mas cuando pulso algun boton dentro de otro grid view que lo muestro en la pagina sigue apareciendo el modalpopupextender.

    ResponderEliminar
    Respuestas
    1. hola
      el tema es que tienes botones dentro del grid, quizas debas usar jquery para inhabilitar la funcionalidad de enter de la pagina y poder controlar que accion quieres realizar al dar enter en ese textbox
      How To Disable Enter Key On Forms
      saludos

      Eliminar
  10. Estimado Leandro tengo un inconveniente ojala puedas ayudarme

    Tengo 2 formularios principal.aspx y popup.aspx, en el load popup.aspx por código le puse que muestre un msgbox "Hola Alumno XXX"); lo que sucede es que al cargar principal.ASPX muestra este mensaje como si hubiera cargado el formulario popup.aspx ese momento y aparece el mensaje "Hola alumno XXX" , y lo peor de todo es que al momento de llamar al popup.aspx ya no muestra el mensaje.

    ResponderEliminar
    Respuestas
    1. hola

      Lo primero que podrias mencionar es que nunca deberias usar el msgbox en un desarrollo web, de usa javascript con el alert para mostrar mensajes, ya que el msgbox se ejecuta del lado del servidor

      Ademas no mencionas como lanzas el popup, entiendo lo haces por javascript con el window.open('url'), no?

      saludos

      Eliminar