domingo, 3 de noviembre de 2013

[ASP.NET MVC] WebGrid Filtrado por DropDownList

 

Introducción


En el articulo aprenderemos :

  • como capturar la selección de un DropDownList
  • filtrar una query usando Entity Framework
  • desplegar los datos en un WebGrid

La idea es listas en un dropdownlist los clientes (customers) y al seleccionar alguno, listar las ordenes de compra realizadas.

image

 

Modelo de Dominio


Empezaremos analizando el dominio de entidades que usaremos en el ejemplo.

image

La entidad Orden tiene relación con el cliente (customer) y un empleado (employee), usaremos el primero para filtrar mientras que el segundo será información adicional que se mostrara en una columna del grid

 

Definición de la persistencia


El acceso a los datos lo realizaremos con al ayuda de Entity Framework, por lo que deberemos definir el DbContext y las clases de mapping para cada entidad

 

public class NorthWindContext : DbContext
{

    public NorthWindContext()
        : base("NorthwindDb")
    {
        Database.SetInitializer<NorthWindContext>(null);

        this.Configuration.LazyLoadingEnabled = false;
        this.Configuration.ProxyCreationEnabled = false;
    }

    public DbSet<Customer> Employees { get; set; }

    protected override void OnModelCreating(DbModelBuilder modelBuilder)
    {
        modelBuilder.Configurations.Add(new CustomerMap());
        modelBuilder.Configurations.Add(new EmployeeMap());
        modelBuilder.Configurations.Add(new OrderMap());

        modelBuilder.Configurations.Add(new LocationComplexMap());

        base.OnModelCreating(modelBuilder);
    }

}


public class CustomerMap : EntityTypeConfiguration<Customer>
{
    public CustomerMap()
    {
        HasKey(x => x.CustomerID);

        Property(x => x.CustomerID)
                .HasColumnType("nchar")
                .HasMaxLength(5);


        Property(x => x.CompanyName)
                .HasMaxLength(40)
                .IsRequired();

        Property(x => x.ContactName)
                .HasMaxLength(30);

        Property(x => x.ContactTitle)
                .HasMaxLength(30);

    }
}

public class EmployeeMap : EntityTypeConfiguration<Employee>
{
    public EmployeeMap()
    {
        HasKey(x => x.EmployeeID);

        Property(x => x.LastName)
                .HasMaxLength(20)
                .IsRequired(); ;

        Property(x => x.FirstName)
                .HasMaxLength(10)
                .IsRequired();

        Ignore(x => x.FullName);

    }
}

public class OrderMap : EntityTypeConfiguration<Order>
{
    public OrderMap()
    {
        HasKey(x => x.OrderID);

        Property(x => x.CustomerID)
            .HasColumnType("nchar")
            .HasMaxLength(5)
            .IsOptional();

        HasOptional(x => x.Customer)
                .WithMany()
                .HasForeignKey(x => x.CustomerID);

        HasOptional(x => x.Employee)
                .WithMany()
                .HasForeignKey(x => x.EmployeeID);

        Property(x => x.ShipName)
                .HasMaxLength(40);

        Property(x => x.ShipAddress)
                .HasMaxLength(60);

    }
}


public class LocationComplexMap : EntityTypeConfiguration<Localization>
{
    public LocationComplexMap()
    {
        Property(x => x.Address).HasColumnName("Address").HasMaxLength(60);
        Property(x => x.City).HasColumnName("City").HasMaxLength(15);
        Property(x => x.Region).HasColumnName("Region").HasMaxLength(15);
        Property(x => x.PostalCode).HasColumnName("PostalCode").HasMaxLength(10);
        Property(x => x.Country).HasColumnName("Country").HasMaxLength(15);
    }
}

 

En este caso estamos utilizando una db existente por lo tanto importante definir la línea:

Database.SetInitializer<NorthWindContext>(null);

de esta forma indicamos que no se debe crear la db cuando ejecutemos la aplicacion

 

Definir datos del dropdownlist


Para la vista definiremos un modelo que nos permita especificar los datos que mostraremos al usuario.

public class CustomerOrdersModel
{

    public SelectList CustomerList { get; set; }

    public List<Order> OrderList { get; set; }

}

 

Es por eso que encontraremos dos listas, una para cargar los ítems del combo y la segunda para las ordenes del grid.

Al ingresar al action Index(), el cual responde a los pedidos GET, del controlador debemos cargar la lista del combo, nos ayudaremos con el repositorio para esta tarea.

