Manipulación de datos en AJAX mediante el control ModalPopupExtender (y II)

En la primera entrega de este artículo abordamos el desarrollo de un servicio WCF que permitía a los elementos de un control AJAX el acceso básico a la tabla Shippers de la base de datos Northwind, en esta segunda parte continuaremos con el resto de operaciones de edición necesarias para implementar un mantenimiento de datos sobre dicha tabla.


 


Métodos del servicio que reciben parámetros


La siguiente tarea a desarrollar consistirá en obtener el registro de la tabla Shippers relacionado con el elemento del DropDownList seleccionado por el usuario;  para ello necesitamos añadir al servicio WSDatos un método que reciba como parámetro el valor del campo ShipperID a buscar, y que devuelva dicho registro como resultado.


[OperationContract]
public Shipper ObtenerShipper(int nShipperID)
{
SqlConnection cnConexion = new SqlConnection(
ConfigurationManager.ConnectionStrings[«CadConexion»].ConnectionString);

SqlCommand cmdComando = new SqlCommand(«SELECT * FROM Shippers WHERE ShipperID = @ShipperID»,
cnConexion);
cmdComando.Parameters.AddWithValue(«@ShipperID», nShipperID);

cnConexion.Open();
SqlDataReader drReader = cmdComando.ExecuteReader(CommandBehavior.SingleRow);

drReader.Read();

Shipper oShipper = new Shipper((int)drReader[«ShipperID»],
drReader[«CompanyName»].ToString(),
drReader[«Phone»].ToString());

cnConexion.Close();

return oShipper;
}


Como hemos dicho, este método del servicio será llamado desde la página Web cada vez que se realice una nueva selección en el control ddlShippers, es decir, al producirse su evento cliente onchange. Puesto que se trata de un control de servidor, la manera más adecuada de asociar uno de sus eventos cliente con el nombre de la función Javascript que ejecutará, es desde el code-behind de la página -en el evento Load por ejemplo-, empleando su colección Attributes, como vemos a continuación.


public partial class _Default : System.Web.UI.Page
{
protected void Page_Load(object sender, EventArgs e)
{
this.ddlShippers.Attributes.Add(«onchange», «ddlShippers_onchange()»);
}
}

Pasando al bloque Javascript del WebForm, en la función ddlShippers_onchange llamaremos a este nuevo método del servicio, pasándole como primer parámetro el valor del elemento seleccionado en la lista desplegable, y como segundo el nombre de la función que será llamada si la ejecución del método tiene éxito; en dicha función recibiremos como parámetro el objeto Shipper seleccionado, pasando los valores de sus propiedades a los controles TextBox del cuadro de diálogo.


<script type=»text/javascript» language=»javascript»>
   1:  
   2: //….
   3: function ddlShippers_onchange()
   4: {
   5:     WSDatos.ObtenerShipper(document.getElementById(«ddlShippers»).value,
   6:         ObtenerShipperCompleted);
   7: }
   8:  
   9: function ObtenerShipperCompleted(oShipper)
  10: {
  11:     document.getElementById(«txtCompanyName»).value = oShipper.CompanyName;
  12:     document.getElementById(«txtPhone»).value = oShipper.Phone;
  13: }
</script>

En la siguiente figura podemos apreciar un ejemplo de este caso.



Al pulsar el botón btnAceptar del diálogo, traspasaremos los valores elegidos al formulario principal; para ello, tendremos que asignar a la propiedad OnOkScript del ModalPopupExtender, el nombre de la función Javascript que se encargará de dicha tarea.


<cc1:ModalPopupExtender ID=»mpeShippers» runat=»server»
….
OnOkScript=»mpeShippers_OnOk()» />

//….
<script type=»text/javascript» language=»javascript»>
//….
function mpeShippers_OnOk()
{
document.getElementById(«txtTransportista»).value = document.getElementById(«txtCompanyName»).value + ‘-‘ +
document.getElementById(«txtPhone»).value;
}
//….


 



 


Métodos del servicio para la edición de datos. Insertar, modificar y borrar


