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:
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:
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.
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:
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
.
Las sentencias SQL para la creación de estas tablas, sus atributos y la inserción de algunos registros son las siguientes:
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:
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; }
}
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:
Vistamodelo
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
<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:
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
.
<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:
Más información sobre el componente GridView
aquí: 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.
<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
.
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
.
Persons.Items.Clear();
Como vimos en el GIF, el resultado de utilizar el control CheckBox
es el siguiente:
Más información sobre el componente CheckBox
aquí: 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
.
<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:
Toda la información sobre el control TextBox la podemos encontrar en: 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.
¿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.
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.
Desarrollo de aplicaciones web con ASP.NET Core, DotVVM y MongoDB.
Gracias:
Si tienes alguna inquietud o necesitas ayuda en algo particular, será un gusto poder ayudar.
Nos vemos en Twitter!! :)
¡Hasta pronto!
Top comments (0)