Reportes de datos a través de los controles GridView, CheckBox y TextBox de DotVVM
Tags: webdev, html, dotnet, dotvvm
Published: 2020-06-20 20:47:18

Una tabla es un conjunto de celdas organizadas dentro de las cuales podemos alojar distintos contenidos. Este tipo de elemento es bastante útil al momento de realizar un reporte para visualizar alguna información en específico. 

En artículos anteriores sobre ASP.NET Core y DotVVM, podíamos ver de manera general como utilizar controles predefinidos de estas herramientas para la visualización de datos a manera de reporte. Estos son algunos de esos artículos: 

+ [DotVVM y ASP.NET Core: Implementación de operaciones CRUD](https://dev.to/dotvvm/dotvvm-y-asp-net-core-implementacion-de-operaciones-crud-43oe).

+ [Trabajando con ASP.NET Core y DotVVM en Visual Studio Code](https://dev.to/dotvvm/trabajando-con-asp-net-core-y-dotvvm-en-visual-studio-code-586l).

Asimismo, cuando se manejan formularios para administrar información, es necesario crear tablas para poder visualizar los datos tratados. Estos son algunos artículos previos por si deseas aprender como diseñar formularios con HTML: 

+ [Creación de formularios para páginas web con los controles de DotVVM](https://dev.to/dotvvm/creacion-de-formularios-para-paginas-web-con-los-controles-de-dotvvm-5eoi).

+ [Diseño de formularios web con DotVVM Business Pack](https://dev.to/dotvvm/diseno-de-formularios-web-con-dotvvm-business-pack-43mm).

En esta ocasión aprenderemos los fundamentos para visualizar determinados datos y establecer algunos criterios de búsqueda con C# y HTML, a través de los controles de DotVVM sobre ASP.NET Core. 

Nota: el código fuente del proyecto que analizaremos en este articulo lo puedes encontrar en este repositorio de GitHub: [DotVVM Reports](https://github.com/esdanielgomez/DotVVMReports).

####El patrón de diseño Modelo, Vista, Vistamodelo - MVVM

Un aspecto importante por mencionar es sobre la base de DotVVM. Este marco de trabajo se fundamenta en el patrón de diseño Modelo, Vista, Vistamodelo sobre .NET para la comunicación entre HTML (páginas web) y C# (código fuente). La finalidad de estas partes son las siguientes: 

+ **El modelo**. — es responsable de todos los datos de la aplicación y de la lógica de negocios relacionada.

+ **La vista**. — Representaciones para el usuario final del modelo de la aplicación. La vista es responsable de mostrar los datos al usuario y de permitir la manipulación de los datos de la aplicación.

+ **El Modelo-Vista o Vista-Modelo**. — uno o más por vista; el modelo-vista es responsable de implementar el comportamiento de la vista para responder a las acciones del usuario y de exponer los datos del modelo fácilmente.

####Reporte de datos con ASP.NET Core y DotVVM 

Para ejemplificar la utilización de algunos controles de DotVVM para la realización de un reporte, tendemos una pequeña aplicación como esta: 

![](https://dev-to-uploads.s3.amazonaws.com/i/2o6qmhfjukd8saikm1rl.png)

Teniendo en cuenta el patron MVVM – Modelo, Vista, Vistamodelo, analizaremos de forma general cada una de estas partes para este proyecto. 

**Modelo**

Teniendo en cuenta que los datos de la aplicación y de la lógica de negocios relacionada se maneja en esta sección, a continuación, veremos cómo se manejan los datos y los servicios correspondientes. 

La base de datos está conformada por dos tablas: `Person` y `PersonType`. 

![](https://dev-to-uploads.s3.amazonaws.com/i/mkyuk0nqianq04z5v6cr.png)

Las sentencias SQL para la creación de estas tablas, sus atributos y la inserción de algunos registros son las siguientes:

```sql
CREATE SCHEMA IF NOT EXISTS `db` DEFAULT CHARACTER SET utf8;
USE `db` ;

CREATE TABLE IF NOT EXISTS `db`.`PersonType` (
  `Id` INT NOT NULL,
  `Name` VARCHAR(45) NOT NULL,
  `Description` VARCHAR(45) NOT NULL,
  PRIMARY KEY (`Id`))
;

CREATE TABLE IF NOT EXISTS `db`.`Person` (
  `Id` INT NOT NULL AUTO_INCREMENT,
  `FirstName` VARCHAR(45) NOT NULL,
  `LastName` VARCHAR(45) NOT NULL,
  `IdPersonType` INT NOT NULL,
  PRIMARY KEY (`Id`),
  FOREIGN KEY (`IdPersonType`) REFERENCES `db`.`PersonType` (`Id`))
;

INSERT INTO `persontype` (`Id`, `Name`, `Description`) VALUES ('1', 'Type A', '');
INSERT INTO `persontype` (`Id`, `Name`, `Description`) VALUES ('2', 'Type B', '');

INSERT INTO `person` (`Id`, `FirstName`, `LastName`, `IdPersonType`) VALUES ('1', 'Sergey', 'Brin', '1');
INSERT INTO `person` (`Id`, `FirstName`, `LastName`, `IdPersonType`) VALUES ('2', 'Larry', 'Page', '1');
INSERT INTO `person` (`Id`, `FirstName`, `LastName`, `IdPersonType`) VALUES ('3', 'Tim', 'Barners', '2');
INSERT INTO `person` (`Id`, `FirstName`, `LastName`, `IdPersonType`) VALUES ('4', 'Linus', 'Torvalds', '1');
INSERT INTO `person` (`Id`, `FirstName`, `LastName`, `IdPersonType`) VALUES ('5', 'Larry', 'Ellison', '1');
INSERT INTO `person` (`Id`, `FirstName`, `LastName`, `IdPersonType`) VALUES ('6', 'Steve', 'Ballmer', '2');
INSERT INTO `person` (`Id`, `FirstName`, `LastName`, `IdPersonType`) VALUES ('7', 'Steve', 'Jobs', '2');
INSERT INTO `person` (`Id`, `FirstName`, `LastName`, `IdPersonType`) VALUES ('8', 'Marc', 'Benioff', '1');
INSERT INTO `person` (`Id`, `FirstName`, `LastName`, `IdPersonType`) VALUES ('9', 'Ray', 'Ozzie', '2');
INSERT INTO `person` (`Id`, `FirstName`, `LastName`, `IdPersonType`) VALUES ('10', 'Nicholas', 'Negroponte', '2');
INSERT INTO `person` (`Id`, `FirstName`, `LastName`, `IdPersonType`) VALUES ('11', 'Diane', 'Green', '1');
INSERT INTO `person` (`Id`, `FirstName`, `LastName`, `IdPersonType`) VALUES ('12', 'Sam', 'Palmisano', '1');
INSERT INTO `person` (`Id`, `FirstName`, `LastName`, `IdPersonType`) VALUES ('13', 'Blake', 'Ross', '2');
INSERT INTO `person` (`Id`, `FirstName`, `LastName`, `IdPersonType`) VALUES ('14', 'Ralph', 'Szygenda', '2');
INSERT INTO `person` (`Id`, `FirstName`, `LastName`, `IdPersonType`) VALUES ('15', 'Rick', 'Dalzell', '2');
```

Con la base de datos establecida, la parte correspondiente a la capa de acceso de datos hace referencia a la definición de las clases para trabajar con las entidades de la base de datos y al contexto para establecer la comunicación entre ASP.NET Core y la base de datos, que, en este caso, MySQL es la que se esta utilizando. 

Para este propósito, es necesario instalar tres paquetes NuGet:

+ `Microsoft.EntityFrameworkCore.Design`
+ `Microsoft.EntityFrameworkCore.Tools`
+ `MySql.Data.EntityFrameworkCore`

Posteriormente, es necesario utilizar la consola de administración de paquetes para realizar scaffolding desde la base de datos (generar automáticamente el contexto y las clases de las entidades) a través del siguiente comando: 

```
Scaffold-DbContext "server=servername;port=portnumber;user=username;password=pass;database=databasename" MySql.Data.EntityFrameworkCore -OutputDir Entities -f
```

Con esta primera parte, la conexión a la base de datos esta lista. Lo que prosigue a continuación es la definición de modelos con los cuales se trabajará en la página web. Estos modelos son:

```csharp
public class PersonModel
{
    public int Id { get; set; }
    public string FirstName { get; set; }
    public string LastName { get; set; }
    public int IdPersonType { get; set; }
    public string NamePersonType { get; set; }
}
```

```csharp
public class PersonTypeModel
{
    public int Id { get; set; }
    public string Name { get; set; }
    public string Description { get; set; }
}
```

Para cada uno de estos modelos se cuenta con un servicio, los cuales tienen las siguientes operaciones: 

*PersonService*:

+ `GetAllPersonsAsync()`
+ `GetPersonByIdAsync(int personId)`
+ `GetPersonByIdAndTypeAsync(int personId, int personTypeId)`
+ `GetAllPersonsByTypeAsync(int personTypeId)`

*PersonTypeService*:

+ `GetAllPersonTypesAsync()`
+ `GetPersonTypeByIdAsync(int personTypeId)`

En Visual Studio 2019 tendremos algo como esto: 

![](https://dev-to-uploads.s3.amazonaws.com/i/nmobwig7nrjcfg4etc10.png)

**Vistamodelo**

```csharp
public class DefaultViewModel : MasterPageViewModel
{
    private readonly PersonService personService;
    public GridViewDataSet<PersonModel> Persons { get; set; } = new GridViewDataSet<PersonModel>();
    public List<int> PersonTypes { get; set; } = new List<int>();
    public string IdSearch { get; set; }
    public bool SearchByTextVisible { get; set; } = false;

    public DefaultViewModel(PersonService personService)
    {
        this.personService = personService;
    }

    public override async Task PreRender() {}

    public async Task UpdatePersonList()
    {
        IdSearch = null;

        if (PersonTypes.Count == 2)
        {
            Persons.Items = await personService.GetAllPersonsAsync();
            SearchByTextVisible = true;
        }
        else if (PersonTypes.Count == 1)
        {
            int IdPersonType = PersonTypes.FirstOrDefault();
            Persons.Items = await personService.GetAllPersonsByTypeAsync(IdPersonType);
            SearchByTextVisible = true;
        }
        else
        {
            Persons.Items.Clear();
            SearchByTextVisible = false;
        }
    }

    public async Task SearchById()
    {
        if (PersonTypes.Count == 2)
        {
            if (!string.IsNullOrEmpty(IdSearch))
            {
                List<PersonModel> list = new List<PersonModel>(); ;
                list.Add(await personService.GetPersonByIdAsync(Int32.Parse(IdSearch)));
                Persons.Items = list;
            }
            else {
                Persons.Items = await personService.GetAllPersonsAsync();
            }
        }
        else if (PersonTypes.Count == 1)
        {
            if (!string.IsNullOrEmpty(IdSearch))
            {
                int IdPersonType = PersonTypes.FirstOrDefault();
                List<PersonModel> list = new List<PersonModel>(); ;
                list.Add(await personService.GetPersonByIdAndTypeAsync(Int32.Parse(IdSearch), IdPersonType));
                Persons.Items = list;
            }
            else
            {
                int IdPersonType = PersonTypes.FirstOrDefault();
                Persons.Items = await personService.GetAllPersonsByTypeAsync(IdPersonType);
            }
        }
    }
}
```

**Vista**

```html
<dot:Content ContentPlaceHolderID="MainContent">
	<div class="page-center">
		<div class="page-grid-top">
			<div class="student-image"></div>
			<h1>Person Report</h1>
		</div>

		<p>
			<h4>Search by type:</h4>
			<p />
			<dot:CheckBox CheckedItems="{value: PersonTypes}"
					  Changed="{command: UpdatePersonList()}"
					  CheckedValue="{value: 1}" Text="Type A" />
			<br />
			<dot:CheckBox CheckedItems="{value: PersonTypes}"
					  Changed="{command: UpdatePersonList()}"
					  CheckedValue="{value: 2}" Text="Type B" />
		</p>

		<p >
			<h4>Search by text:</h4>
			<p />
			ID Number:
			<dot:TextBox Text="{value: IdSearch}" Type="Number" class="page-input" Visible="{value: SearchByTextVisible}" />
			<dot:Button Text="Search" Click="{command: SearchById()}" class="page-button" Visible="{value: SearchByTextVisible}" />
		<p />

		<h4>Report:</h4>

		<dot:GridView DataSource="{value: Persons}" class="page-grid">
			<Columns>
				<dot:GridViewTextColumn ValueBinding="{value: Id}" HeaderText="Id" />
				<dot:GridViewTextColumn ValueBinding="{value: FirstName}" HeaderText="Firstname" />
				<dot:GridViewTextColumn ValueBinding="{value: LastName}" HeaderText="LastName" />
				<dot:GridViewTextColumn ValueBinding="{value: NamePersonType}" HeaderText="Type" />
			</Columns>
			<EmptyDataTemplate>
				There are no search results.
			</EmptyDataTemplate>
		</dot:GridView>

	</div>
</dot:Content>
```

####Análisis de la aplicación 

En el siguiente GIF se puede visualizar de manera general la interacción con esta pequeña página web. 

El primer elemento que analizaremos es el `GridView`, un control de DotVVM que nos permite tablas para representar datos en específico. Este componente nos permite especificar la fuente de datos a través de la propiedad `DataSource`, en este caso, la fuente de datos se define de la siguiente manera: 

```html
public GridViewDataSet<PersonModel> Persons { get; set; } = new GridViewDataSet<PersonModel>();
```

La fuente de datos, adicional a un listado de tipo `GridViewDataSet`, tambien puede estar definido a través de otra colección de tipo `List`. Para la definición de las columnas se utiliza la etiqueta `GridViewTextColumn`. En este caso, podemos encontrar las columnas `Id`, `FirstName`, `LastName` y `Type`. Estos nombres vienen del tipo de dato de la fuente de datos, en este caso, del modelo `PersonModel`. 

```html
<dot:GridView DataSource="{value: Persons}" class="page-grid">
	<Columns>
		<dot:GridViewTextColumn ValueBinding="{value: Id}" HeaderText="Id" />
		<dot:GridViewTextColumn ValueBinding="{value: FirstName}" HeaderText="Firstname" />
		<dot:GridViewTextColumn ValueBinding="{value: LastName}" HeaderText="LastName" />
		<dot:GridViewTextColumn ValueBinding="{value: NamePersonType}" HeaderText="Type" />
	</Columns>
	<EmptyDataTemplate>
		There are no search results.
	</EmptyDataTemplate>
</dot:GridView>
```

Otra de las sub-etiquetas de GridView es `EmptyDataTemplate`. Esta etiqueta permite mostrar algún contenido HTML en caso de que el listado de elementos se encuentre vacío. A la final, con el GridView visualizaremos algo como esto: 

![](https://dev-to-uploads.s3.amazonaws.com/i/u13zu1rc9dacnlfnhg8s.png)

Más información sobre el componente `GridView` aquí: [https://www.dotvvm.com/docs/controls/builtin/GridView/2.0](https://www.dotvvm.com/docs/controls/builtin/GridView/2.0).

Ahora bien, a partir de esta tabla son varias las operaciones que se pueden realizar, por ejemplo, establecer componentes adicionales para crear criterios de búsqueda y actualizar esta tabla según la búsqueda. 

El primer caso es utilizando un `CheckBox` de DotVVM. Al igual que en HTML o cualquier otro entorno de diseño, el `CheckBox` tiene el rol como casilla de verificación para seleccionar elementos en un conjunto de opciones. Para este ejemplo, el objetivo es tener dos casillas de verificación, los cuales corresponden a los tipos de personas. Según la selección, ya sea del Tipo A, del Tipo B, o de los dos, la tabla de registros se actualizará de acuerdo con esta decisión. 

En la parte de la vista, nos encontramos con la propiedad `CheckedItems` que almacenara el valor de los elementos que se encuentren seleccionados. Asimismo, nos encontramos con la propiedad Changed, la cual permite especificar el método que realizara las acciones al momento que este elemento sea activado o desactivado. 

```html
<dot:CheckBox CheckedItems="{value: PersonTypes}"
			Changed="{command: UpdatePersonList()}"
			CheckedValue="{value: 1}" Text="Type A" />
<br />
<dot:CheckBox CheckedItems="{value: PersonTypes}"
			Changed="{command: UpdatePersonList()}"
			CheckedValue="{value: 2}" Text="Type B" />
```

En el método de actualización, por ejemplo, si seleccionados uno de los dos tipos, entonces realizaremos una consulta en la base de datos de acuerdo con el servicio definido: `PersonService`, para obtener el listado de personas según el id seleccionado. Con este listado recuperado, actualizaremos la base de datos al establecer nuevamente los ítems de la fuente de datos del `GridView`. 

```csharp
int IdPersonType = PersonTypes.FirstOrDefault();
Persons.Items = await personService.GetAllPersonsByTypeAsync(IdPersonType);
SearchByTextVisible = true;
```

Algo parecido sucederá si no seleccionamos ninguna de las casillas de verificación. En este caso solo limpiaremos los ítems del `GridView`.

```csharp
Persons.Items.Clear();
```

Como vimos en el GIF, el resultado de utilizar el control `CheckBox` es el siguiente: 

![](https://dev-to-uploads.s3.amazonaws.com/i/5axb2uz102qlgbom29k3.png)

Más información sobre el componente `CheckBox` aquí: [https://www.dotvvm.com/docs/controls/builtin/CheckBox/2.0](https://www.dotvvm.com/docs/controls/builtin/CheckBox/2.0).

Otro de los controles que nos permiten seguir agregando funcionalidades al `GridView` para establecer criterios de búsqueda a este reporte son los elementos `TextBox` y `Button`. En este caso, estos componentes pueden ser de utilizad para buscar algún elemento específico del reporte a través de una entrada de texto. Para ejemplificar, en esta aplicación, los controles sirven para encontrar a una persona especifica según su `Id`.

```html
<dot:TextBox Text="{value: IdSearch}" Type="Number" class="page-input" Visible="{value: SearchByTextVisible}" />

<dot:Button Text="Search" Click="{command: SearchById()}" class="page-button" Visible="{value: SearchByTextVisible}" />
``` 

La actualización de los elementos del `GridView` es similar al caso del `CheckBox`. El resultado es el siguiente: 

![Alt Text](https://dev-to-uploads.s3.amazonaws.com/i/pglq4x83cpt87782txse.png)

Toda la información sobre el control TextBox la podemos encontrar en: [https://www.dotvvm.com/docs/controls/builtin/TextBox/2.0](https://www.dotvvm.com/docs/controls/builtin/TextBox/2.0). Y la del control Button en: [https://www.dotvvm.com/docs/controls/builtin/Button/2.0](https://www.dotvvm.com/docs/controls/builtin/Button/2.0). 

####¿Qué sigue?

Con este artículo, hemos aprendido ciertas características de los componentes `GridView`, `CheckBox`, `TextBox` y `Button` para visualizar un listado de datos y establecer criterios de búsqueda a través del patrón de diseño Modelo, Vista, Vistamodelo en ASP.NET Core y DotVVM. 

El código fuente de esta implementación está disponible en este repositorio: [DotVVM Reports](https://github.com/esdanielgomez/DotVVMReports).

**Recursos adicionales**

Deseas seguir adquiriendo nuevos conocimientos sobre ASP.NET Core y DotVVM, estos recursos podrían ser de tu interés:

+ Curso gratuito y abierto en YouTube: [Fundamentos de ASP.NET Core y DotVVM](http://bit.ly/cursoaspnetdotvvm). 

+ [Desarrollo de aplicaciones web con ASP.NET Core, DotVVM y MongoDB](https://dev.to/dotvvm/desarrollo-de-aplicaciones-web-con-asp-net-core-dotvvm-y-mongodb-59pp). 

+ [DotVVM y ASP.NET Core: Implementación de operaciones CRUD](https://dev.to/dotvvm/dotvvm-y-asp-net-core-implementacion-de-operaciones-crud-43oe).

**Gracias**:

Si tienes alguna inquietud o necesitas ayuda en algo particular, será un gusto poder ayudar. 

Nos vemos en [Twitter](https://twitter.com/esDanielGomez)!! :)

¡Hasta pronto!