El resto de métodos que nos queda por escribir son aquellos relacionados con las operaciones de edición sobre la tabla Shippers. Al igual que en los anteriores casos, en primer lugar añadiremos estos métodos al servicio, como vemos en el siguiente código fuente.


[OperationContract]
public void InsertarShipper(string sCompanyName, string sPhone)
{
SqlConnection cnConexion = new SqlConnection(
ConfigurationManager.ConnectionStrings[«CadConexion»].ConnectionString);

SqlCommand cmdComando = new SqlCommand(«INSERT INTO Shippers VALUES (@CompanyName, @Phone)»,
cnConexion);
cmdComando.Parameters.AddWithValue(«@CompanyName», sCompanyName);
cmdComando.Parameters.AddWithValue(«@Phone», sPhone);

cnConexion.Open();
cmdComando.ExecuteNonQuery();
cnConexion.Close();
}

[OperationContract]
public void ModificarShipper(int nShipperID, string sCompanyName, string sPhone)
{
SqlConnection cnConexion = new SqlConnection(
ConfigurationManager.ConnectionStrings[«CadConexion»].ConnectionString);

string sSQL = «UPDATE Shippers «;
sSQL += «SET CompanyName = @CompanyName, «;
sSQL += «Phone = @Phone «;
sSQL += «WHERE ShipperID = @ShipperID»;

SqlCommand cmdComando = new SqlCommand(sSQL, cnConexion);
cmdComando.Parameters.AddWithValue(«@CompanyName», sCompanyName);
cmdComando.Parameters.AddWithValue(«@Phone», sPhone);
cmdComando.Parameters.AddWithValue(«@ShipperID», nShipperID);

cnConexion.Open();
cmdComando.ExecuteNonQuery();
cnConexion.Close();
}

[OperationContract]
public void BorrarShipper(int nShipperID)
{
SqlConnection cnConexion = new SqlConnection(
ConfigurationManager.ConnectionStrings[«CadConexion»].ConnectionString);

string sSQL = «DELETE FROM Shippers «;
sSQL += «WHERE ShipperID = @ShipperID»;

SqlCommand cmdComando = new SqlCommand(sSQL, cnConexion);
cmdComando.Parameters.AddWithValue(«@ShipperID», nShipperID);

cnConexion.Open();
cmdComando.ExecuteNonQuery();
cnConexion.Close();
}


Como detalle aclarativo para el método InsertarShipper, recordemos que el campo ShipperID de la tabla Shippers es autonumérico, por lo que no es necesario calcular el valor para dicho campo al insertar un nuevo registro en la tabla.


A continuación pasaremos al código de marcado de la página Web, asignando al evento onclick de los botones correspondientes, el nombre de la función que realizará la operación de edición pertinente.


….
<input id=»btnInsertar» type=»button» value=»Insertar» onclick=»btnInsertar_onclick()» />
….
<input id=»btnModificar» type=»button» value=»Modificar» onclick=»btnModificar_onclick()» />
….
<input id=»btnBorrar» type=»button» value=»Borrar» onclick=»btnBorrar_onclick()» />
….

Solamente queda ya, escribir las funciones Javascript que realizarán las llamadas a los métodos de edición del servicio WCF, como se muestra en el siguiente bloque de código.


