lunes, 28 de octubre de 2013

jqGrid y Asp.net, un primer acercamiento

Luego de varios días de intenso trabajo, hoy he tomado un pequeño espacio de tiempo para dedicar un post al trabajo con datos en forma de tablas en asp.net 

Todos los que hemos trabajado desde hace bastante con asp.net, hemos aprendido a querer al GridView, un excelente control que nos permite mostrar los datos directamente desde una base de datos y trabajar con ellos, llamase trabajar a las operaciones de: editar, buscar, agregar y eliminar. Por supuesto que existen muchas otras opciones que no son el objetivo de este post. A pesar de que e GridView es una buena herramienta para trabajar, tiene un inconveniente, es un control que funciona del lado del servidor, lo cual ocasiona un postback hacia el servidor, afectando muchas veces el rendimiento y obligándonos a controlar los parpadeos de la página con controles adicionales. Así que, para evitarnos todo eso, hoy veremos como trabajar con jqGrid, un control basado en jquery para realizar las operaciones básicas (CRUD). La principal ventaja de trabajar con este control, es que, al ser creado en jquery, es del lado del cliente, lo cual hace mucho mas eficiente y por supuesto, cuenta con opciones adicionales que el gridview no posee. 

Antes de continuar, debo recalcar que existe ya un control basado en jqGrid para asp.net, pero este tiene un costo superior a $300 lo cual para muchos no es accesible, así que tendremos que trabajar con el control opensource y hacer algunas adaptaciones. Para todos aquellos que les gusta ver un ejemplo antes de sumergirse en un proceso de estos, les dejo la pagina de demos Demos jqgrid 

Ahora sí, es momento de empezar con nuestro control.

Descarga del control 
Lo primero que requerimos para iniciar nuestro trabajo es descargar el control de la página oficial, en ella pueden escoger cuales serán las funciones que desean descargar, generalmente recomiendo que las descarguen todas.
Descarga jqgrid 

Preparando el sitio 
Una vez descargado abrimos Visual Studio seleccionamos Archivo>Nuevo>Sitio Web (en blanco sería mejor)

Ahora creamos una nueva carpeta y la llamamos js y ponemos en ella todos los scripts del jqgrid que descargamos anteriormente.

 Nuevamente creamos una nueva carpeta llamada css y de igual forma ponemos hay todos los archivos CSS y las imágenes. Si en la descarga no están incluidos los archivos css, no se preocupen, los encontrarán en el archivo para descargar al final del post.

 Con esto, ya estaríamos listos para iniciar a codificar el control.

Creando el control
Para este primer acercamiento, veremos como implementar la funcionalidades básicas. En otros post, veremos como crear un maestro detalle y como utilizar un SubGrid. Primer paso, agregar las referencias javascript a la página
    /**
     * SyntaxHighlighter
     */
    
    
    
    
    
    
    
    
    
    
    
    
    
Si vemos las referencias, únicamente hemos agregado algunas, que serán las que nos brinden las funcionalidades que requerimos para trabajar con las operaciones básicas. Una vez agregadas, crearemos una nueva función javascript en la cual codificaremos el jqgrid. Si vemos la documentaciónoriginal de jqGrid, veremos que jqgrid utiliza un atributo llamado colModel para crear las columnas del grid y que su definición es manual, es decir, se hace directamente en el control. Algo así: 

    /**
     * SyntaxHighlighter
     */
  colNames:['ID Number','Notes'],
  colModel:[
   {name:'id',index:'id', width:90, sorttype:"int", editable: true},                    
   {name:'note',index:'note', width:200, sortable:false,editable: true,edittype:"textarea", editoptions:
   rows:"2",cols:"10"}}                      
   ],