public ActionResult Index()
{

    CustomerOrdersModel model = new CustomerOrdersModel()
    {
        CustomerList = new SelectList(repository.GetAllWithoutSelection(), "CustomerID", "CompanyName")
    };

    return View(model);
}

 

la vista hará uso de las propiedades del modelo para cargar el dropdownlist

SNAGHTML978badb8

 

Capturar selección dropdownlist


La captura de la selección puede realizarse de varias formas, pero no debe perderse de vista que es la propiedad “name” de los controles html los que participan en el post que se realiza al servidor, no hay que confundirse con la propiedad “id” esta solo es útil para manipular el control en el cliente usando jquery.

Para capturar la acción del usuario al cambiar la selección en el dropdownlist nos ayudamos de jquery, para poder definir el selector definimos un id en el tag <form>

SNAGHTML97918cef

Es en ese momento cuando forzamos un submit para invocar a un action concreto

image

en el controlador encontraremos

[HttpPost]
public ActionResult Index(string CustomerList)
{
    //
    // usamos el metodo del repositorio el cual nos permite recuperar las odenes de compra del cliente
    // se incluye en esta la informacion del cliente y el empleaod que realizo la venta
    //
    var orders = orderRepository.Filter(x => x.CustomerID == CustomerList,
                                            new List<Expression<Func<Entities.Order,object>>>() { x=>x.Employee, x=>x.Customer });

    CustomerOrdersModel model = new CustomerOrdersModel()
    {
        CustomerList = new SelectList(repository.GetAllWithoutSelection(), "CustomerID", "CompanyName"),
        OrderList = orders
    };


    return View(model);
}

 

El nombre del parámetro debe coincidir con la propiedad “name” del control para poder tomar el dato, en este caso hemos dejado el valor por defecto que crear asp.net mvc, pero podríamos redefinirlo.

SNAGHTML97af81c9

Si en la vista cambiamos la propiedad “name” debemos también hacerlo en el parámetro del action.

SNAGHTML97a1f0d0

 

 

Definición del WebGrid


El post al action devolverá un nuevo modelo con la lista de ordenes cargada

image

En la definición del grid es cuando indicamos que propiedad del modelo define los datos

var grid = new WebGrid(Model.OrderList, canPage: false, canSort: false);

luego se define el lugar donde ira el grid y que columnas se mostrara

 

@if (Model.OrderList != null)
{
    @grid.GetHtml(
                tableStyle: "webgrid-table",
                headerStyle: "webgrid-head",
                alternatingRowStyle: "webgrid-alternating-row",
                selectedRowStyle: "webgrid-selected-row",
                
                columns: grid.Columns(
                    grid.Column(columnName: "Employee.FullName", header: "Empleado", style: "webgrid-column-employee"),
                    grid.Column(columnName: "OrderDate", header: "Fecha Pedido", format: x=>x.OrderDate.ToString("dd/MM/yyyy")),
                    grid.Column(columnName: "RequiredDate", header: "Fecha Requisito", format: x=>x.RequiredDate.ToString("dd/MM/yyyy")),
                    grid.Column(columnName: "ShippedDate", header: "Fecha Envio", format: x=>x.ShippedDate != null ? x.ShippedDate.ToString("dd/MM/yyyy") : ""),
                    grid.Column(columnName: "ShipName", header: "Nombre Envio"),
                    grid.Column(columnName: "ShipAddress", header: "Direccion Envio") 
                
                ))
}
else
{
    <p>No hay datos</p>
}

se podría resaltar como se especifica una columna cuando la entidad esta relacionada con otra

grid.Column(columnName: "Employee.FullName", header: "Empleado", style: "webgrid-column-employee"),

al necesitar el nombre del empleado se realiza por medio de la relación

También se definió formato para las columnas fecha

grid.Column(columnName: "ShippedDate", header: "Fecha Envio", format: x=>x.ShippedDate != null ? x.ShippedDate.ToString("dd/MM/yyyy") : ""),

no solo indicando la representación de la fecha sino también validando cuando esta pueda tener un valor nulo

 

WebGrid referencia en asp.net mvc


Según el tipo de proyecto que crees en el Visual Studio puede que no tengas acceso al WebGrid, si este es el caso deberá agregar la referencia a la librería, para esta tarea NuGet nos dará una mano

Solo es cuestión de buscar: microsoft-web-helper

en la herramienta de nuget y proceder a instalarlo

SNAGHTML988cc7c2

 

Código


Para el ejemplo hemos utilizado Visual Studio 2012, la base de datos se encuentra en la carpeta App_Data del proyecto web

[C#]