<script type=»text/javascript» language=»javascript»>
   1:  
   2: //….
   3: function btnInsertar_onclick()
   4: {
   5:     WSDatos.InsertarShipper(document.getElementById(«txtCompanyName»).value,
   6:         document.getElementById(«txtPhone»).value,            
   7:         InsertarShipperOnSuccess);
   8:  
   9:     LimpiarCargarCombo();       
  10: }
  11:  
  12: function InsertarShipperOnSuccess()
  13: {
  14:     alert(«Registro añadido»);
  15: }
  16:  
  17: function btnModificar_onclick()
  18: {    
  19:     var ddlShippers = document.getElementById(«ddlShippers»);
  20:  
  21:     WSDatos.ModificarShipper(ddlShippers.value,
  22:         document.getElementById(«txtCompanyName»).value,
  23:         document.getElementById(«txtPhone»).value);
  24:         
  25:     LimpiarCargarCombo();
  26: }    
  27:  
  28: function btnBorrar_onclick()
  29: {
  30:     var resultado = confirm(«¿Seguro que quiere borrar?»);
  31:     
  32:     if (resultado == true)
  33:     {
  34:         var ddlShippers = document.getElementById(«ddlShippers»);
  35:         
  36:         WSDatos.BorrarShipper(ddlShippers.value);
  37:  
  38:         LimpiarCargarCombo();
  39:     }        
  40: }
  41:  
  42: function LimpiarCargarCombo()
  43: {    
  44:     document.getElementById(«txtCompanyName»).value = «»;
  45:     document.getElementById(«txtPhone»).value = «»;   
  46:  
  47:     var ddlShippers = document.getElementById(«ddlShippers»);
  48:     
  49:     while (true)
  50:     {            
  51:         if (ddlShippers.length == 0)
  52:         {
  53:             break;
  54:         }
  55:         else
  56:         {
  57:             ddlShippers.remove(0);
  58:         }
  59:     }   
  60:     
  61:     WSDatos.ObtenerShippers(ObtenerShippersCompleted);
  62: }
</script>

Y tras este conjunto de operaciones podemos dar por concluida la segunda entrega del artículo, en el que hemos ilustrado una técnica que permite a un control AJAX acceder y manipular una tabla existente en una base de datos desde código Javascript.


Para poder realizar las pruebas necesarias, al igual que en la primera parte, el código fuente queda disponible en los siguientes enlaces: C# y VB. Espero que os resulte interesante


Un saludo.