Esto, a mi parecer es algo no muy agradable, porque simplemente obligaría a tener que tocar código sensible del control cada vez que queramos mostrar información distinta. Así que, me di a la tarea de crear una nueva funcionalidad para el jqGrid que obtiene una serie de elementos desde un webservice vía Json y los mete en un array de jquery, de forma tal que podamos tener las columnas dinámicas. Este método, lo ejecutare antes de empezar la definición del jqGrid, es decir antes de la línea
    /**
     * SyntaxHighlighter
     */
 $("#grid").jqGrid(
Nuestro nuevo método luce así
    /**
     * SyntaxHighlighter
     */
 //aca es modificacion
           $.ajax({
                dataType: "json",
                type: "post",
                url: "JqGridEjemplo.aspx/GetNames",
                data: "{}",
                contentType: "application/json;",
                async: false, //esto es requerido, de otra forma el jqgrid se cargaria antes que el grid
                success: function (data) {
                  var cars = JSON.parse(data.d);
                    $.each(cars, function (index, car) {
                  colMode.push({ name: car.Name, index: car.index, width: car.width, align: 'left', editable: true, editrules: { edithidden: true }, hidden: false });
                     })
               } //or
           }),
El método anterior no tiene mucho especial que explicar: 1. Primero con el type=”post” hacemos referencia a la forma en que obtendremos los datos. 2.Con el data:”{}” le decimos al json que no vamos a enviarle parámetros 3. La url que direcciona al webservice 4. A la variable cars le asignamos el valor obtenido del Json 5. y Por ultimo con un each recorremos cada campo y lo metemos en el arreglo. El código del Jqgrid Al igual que el procedimiento anterior, para el jqgri tenemos que especificar los tipos de datos, la url y en este caso algunos parámetros que luego utilizaremos para traer los datos
    /**
     * SyntaxHighlighter
     */
 $("#grid").jqGrid(
Nuestro nuevo método luce así
    /**
     * SyntaxHighlighter
     */
 $("#grid").jqGrid(
   {
    datatype: function () {
     $.ajax(
         {
          url: "JqGridEjemplo.aspx/GetPersons", //PageMethod
         data: "{'pPageSize':'" + $('#grid').getGridParam("rowNum") +
          "','pCurrentPage':'" + $('#grid').getGridParam("page") +
          "','pSortColumn':'" + $('#grid').getGridParam("sortname") +
          "','pSortOrder':'" + $('#grid').getGridParam("sortorder") + "'}", //PageMethod Parametros de entrada
             dataType: "json",
             type: "post",
             contentType: "application/json; charset=utf-8",
             complete: function (jsondata, stat) {
             if (stat == "success")
               jQuery("#grid")[0].addJSONData(JSON.parse(jsondata.responseText).d);
                   else
               alert(JSON.parse(jsondata.responseText).Message);
                }
              });
            },
         jsonReader: //Set the jsonReader to the JQGridJSonResponse squema to bind the data.
         {
            root: "Items",
            page: "CurrentPage",
            total: "PageCount",
            records: "RecordCount",
            repeatitems: true,
            cell: "Row",
            id: "ID" //index of the column with the PK in it    
        },
Luego, establecemos las columnas utilizando el colmodel y algunos parámetros de configuración de nuestro grid
    /**
     * SyntaxHighlighter
     */
    colModel: colMode,
    pager: "#pager", //Pager.
    loadtext: 'Cargando datos...',
    recordtext: "{0} - {1} de {2} elementos",
    emptyrecords: 'No hay resultados',
    pgtext: 'Pág: {0} de {1}', //Paging input control text format.
    rowNum: "10", // PageSize.
    rowList: [10, 20, 30], //Variable PageSize DropDownList. 
    viewrecords: true, //Show the RecordCount in the pager.
    multiselect: true,
    sortname: "Name", //Default SortColumn
    sortorder: "asc", //Default SortOrder.
    width: "800",
    height: "230",
    caption: "Personas",
    ondblClickRow: function (id) {
      gdCustomers.restoreRow(lastSel);
      gdCustomers.editRow(id, true);
      lastSel = id;
    }
Jqgrid tiene la posibilidad de mostrar en su barra inferior las opciones de insert,delete, add, update y refresh, así que las configuraremos de la siguiente manera
    /**
     * SyntaxHighlighter
     */
    }).navGrid("#pager", { edit: true, add: true, search: true, del: true },
    { url: "jqGridEjemplo.aspx/EditData", closeAfterEdit: true },
    { url: "jqGridEjemplo.aspx/EditData", closeAfterAdd: true },
    { url: "jqGridEjemplo.aspx/DeleteData" });
Acá vemos que al pager, le estamos diciendo que si vamos a permitir todas las opciones y además, para cada una de ellas estamos asignando una dirección en especifico, que de igual forma, apuntan a un procedimiento en un webservice. Estas opciones son completamente configurables y por supuesto que bueno estar deshabilitadas o no presentes por ahora, tenemos que colocarlas manualmente, luego trabajaremos en hacerlo de forma dinámica. Para concluir con la configuración de nuestro jqgrid, necesitamos configurar las opciones de agregar y borrar utilizando el método jquery.extend, de otra forma aunque configuremos el jqgrid estas opciones nos devolverían un error.
    /**
     * SyntaxHighlighter
     */
    jQuery.extend(jQuery.jgrid.edit, {
        ajaxEditOptions: { contentType: "application/json" },
             recreateForm: true,
             serializeEditData: function (postData) {
             return JSON.stringify(postData);
              }
            });

     jQuery.extend(jQuery.jgrid.del, {
           ajaxDelOptions: { contentType: "application/json" },
           serializeDelData: function (postData) {
           return JSON.stringify(postData);
            }
         });
     });
