Mostrando entradas con la etiqueta ASP.NET MVC. Mostrar todas las entradas
Mostrando entradas con la etiqueta ASP.NET MVC. Mostrar todas las entradas

sábado, 28 de diciembre de 2013

[ASP.NET MVC] Carga DropDownList dependientes - Usando un único action

 

Introducción


Este articulo propone una versión diferente a la planteada en el anterior

[ASP.NET MVC] Carga DropDownList dependientes

En este caso definiremos un único action para recibir la selección que realice el usuario.

La funcionalidad de la pantalla se conserva idéntico al articulo anterior, al igual que el modelo de datos.

 

Modelo de Vista


Para el modelo se define una clase que recibirá las propiedades con la selección del usuario, además de propiedades que permitirán cargar los controles de listas en la view

image

 

Definición de la View


La vista esta formada por un solo Form, el cual realiza el post al action Index() el cual se encuantra marcado con el atributo [HttpPost]

SNAGHTML19f9df10

 

Tanto si se selecciona un ítem combo, o se presiona el botón del submit (el cual selecciona los empleados) todas las acciones invocan al único action

A diferencia del articulo anterior ya no se requieren de hidden para conservar la selección de la acción anterior.

 

Definición del action


El action implementar bastante mas lógica que la usada en el articulo anterior, ya que deberá determinar que propiedades vienen con datos y cargar las listas en consecuencia.

 

[HttpPost]
public ActionResult Index(EmployeeModel model)
{
    //se carga la lista de regiones
    var regionList = regionRepository.GetAll();

    List<Territory> territoryList = null;
    List<Employee> employeeList = null;

    //si hay una region seleccionada 
    if (model.RegionId != null)
    {
        //se carga la lista de territorios
        territoryList = territoryRepository.Filter(x => x.RegionID == model.RegionId);

        //si hay un territorio seleccionado
        if (!string.IsNullOrEmpty(model.TerritoryId))
        {
            //se recupera el territorio, para poder obtener los empleados asociados
            employeeList = territoryRepository.GetEmployees(model.TerritoryId);
        }

    }


    model.RegionList = new SelectList(regionList, "RegionID", "Description");

    if (territoryList != null && territoryList.Count > 0)
    {
        model.TerritoryList = new SelectList(territoryList, "TerritoryID", "Description");
    }

    if (employeeList != null && employeeList.Count > 0)
    {
        model.EmployeeList = new SelectList(employeeList, "EmployeeID", "FullName");
    }


    return View("Index", model);

}

 

Si se selecciona una región se recibirá el valor en el parámetro del modelo

SNAGHTML1a04b6aa

 

Si luego se selecciona un territorio

SNAGHTML1a08bf80

 

Y al marcar empleados sobre el listbox y presionar el botón de submit se carga la lista

SNAGHTML1a0b84e0

 

En cada invocación al acción será necesario volver a cargar las listas del model utilizadas para definir los ítems de los controles la la view, ya que estas no participan en el proceso de binding donde son asignados los valores que se envían en al post a las propiedades del parámetro del action.

 

Model Binding


Para entender como se produce la asignación de la selección de los controles con las propiedades del modelo es necesario evaluar el html

Si las propiedades “name” de los controles que se renderizan en el html coinciden con los nombres de las propiedades de la clase definida como modelo la asignación del valor es automáticamente.

 

SNAGHTML1a315f77

si algún control no llegara a tener esta coincidencia se puede redefinir el name utilizando

image

 

Código


 

[C#]
 

viernes, 27 de diciembre de 2013

[ASP.NET MVC] Carga DropDownList dependientes (cascada o secuencial)

 

Introducción


Es muy común en las aplicaciones necesitar de controles que dependan unos de otros para cargar los ítems que serán desplegados al usuario.

En este articulo explicare como llevar a cabo esta tarea implementando la carga en cascada de combos y listas, además poder tomar en cada post la selección que realiza el usuario.

El ejemplo permitirá seleccionar una región la cual será utilizada como filtro para cargar los territorios, este ultimo cargar una lista de empleados, el usuario seleccionara uno o mas de la lista.

 

image

 

Modelo de datos


Haremos uso del siguiente modelo de datos el cual define las clases que serán mapeadas a las tablas utilizando Entity Framework

Vemos como interactúan las entidades para permitir la asociaciones que las vinculan.

image

 

Modelo de Vista


Para poder interactuar con al view de mvc creamos una clase que actuara de modelo, esta nos permitirá definir los ítems de las listas que desplegaremos al usuario.

En el modelo tendremos propiedades simples que contendrán la selección del usuario y también lista que nos permitirán cargar los dropdownlist y listbox que presentaremos al usuario.

 

image

Las propiedades de lista se definen del tipo SelectList, de esta forma desde el controlador indicamos cuales serán las propiedades que usara la lista como value y display en el dropdownlist

 

Lista de Regiones


Al iniciar la aplicación el primer controlador que se ejecuta es el Index(), en este invocamos al repositorio para obtener la lista de regiones

image

 

Creamos una instancia de la clase Model que el view requiere, a esta le asignamos la lista de regiones que mostrara en el combo

En la view se carga el dropdownlist y se controla con la ayuda de jquery cuando el usuario cambia la selección, lo cual produce el post del form.

En la imagen podemos ver como se relacionan los id del dropdownlist y del form con el evento change() de jquery para generar el submit al action del controlador

 

SNAGHTML146b09bb

 

Lista de Territorios


Al seleccionar una región se invoca el action definido en el BeginForm()

SNAGHTML14776cd1 

Si hacemos coincidir el name del control dropdownlist con el del parámetro del action este recibirá el valor. Usaremos la selección para filtrar la lista de territorios.

 

Lista de Empleados


Al seleccionar un ítem del combo de territorios se dispara el submit del forma que invoca el action del controlador, en este caso se pasaran como argumento el id de la región y del territorio para poder filtrar los datos y cargar las listas

 

SNAGHTML14cd3b57

Seguramente se habrá notado la definición de controles Hidden que permiten enviar en cada submit del form los datos seleccionados en una acción anterior

 

Selección de empleados


Cuando se selecciona los empleados de la lista y se presiona el botón se realiza el submit enviando la la selección del listbox al action

SNAGHTML14d57876

En cada invocación a los action se vuelve a crear una instancia del model asignando la selección y cargando las listas que requiere el view

 

Código


Se utilizo Visual Studio 2012, la base de datos de encuentra en la carpeta App_Data, se utilizo Sql Server Express 2012

[C#]
 

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#]