19 Comentarios

  1. anonymous

    estoy tratando de hacer un ejemplo parecido a la pantalla de transporte en donde en vez de un combo tengo un cuadro de texto y a la hora de digitar un codgio me aparezca los demas datos , como podria lograrlo????

  2. lmblanco

    Hola Guido

    Puedes probar añadiendo al servicio WCF un método que reciba como parámetro el código que hayas tecleado en el textbox del modalpopup reservado para buscar datos. Dentro de este método del servicio, ejecutas una consulta contra la base de datos que te devuelva los datos necesarios dentro de un objeto de la clase que se utiliza para enviar la información desde el servicio hasta el lado cliente.

    //———————————
    [OperationContract]
    public Shipper ObtenerShipper(int nShipperID)
    {
    SqlConnection cnConexion = new SqlConnection(
    ConfigurationManager.ConnectionStrings[«CadConexion»].ConnectionString);

    SqlCommand cmdComando = new SqlCommand(«SELECT * FROM Shippers WHERE ShipperID = @ShipperID»,
    cnConexion);
    cmdComando.Parameters.AddWithValue(«@ShipperID», nShipperID);

    cnConexion.Open();
    SqlDataReader drReader = cmdComando.ExecuteReader(CommandBehavior.SingleRow);

    drReader.Read();

    Shipper oShipper = new Shipper((int)drReader[«ShipperID»],
    drReader[«CompanyName»].ToString(),
    drReader[«Phone»].ToString());

    cnConexion.Close();

    return oShipper;
    }
    //———————————

    Pasando al lado cliente del proceso, supongamos que en el modalpopup tenemos un botón que será el encargado de llamar al método del servicio, pasándole el código, para seguidamente, recoger el resultado en forma de objeto, cuyas propiedades asignaremos a los controles del modalpopup.

    //———————————

    //———————————

    Espero que con algunas modificaciones, te sirva para que funcione en tu caso.

    Un saludo.
    Luismi

  3. anonymous

    ante todo gracias Luis, en base a tu ejemplo he desarrollado un webforms(no utilizo modal popup extender) que utiliza la tabla shippers , el funcionamiento es el siguiente digito el ShipperId y me aparece CompanyName y Phone.

    realize lo siguiente

    1-cree un namespace para contener todas las clases que voy a utilizar

    2-Creo el servicio Web

    3-Creo las funciones JavaScript que van hacer llamado de los servicios web

    4-en el page load pongo lo siguiente
    If Not IsPostBack Then
    TxtTra.Attributes.Add («onblur», «TxtTransportista_Validar();»)
    End If

    esto me permite que cuando pierde el foco se active la funcion de java que asu vez manda a llamar el servicio web que me trae los datos(companyName, Phone).

    tengo 2 preguntas

    1-en windows forms en el textbox hay un evento que se llama validated entonces todo lo que digitaba pasaba por ese evento y luego pues mandaba a llamar los datos, hay alguna otra forma de hacerlo aparte de utilizar servicio web y utilizar onblur(lostfocus)????

    2-ese mismo ejemplo lo quise realizar en masterpage pero a la hora de digitar ShipperId no esta trayendo los datos, no se cual es el problema ?????????

    te adjunto el codigo del webforms con masterpage
    < %@ Page Title="" Language="VB" MasterPageFile="~/MasterPage.master" AutoEventWireup="false" CodeFile="PruebaEnter.aspx.vb" Inherits="PruebaEnter" %>


    Gracias de antemano por tu ayuda…..

  4. lmblanco

    Hola Guido

    Respecto a la primera de tus preguntas, puedes utilizar alguno de los diversos controles de validación existentes en ASP.NET para validar los contenidos de un control del formulario. En el siguiente ejemplo puedes ver un TextBox al que se asocia un validador de tipo RequiredFieldValidator, el cual mostrará un literal de aviso en el caso de que el TextBox no se rellene:

    //——————————


    //——————————

    En cuanto a la segunda pregunta, cuando en una aplicación empleamos páginas master, debemos tener en cuenta que para evitar problemas en lo que respecta a conflictos con los identificadores de los controles, ASP.NET reasigna estos identificadores. De esta forma, supongamos que en nuestra página tenemos un control TextBox con el identificador txtNombre; si usamos páginas maestras, el identificador real que aparece en el html generado sería algo así: ctl00_ContentPlaceHolder1_txtNombre, por lo que es muy posible que el problema que estás teniendo es debido a esta causa.

    Una forma de solucionarlo consistiría en generar el script cliente desde el codebehind de la página, haciendo uso de la propiedad ClientID que tienen los controles, la cual nos devuelve el identificador real que el control tiene en el html generado. Pongamos como ejemplo una aplicación en la que hemos creado un servicio wcf como el siguiente:

    //——————————
    [ServiceContract(Namespace = «»)]
    [AspNetCompatibilityRequirements(RequirementsMode = AspNetCompatibilityRequirementsMode.Allowed)]
    public class WSDatos
    {
    [OperationContract]
    public string DevolverNombre()
    {
    return «valor obtenido del servicio wcf»;
    }
    }
    //——————————

    Añadimos también a esta aplicación una página master y después un webform que utiliza dicha página master. Si en este webform queremos que al pulsar un botón, se haga una llamada al servicio wcf pero desde código javascript, el botón lo crearemos como un control input básico. El objetivo a cumplir es que al llamar al método del servicio wcf, el resultado obtenido por dicha llamada sea depositado en un control TextBox.

    //——————————
    < %@ Page Title="" Language="C#" MasterPageFile="~/Maestra.Master" AutoEventWireup="true" CodeBehind="WebForm1.aspx.cs" Inherits="PruManipDatosMaster.WebForm1" %>












    //——————————

    Para poder hacer una referencia correcta hacia el identificador del TextBox, en el evento Load del webform construiremos el script de cliente, en el que utilizaremos la propiedad ClientID del TextBox. Dicho script lo registraremos en la página mediante la propiedad ClientScript de la página, y su método RegisterClientScriptBlock, de forma parecida a la siguiente:

    //——————————
    protected void Page_Load(object sender, EventArgs e)
    {
    System.Text.StringBuilder sbScripCliente = new System.Text.StringBuilder();
    sbScripCliente.Append(«function Button1_onclick() { n»);
    sbScripCliente.Append(«WSDatos.DevolverNombre(DevolverNombreCompleted); n»);
    sbScripCliente.Append(«} n»);

    sbScripCliente.Append(«function DevolverNombreCompleted(sCadena) { n»);
    sbScripCliente.Append(«document.getElementById(‘» + this.txtNombre.ClientID + «‘).value = sCadena; n»);
    sbScripCliente.Append(«} n»);

    this.ClientScript.RegisterClientScriptBlock(this.GetType(),
    «CodigoJavascript», sbScripCliente.ToString(), true);
    }
    //——————————

    De esta manera, al llamar al servicio wcf desde el lado cliente, el valor devuelto por el servicio será depositado correctamente en el control de la página.

    Espero que este ejemplo te sirva de ayuda.

    Un saludo.
    Luismi

  5. anonymous

    Excelente , estoy realizando una variacion del ejemplo de shippers tengo el siguiente escenario en el formulario normal tengo los 3 campos TxtidShippers, TxtCompanyname,TxtPhone ala par de txtidshippers tengo un boton de buscar, al darle clcik en el botn de buscar me aparece el modal popup extender que contiene un textbox y un grid en el grid posee un command field el cual me va a permitir seleccionar el registro que quiero pasar al texbox TxtidShippers hay un boton , la busquedad la realizo por compania si escribo DHL, me aparece todas las coincidencias de dhl

    mi modal popu extender quedaria de la siguiente manera

    no se como asociar el boton commanfield al OkControlID para que me pase el valor seleccionado a TxtidShippers

    este es el codigo completa de la pantalla

    < %@ Register Assembly="AjaxControlToolkit" Namespace="AjaxControlToolkit" TagPrefix="cc1" %>












       




  6. lmblanco

    Hola Guido

    Cuando en un control GridView defines una columna de tipo CommandField para poder seleccionar filas, debes tener en cuenta que cuando el usuario pulse en una de las celdas de dicha columna, se producirá un post back de la página, y si el GridView lo tienes dentro de un panel ModalPopupExtender, este será cerrado.

    Dado que la pulsación sobre la celda del CommandField provoca el cierre inmediato del ModalPopup, para obtener el valor clave del registro seleccionado en el GridView, lo que puedes hacer en tu código es escribir el evento OnSelectedIndexChanged del GridView, dentro de cuyo código puedes averiguar este dato y pasarlo al control de la página que necesites o realizar otra acción relacionada con esta operación.

    Supongamos que en nuestro formulario tenemos un código similar al siguiente, donde definimos un ModalPopupExtender que contiene un GridView:

    //—————————————-















    //—————————————-

    Puesto que hemos declarado en el GridView el evento OnSelectedIndexChanged, en el codebehind podemos escribir el siguiente código para obtener el valor de la clave del registro seleccionado y la asignamos a otro control de la página de la siguiente forma:

    //—————————————-
    protected void GridView1_SelectedIndexChanged(object sender, EventArgs e)
    {
    DataKey dk = this.GridView1.SelectedDataKey;
    this.txtTransportista.Text = dk.Value.ToString();
    }
    //—————————————-

    Espero que te sirva para aplicarlo a tu caso.

    Un saludo.
    Luismi

  7. anonymous

    Gracias con tu idea logre hacer lo que que queria , ahora bien tengo un problema con el servicio web, despues que termino mi jornada laboral al dia siguiente que vengo me da este error Microsoft JScript runtime error: ‘Webservice’ is undefined, derrepente ya me vuelve afuncionar no detecto cual es el error.

    asi tengo declarado el berservice






    tambien lo tengo en el formulario que lo utilizo






  8. lmblanco

    Hola Guido

    Aparentemente, la declaración del webservice en el ScriptManager es correcta. Quizá pudiera deberse a algún problema por estar utilizando también un ScripManagerProxy. Te paso la dirección de un tutorial sobre este último que quizá te sirva de ayuda.

    http://www.asp.net/Learn/ajax-videos/video-95.aspx

    Un saludo.
    Luismi

  9. anonymous

    Entonces Luismi

    gracias a tu ayuda he realizado un webforms que tiene la funcionalidad del 100% de un windows forms. este web forms posee Grabar,Eliminar,Buscar ,validaciones de campos etc.

    el webforms trabaja de la siguiente manera
    en la casilla codigo digito el valor y me trae los demas datos correspondientes.

    ahora bien mi siguiente paso es crear un maestro detalle , siguiendo la misma logica en un campo codigo digitar por ejemplo el 10 y luego traer los datos correspondientes al encabezado y al detalle de la factura.

    amigo mio te recuerdo que yo utilizo un servicio web y el paso de textbox a javascript es de la siguiente manera document.all(«< %=TxtCodigo.clientid %>«).value, ademas utilizo una clase . mi pregunta puntual y directa como puedo hacer para pasar el Gridview a java y acceder a cada elemento del grid para asociarlo a una clase.

    adjunto el codigo

    Gracias por tu invaluable ayuda..

    Denis Guido
    Nicaragua

  10. anonymous

    tengo un modal popup en el cual tengo un par de campos con validadores..
    el tema es q cuando hago click en el Boton Update, en lugar de mostrarme los errores me cierra la pantalla.. como puedo solucionar esto?

  11. lmblanco

    Hola Juli

    Parece ser que al pulsar el botón se está produciendo un envío del formulario al servidor, por lo que podrías solucionarlo utilizando un botón html simple:

    y realizando la validación en el lado cliente.

    Un saludo,
    Luismi

  12. anonymous

    hola luis

    hace mucho tiempo desde lo ultimo que te consulte, resulta que quiero hacer una facturacion web, pero lo que realmente me esta dando dificultades es el maestro detalle , no tengo idea como hacerlo… agradeceria tus comentarios y si tienen algunos sitios de ejemplo que apliquen una facturacion web sencilla ….

    Denis Guido
    Nicaragua

  13. lmblanco

    Hola Guido

    En el siguiente enlace tienes un ejemplo de creación de un maestro-detalle.

    http://tydw.wordpress.com/2008/02/14/asp-net-formulario-maestro-detalle-aplicable-a-vb-y-c/

    Por otro lado, en esta otra dirección de los grupos de discusión de asp.net, se recomiendan algunos enlaces sobre ejemplos de desarrollo de facturación y pedidos.

    http://groups.google.es/group/microsoft.public.es.dotnet.aspnet/browse_thread/thread/fd9b97a6278ebb89?pli=1

    Espero que te sean de utilidad.

    Un saludo,
    Luismi

  14. anonymous

    estimado Luismi..

    soy un programador en windows forms , estoy acostumbrado al enter , al validate .. , etc.. mi pregunta es como realizar un sistema web o mejor dicho cual es el standar para realizar un sistema web .. por ejemplo contabilidad… mira he buscado ejemplos de sistemas web … pero no he encontrado .. lo que se haya es carrito de compras …. eso es comun , en un sistema de contabilidad tengo Plan de cuentas, Comprobantes de diario.. etc… , talvez puedes realizar un articulo como comenzar a realizar un sistema web , cual es el standard a seguir… lo correcto en web…

    Saludos cordiales desde Nicaragua

  15. lmblanco

    Hola Guido

    Te paso un enlace a una página de Microsoft en la que hay información sobre el desarrollo de contabilidades con .NET.

    http://msdn.microsoft.com/es-es/library/aa288571(VS.71).aspx

    Un saludo,
    Luismi

  16. anonymous

    hola hermano ke tal tengo un problema con el codigo descargado, el error surge en el web.config linea 78 como puedo solucionarlo soy un completo now en asp.net toda ayuda sera bienvenida respuesta urgente a solo_flash@hotmail.com

  17. lmblanco

    Hola Sam

    La lína que indicas del web.config se refiere al compilador utilizado para generar el proyecto, quizá el problema radique en que usas una versión distinta del compilador o de Visual Studio. Puedes probar a crear un nuevo proyecto desde tu Visual Studio siguiendo los pasos que se comentan en el artículo.

    Un saludo,
    Luismi

Responder a Cancelar respuesta

Tema creado por Anders Norén