Finalmente, el codigo completo seria
    /**
     * SyntaxHighlighter
     */
    
Ahora que la configuración el grid esta completa, es hora de trabajar el html donde se mostrarán los datos, siendo tan sencillo como agregar una tabla y un div
    /**
     * SyntaxHighlighter
     */
  
Los datos a mostrar, los traeremos de SQL utilizando un procedimiento almacenado que luce asi
    /**
     * SyntaxHighlighter
     */
  USE [JqGrid]
   GO
   /****** Object:  StoredProcedure [dbo].[GetPersons]    Script Date: 05/19/2011 13:29:50 ******/
   SET ANSI_NULLS ON
   GO
   SET QUOTED_IDENTIFIER ON
   GO
  -- GetPersons 3, 4, 'LastName', 'desc'
   ALTER procedure [dbo].[GetPersons]
   @PageSize int , 
   @CurrentPage int , 
   @SortColumn varchar(20), 
   @SortOrder varchar(4)
    as
   declare @RecordCount int
   declare @PageCount int
   declare @PageIndex int
   Select @RecordCount = count(ID)from Person
   set @PageCount = Ceiling(cast (@RecordCount as float) / cast (@PageSize as float))
   if (@CurrentPage > @PageCount) set @CurrentPage = @PageCount
   set @PageIndex = @CurrentPage - 1
   Select RecordCount = @RecordCount, PageCount = @PageCount, CurrentPage = @CurrentPage
   declare @Query varchar(300)
   set @Query = 'Select ID, Name, LastName, BirthDate, Weight, RowNumber = ROW_NUMBER() OVER (ORDER BY ' + @SortColumn + ' ' + @SortOrder + ')from Person' 
  set @Query = 'Select ID, Name, LastName, BirthDate, Weight from (' + @Query + ' )as result 
  where RowNumber BETWEEN ' + cast(@PageSize * @PageIndex + 1 as varchar(10)) + ' 
        AND ' + cast(@PageSize * (@PageIndex + 1) as varchar(10))
 
Exec (@Query)

Si observamos bien el Stored Procedure veremos que recibe algunos parámetros que estamos enviando por Json cuando la url que carga los datos es activada. El primer metodo que utilizaremos para cargar los datos con base en el stored procedure será
    /**
     * SyntaxHighlighter
     */
  
   internal static JQGridJsonResponse GetPersonasJSon(int pPageSize, int pPageNumber, string pSortColumn, string pSortOrder)
   {
       SqlConnection sqlCon = new SqlConnection(ConfigurationManager.ConnectionStrings["DataConnection"].ConnectionString);
       SqlCommand command = new SqlCommand("GetPersons", sqlCon);
       command.CommandType = CommandType.StoredProcedure;
       command.Parameters.Add("PageSize", SqlDbType.Int).Value = pPageSize;
       command.Parameters.Add("CurrentPage", SqlDbType.Int).Value = pPageNumber;
       command.Parameters.Add("SortColumn", SqlDbType.VarChar, 20).Value = pSortColumn;
       command.Parameters.Add("SortOrder", SqlDbType.VarChar, 4).Value = pSortOrder;
       DataSet dataSet = new DataSet();
       SqlDataAdapter dataAdapter = new SqlDataAdapter(command);
       dataAdapter.Fill(dataSet);
       var persons = new List();
       foreach (DataRow row in dataSet.Tables[1].Rows)
        {
            Person person = new Person
            {
               ID = Convert.ToInt32(row["ID"]),
               Name = row["Name"].ToString(),
               LastName = row["LastName"].ToString(),
               BirthDate = Convert.ToDateTime(row["BirthDate"]),
               Weight = Convert.ToSingle(row["Weight"])
            };
            persons.Add(person);
       }
  
return new JQGridJsonResponse(Convert.ToInt32(dataSet.Tables[0].Rows[0]["PageCount"]), Convert.ToInt32(dataSet.Tables[0].Rows[0]["CurrentPage"]),Convert.ToInt32(dataSet.Tables[0].Rows[0]["RecordCount"]), persons);

 }
Sin embargo, este método solo llena los datos, pero aun no los devuelve, eso lo haremos a través de un webservice de la siguiente forma
    /**
     * SyntaxHighlighter
     */
  [WebMethod]
   [ScriptMethod(ResponseFormat = ResponseFormat.Json)]
   public static JQGridJsonResponse GetPersons(int pPageSize, int pCurrentPage, string pSortColumn, string pSortOrder)
   {
      return GetPersonasJSon(pPageSize, pCurrentPage, pSortColumn, pSortOrder);
  }
Es importante que las líneas ResponseFormat=ResponseFormat.Json estén incluidas para que la información que se envié sea codificada de la forma correcta. También debemos notar que ambos metodos hacen referencia a JQGridJsonResponse que es una clase definida para almacenar los datos y llenar cada item
    /**
     * SyntaxHighlighter
     */
   /// 

  /// Respuesta JSON para JQGrid.
 /// 
 public class JQGridJsonResponse
 {

   #region Passive attributes.
  private int _pageCount;
  private int _currentPage;
  private int _recordCount;
  private List _items;
 #endregion
#region Properties
  /// 
  /// Cantidad de páginas del JQGrid.
  /// 
  public int PageCount
  {
    get { return _pageCount; }
   set { _pageCount = value; }
   }

  /// 
 /// Página actual del JQGrid.
   /// 
 public int CurrentPage
   {
     get { return _currentPage; }
    set { _currentPage = value; }
   }

   /// 
   /// Cantidad total de elementos de la lista.
   /// 
   public int RecordCount
   {
     get { return _recordCount; }
     set { _recordCount = value; }
   }

  /// 
  /// Lista de elementos del JQGrid.
  /// 
  public List Items
  {
    get { return _items; }
    set { _items = value; }
  }
  
 #endregion
#region Active attributes

 /// 
 /// Constructor.
 /// 
 /// Lista de elementos a mostrar en el JQGrid
 public JQGridJsonResponse(int pPageCount, int pCurrentPage, int pRecordCount, List pPersons)
  {
     _pageCount = pPageCount;
     _currentPage = pCurrentPage;
    _recordCount = pRecordCount;
   _items = new List();
     foreach (Person person in pPersons)
     _items.Add(new JQGridItem(person.ID, new List  { person.ID.ToString(),person.Name, person.LastName, person.BirthDate.ToShortDateString(), person.Weight.ToString() }));
  }
  
#endregion  
 }

Pero, nuevamente debemos tener cuidado, ya que esta clase llama a otra clase que contiene los atributos del grid llamada JQGridItem
    /**
     * SyntaxHighlighter
     */
   /// 
  /// Item del JQGrid. Elemento de la propiedad Items de la clase JsonJQGridResponse.
 /// 
  public class JQGridItem
   {
    #region Passive attributes
   private long _id;
   private List _row;
   #endregion

 #region Properties
    /// 
   /// RowId de la fila.
   /// 
  public long ID
   {
      get { return _id; }
      set { _id = value; }
    }

  /// 
  /// Fila del JQGrid.
 /// 
 public List Row
   {
      get { return _row; }
     set { _row = value; }
  }
 #endregion

 #region Active Attributes
 /// 
 /// Contructor.
 /// 
  public JQGridItem(long pId, List pRow)
   {
      _id = pId;
      _row = pRow;
    }
#endregion
}

Con esto, nuestro jqGrid sería totalmente funcional para mostrar los datos, paginar y ordenar pero aún no lo es para guardar, editar o borrar. Estos método al igual que los anteriores, serán definidos en un webservice y serán más sencillos de utilizar pues básicamente reciben los datos mediante Json y lo demás se traduce en código que ya conocemos.
    /**
     * SyntaxHighlighter
     */
  
[WebMethod]
 [ScriptMethod(ResponseFormat = ResponseFormat.Json)]
 public static void DeleteData(string id,string oper)
   { 
    if (String.Compare(oper, "del", StringComparison.Ordinal) == 0)
     {
      using (SqlConnection conn = new SqlConnection(ConfigurationManager.ConnectionStrings["DataConnection"].ConnectionString))
        {
          using (SqlCommand command = new SqlCommand("delete from person where id=@id", conn))
             {
                conn.Open();
                command.Parameters.AddWithValue("@id", id);
                command.ExecuteNonQuery();
               }
          }
       }
     }
    /**
     * SyntaxHighlighter
     */
    [WebMethod]
    [ScriptMethod(ResponseFormat = ResponseFormat.Json)]
    public static void EditData(string BirthDate, string LastName, string Name, string Weight, string id, string oper)
     {
        if (String.Compare(id, "_empty", StringComparison.Ordinal) == 0 ||
           String.Compare(oper, "add", StringComparison.Ordinal) == 0)
         {
      using (SqlConnection conn = new SqlConnection(ConfigurationManager.ConnectionStrings["DataConnection"].ConnectionString))
            {
               using (SqlCommand command = new SqlCommand("insert into person (name,lastname,birthdate,weight)values(@name,@lname,@date,@peso)", conn))
                 {
                    conn.Open();
                    command.Parameters.AddWithValue("@id", id);
                    command.Parameters.AddWithValue("@name", Name);
                    command.Parameters.AddWithValue("@lname", LastName);
                    command.Parameters.AddWithValue("@date", Convert.ToDateTime(BirthDate));
                    command.Parameters.AddWithValue("@peso", double.Parse(Weight));
                    command.ExecuteNonQuery();
                 }
             }
        }
        else if (String.Compare(oper, "edit", StringComparison.Ordinal) == 0)
        {
          using (SqlConnection conn = new SqlConnection(ConfigurationManager.ConnectionStrings["DataConnection"].ConnectionString))
          {
          using (SqlCommand command = new SqlCommand("update person set name=@name,lastname=@lname,birthdate=@date,weight=@peso where id=@id", conn))
              {
                 conn.Open();
                 command.Parameters.AddWithValue("@id", id);
                 command.Parameters.AddWithValue("@name", Name);
                 command.Parameters.AddWithValue("@lname", LastName);
                 command.Parameters.AddWithValue("@date", Convert.ToDateTime(BirthDate));
                 command.Parameters.AddWithValue("@peso", double.Parse(Weight));
                 command.ExecuteNonQuery();
                 }
             }
         }     
   }
 



Ejemplo para descargar

0 comentarios:

Publicar un comentario

Twitter Delicious Facebook Digg Stumbleupon Favorites More

 
Design by Free WordPress Themes | Bloggerized by Lasantha - Premium Blogger Themes | Hosted Desktops