{"id":51405232,"url":"https://github.com/josejuansanchez/blazorbootstrap","last_synced_at":"2026-07-04T10:31:08.352Z","repository":{"id":322473235,"uuid":"1089621395","full_name":"josejuansanchez/blazorbootstrap","owner":"josejuansanchez","description":"Repositorio con ejemplos sobre cómo usar componentes BlazorBootstrap.","archived":false,"fork":false,"pushed_at":"2025-11-04T17:07:15.000Z","size":8,"stargazers_count":0,"open_issues_count":0,"forks_count":0,"subscribers_count":0,"default_branch":"main","last_synced_at":"2025-11-04T18:09:11.107Z","etag":null,"topics":["blazor","blazor-server","blazorbootstrap"],"latest_commit_sha":null,"homepage":"","language":null,"has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":null,"status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/josejuansanchez.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":null,"code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null,"notice":null,"maintainers":null,"copyright":null,"agents":null,"dco":null,"cla":null}},"created_at":"2025-11-04T15:34:18.000Z","updated_at":"2025-11-04T17:07:20.000Z","dependencies_parsed_at":null,"dependency_job_id":null,"html_url":"https://github.com/josejuansanchez/blazorbootstrap","commit_stats":null,"previous_names":["josejuansanchez/blazorbootstrap"],"tags_count":null,"template":false,"template_full_name":null,"purl":"pkg:github/josejuansanchez/blazorbootstrap","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/josejuansanchez%2Fblazorbootstrap","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/josejuansanchez%2Fblazorbootstrap/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/josejuansanchez%2Fblazorbootstrap/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/josejuansanchez%2Fblazorbootstrap/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/josejuansanchez","download_url":"https://codeload.github.com/josejuansanchez/blazorbootstrap/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/josejuansanchez%2Fblazorbootstrap/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":35118970,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-05-26T15:22:16.424Z","status":"online","status_checked_at":"2026-07-04T02:00:05.987Z","response_time":113,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["blazor","blazor-server","blazorbootstrap"],"created_at":"2026-07-04T10:31:07.745Z","updated_at":"2026-07-04T10:31:08.347Z","avatar_url":"https://github.com/josejuansanchez.png","language":null,"funding_links":[],"categories":[],"sub_categories":[],"readme":"# Introducción a BlazorBootstrap\n\n## Índice\n\n- [Introducción](#introducción)\n- [Instalación de BlazorBootstrap](#instalación-de-blazorbootstrap)\n- **Ejemplos**\n  - [Ejemplo 1. Alertas y botones](#ejemplo-1-alertas-y-botones)\n  - [Ejemplo 2. Uso de modales](#ejemplo-2-uso-de-modales)\n  - [Ejemplo 3. Tarjetas responsivas con información de usuario](#ejemplo-3-tarjetas-responsivas-con-información-de-usuario)\n  - [Ejemplo 4. Formulario con validación](#ejemplo-4-formulario-con-validación)\n  - [Ejemplo 5. Listado de usuarios con Grid](#ejemplo-5-listado-de-usuarios-con-grid)\n- [Ejercicios propuestos](#ejercicios-propuestos)\n- [Referencias](#referencias)\n\n---\n\n## Introducción\n\nEn esta práctica aprenderemos a utilizar la biblioteca **[BlazorBootstrap][1]**, una librería de componentes creada específicamente para **Blazor Server y Blazor WebAssembly**, que permite aprovechar la potencia de **[Bootstrap 5][2]** sin necesidad de escribir código JavaScript o HTML adicional. Existen otras librerías similares, como por ejemplo **[Blazorise][3]**.\n\nEl objetivo de estos ejemplos es familiarizarse con los componentes visuales más comunes: **alertas, botones, modales, tarjetas, formularios y grids**, así como comprender su integración dentro del flujo interactivo de una aplicación Blazor.\n\n---\n\n## Instalación de BlazorBootstrap\n\nSiga los pasos que se detallan en la página oficial de [BlazorBootstrap](https://docs.blazorbootstrap.com/getting-started/blazor-webapp-server-global-net-8) para instalar la librería en su proyecto Blazor.\n\n---\n\n## Ejemplo 1. Alertas y botones\n\nEn este primer ejemplo se muestran las **alertas** (`\u003cAlert\u003e`) y **botones** (`\u003cButton\u003e`) básicos de [BlazorBootstrap][1], junto con la gestión de eventos desde código C#.\n\n**Paso 1. Crear un nuevo componente Razor**\n\nCree un nuevo archivo Razor llamado `Ejemplo1.razor` en la carpeta `Pages` de su proyecto Blazor.\n\n**Paso 2. Añadir el código del ejemplo**\n\nAñada el siguiente código al archivo `Ejemplo1.razor`:\n\n```razor\n@page \"/ejemplo1\"\n@rendermode InteractiveServer\n\n\u003ch3\u003eEjemplo 1. Alertas y Botones\u003c/h3\u003e\n\n\u003c!-- Alerta fija con botón de cerrar --\u003e\n\u003cAlert Color=\"AlertColor.Success\" Dismissable=\"true\"\u003e\n    Alerta sin icono que se puede cerrar\n\u003c/Alert\u003e\n\n\u003cAlert Color=\"AlertColor.Danger\" Dismissable=\"true\"\u003e\n    \u003cIcon Name=\"IconName.ExclamationTriangleFill\" class=\"me-2\"\u003e\u003c/Icon\u003e\n    Alerta con icono que se puede cerrar\n\u003c/Alert\u003e\n\n\u003c!-- Botón que muestra/oculta la alerta informativa --\u003e\n\u003cButton Color=\"ButtonColor.Primary\" Type=\"ButtonType.Button\" class=\"me-2\" @onclick=\"ToggleAlerta\"\u003e\n    Mostrar / Ocultar alerta informativa\n\u003c/Button\u003e\n\n@if (mostrarInfo)\n{\n    \u003cAlert Color=\"AlertColor.Info\" Dismissable=\"false\" class=\"mt-3\"\u003e\n        \u003cIcon Name=\"IconName.InfoCircleFill\" class=\"me-2\"\u003e\u003c/Icon\u003e\n        Esta alerta se muestra dinámicamente desde C#\n    \u003c/Alert\u003e\n}\n\n\u003cp class=\"mt-2\"\u003e\u003cstrong\u003emostrarInfo:\u003c/strong\u003e @mostrarInfo\u003c/p\u003e\n\n@code {\n    private bool mostrarInfo = true;\n\n    private void ToggleAlerta()\n    {\n        mostrarInfo = !mostrarInfo;\n        Console.WriteLine(\"Botón ToggleAlerta pulsado\");\n    }\n}\n```\n\n### Comentarios\n\n* `\u003cAlert\u003e`: Componente que muestra mensajes visuales de diferentes colores (`Success`, `Danger`, `Info`, etc.).\n* `Dismissable=\"true\"`: Es un atributo del componente `Alert` que añade un botón en la esquina superior derecha para cerrar la alerta.\n* `\u003cIcon\u003e`: Inserta un icono Bootstrap. Existen muchos tipos, por ejemplo: `ExclamationTriangleFill`, `InfoCircleFill`, etc.\n* `\u003cButton\u003e`: Componente que crea un botón configurable por color y tipo.\n\n**Paso 3. Modificar el archivo de navegación**\n\nAñade un enlace al nuevo componente en Components/Layout/NavMenu.razor:\n\n```razor\n\u003cdiv class=\"nav-item px-3\"\u003e\n    \u003cNavLink class=\"nav-link\" href=\"ejemplo1\"\u003e\n        \u003cspan class=\"bi bi-list-nested-nav-menu\" aria-hidden=\"true\"\u003e\u003c/span\u003e Ejemplo 1\n    \u003c/NavLink\u003e\n\u003c/div\u003e          \n```\n\n---\n\n## Ejemplo 2. Uso de modales\n\nEste ejemplo muestra cómo utilizar el componente `\u003cModal\u003e` de [BlazorBootstrap][1], que permite abrir ventanas emergentes con contenido y acciones personalizadas.\n\n**Paso 1. Crear un nuevo componente Razor**\n\nCree un nuevo archivo Razor llamado `Ejemplo2.razor` en la carpeta `Pages` de su proyecto Blazor.\n\n**Paso 2. Añadir el código del ejemplo**\n\n```razor\n@page \"/ejemplo2\"\n@rendermode InteractiveServer\n\n\u003ch3\u003eEjemplo 2. Modal con Blazor Bootstrap\u003c/h3\u003e\n\n\u003cModal @ref=\"modal\" Title=\"Título del Modal\"\u003e\n    \u003cBodyTemplate\u003e\n        El contenido del modal va aquí.\n    \u003c/BodyTemplate\u003e\n    \u003cFooterTemplate\u003e\n        \u003cButton Color=\"ButtonColor.Secondary\" @onclick=\"OnHideModalClick\"\u003eCerrar\u003c/Button\u003e\n        \u003cButton Color=\"ButtonColor.Primary\"\u003eGuardar cambios\u003c/Button\u003e\n    \u003c/FooterTemplate\u003e\n\u003c/Modal\u003e\n\n\u003cButton Color=\"ButtonColor.Primary\" @onclick=\"OnShowModalClick\"\u003eMostrar Modal\u003c/Button\u003e\n\n@code {\n    private Modal modal = default!;\n\n    private async Task OnShowModalClick() =\u003e await modal.ShowAsync();\n    private async Task OnHideModalClick() =\u003e await modal.HideAsync();\n}\n```\n\n### Comentarios\n\n* `\u003cModal\u003e`: Componente que define la ventana modal.\n\n  * `@ref=\"modal\"`: Referencia al componente para manipularlo desde C#.\n* `\u003cBodyTemplate\u003e`: Contenido principal de la ventana modal.\n* `\u003cFooterTemplate\u003e`: Parte inferior del modal, normalmente con botones de acción.\n* `modal.ShowAsync()` / `modal.HideAsync()`: Métodos que muestran u ocultan la ventana modal.\n\n**Paso 3. Modificar el archivo de navegación**\n\nAñade un enlace al nuevo componente en `Components/Layout/NavMenu.razor`:\n\n```razor\n\u003cdiv class=\"nav-item px-3\"\u003e\n    \u003cNavLink class=\"nav-link\" href=\"ejemplo2\"\u003e\n        \u003cspan class=\"bi bi-list-nested-nav-menu\" aria-hidden=\"true\"\u003e\u003c/span\u003e Ejemplo 2\n    \u003c/NavLink\u003e\n\u003c/div\u003e\n```\n\n---\n\n## Ejemplo 3. Tarjetas responsivas con información de usuario\n\nEste ejemplo utiliza el componente `\u003cCard\u003e` para mostrar información de varios usuarios en un diseño responsive.\n\n**Paso 1. Crear un nuevo componente Razor**\n\nCree un nuevo archivo Razor llamado `Ejemplo3.razor` en la carpeta `Pages` de su proyecto Blazor.\n\n**Paso 2. Añadir el código del ejemplo**\n\n```razor\n@page \"/ejemplo3\"\n@rendermode InteractiveServer\n\n\u003ch3\u003eEjemplo 3. Tarjetas responsivas con información de usuario\u003c/h3\u003e\n\n\u003cdiv class=\"row g-3\"\u003e\n    @foreach (var usuario in usuarios)\n    {\n        \u003cdiv class=\"col-12 col-md-6 col-lg-4\"\u003e\n            \u003cCard Class=\"h-100 shadow-sm\"\u003e\n                \u003cCardHeader\u003e\n                    \u003cstrong\u003e@usuario.Nombre\u003c/strong\u003e\n                \u003c/CardHeader\u003e\n\n                \u003cCardBody\u003e\n                    \u003cimg src=\"@usuario.Imagen\"\n                         class=\"img-fluid rounded-circle mb-3\"\n                         alt=\"@usuario.Nombre\" width=\"120\" /\u003e\n                    \u003cp class=\"fw-semibold mb-1\"\u003e@usuario.Rol\u003c/p\u003e\n\n                    \u003cdiv class=\"border-top pt-2 mt-2 small\"\u003e\n                        \u003cp class=\"mb-1\"\u003e\u003cstrong\u003eCorreo:\u003c/strong\u003e @usuario.Email\u003c/p\u003e\n                        \u003cp class=\"mb-1\"\u003e\u003cstrong\u003eTeléfono:\u003c/strong\u003e @usuario.Telefono\u003c/p\u003e\n                        \u003cp class=\"mb-0\"\u003e\u003cstrong\u003eDescripción:\u003c/strong\u003e @usuario.Descripcion\u003c/p\u003e\n                    \u003c/div\u003e\n                \u003c/CardBody\u003e\n            \u003c/Card\u003e\n        \u003c/div\u003e\n    }\n\u003c/div\u003e\n\n@code {\n    private readonly List\u003cUsuario\u003e usuarios = new()\n    {\n        new Usuario\n        {\n            Nombre = \"Usuario 1\",\n            Rol = \"Administrador del sistema\",\n            Email = \"admin@empresa.com\",\n            Telefono = \"950 252525\",\n            Imagen = \"https://randomuser.me/api/portraits/men/40.jpg\",\n            Descripcion = \"Responsable de la configuración general del sistema y seguridad.\"\n        },\n        new Usuario\n        {\n            Nombre = \"Usuario 2\",\n            Rol = \"Gestor de contenido\",\n            Email = \"content@empresa.com\",\n            Telefono = \"950 242424\",\n            Imagen = \"https://randomuser.me/api/portraits/men/41.jpg\",\n            Descripcion = \"Encargado de la creación y actualización de contenidos en el portal.\"\n        },\n        new Usuario\n        {\n            Nombre = \"Usuario 3\",\n            Rol = \"Soporte técnico\",\n            Email = \"soporte@empresa.com\",\n            Telefono = \"950 232323\",\n            Imagen = \"https://randomuser.me/api/portraits/men/42.jpg\",\n            Descripcion = \"Atiende incidencias y da soporte a usuarios internos y externos.\"\n        }\n    };\n\n    private class Usuario\n    {\n        public string Nombre { get; set; } = string.Empty;\n        public string Rol { get; set; } = string.Empty;\n        public string Email { get; set; } = string.Empty;\n        public string Telefono { get; set; } = string.Empty;\n        public string Imagen { get; set; } = string.Empty;\n        public string Descripcion { get; set; } = string.Empty;\n    }\n}\n```\n\n### Comentarios\n\n* `\u003cCard\u003e`: Contenedor visual con borde y sombra (`shadow-sm`).\n* `\u003cCardHeader\u003e`: Parte superior de la tarjeta, usada para el nombre del usuario.\n* `\u003cCardBody\u003e`: Contenido principal de la tarjeta con imagen, texto y datos secundarios.\n* Las clases Bootstrap (`col-12`, `col-md-6`, `col-lg-4`) hacen que la cuadrícula sea **responsive**.\n\n| Clase | Breakpoint | Significado | Resultado visual |\n| :--- | :--- |:--- | :--- |\n| `col-12`   | **Extra pequeño (XS)**: pantallas menores de 576 px (móviles) | La columna ocupa **todo el ancho disponible (12 columnas)**      | Una sola tarjeta por fila |\n| `col-md-6` | **Mediano (MD)**: \u003e= 768 px (tablets)                          | La columna ocupa **6 de 12 columnas** (la mitad del contenedor)  | Dos tarjetas por fila     |\n| `col-lg-4` | **Grande (LG)**: \u003e= 992 px (ordenadores de escritorio)         | La columna ocupa **4 de 12 columnas** (un tercio del contenedor) | Tres tarjetas por fila    |\n\n\n**Paso 3. Modificar el archivo de navegación**\n\nAñade un enlace al nuevo componente en `Components/Layout/NavMenu.razor`:\n\n```razor\n\u003cdiv class=\"nav-item px-3\"\u003e\n    \u003cNavLink class=\"nav-link\" href=\"ejemplo3\"\u003e\n        \u003cspan class=\"bi bi-list-nested-nav-menu\" aria-hidden=\"true\"\u003e\u003c/span\u003e Ejemplo 3\n    \u003c/NavLink\u003e\n\u003c/div\u003e     \n``` \n\n---\n\n## Ejemplo 4. Formulario con validación\n\nEn este ejemplo se implementa un formulario controlado con **validaciones automáticas** mediante `DataAnnotations` y componentes para la entrada de datos de BlazorBootstrap.\n\n**Paso 1. Crear un nuevo componente Razor**\n\nCree un nuevo archivo Razor llamado `Ejemplo4.razor` en la carpeta `Pages` de su proyecto Blazor.\n\n**Paso 2. Añadir el código del ejemplo**\n\n```razor\n@page \"/ejemplo4\"\n@using System.ComponentModel.DataAnnotations\n@rendermode InteractiveServer\n\n\u003ch3\u003eEjemplo 4. Formulario con validación\u003c/h3\u003e\n\n\u003cEditForm Model=\"@usuario\" OnValidSubmit=\"Guardar\"\u003e\n    \u003cDataAnnotationsValidator /\u003e\n    \u003cValidationSummary /\u003e\n\n    \u003cdiv class=\"mb-3\"\u003e\n        \u003clabel for=\"nombre\" class=\"form-label\"\u003eNombre\u003c/label\u003e\n        \u003cInputText id=\"nombre\" class=\"form-control\" @bind-Value=\"usuario.Nombre\" /\u003e\n        \u003cValidationMessage For=\"@(() =\u003e usuario.Nombre)\" /\u003e\n    \u003c/div\u003e\n\n    \u003cdiv class=\"mb-3\"\u003e\n        \u003clabel for=\"email\" class=\"form-label\"\u003eCorreo electrónico\u003c/label\u003e\n        \u003cInputText id=\"email\" class=\"form-control\" @bind-Value=\"usuario.Email\" /\u003e\n        \u003cValidationMessage For=\"@(() =\u003e usuario.Email)\" /\u003e\n    \u003c/div\u003e\n\n    \u003cdiv class=\"mb-3\"\u003e\n        \u003clabel for=\"fechaNacimiento\" class=\"form-label\"\u003eFecha de nacimiento:\u003c/label\u003e\n        \u003cDateInput TValue=\"DateOnly?\" id=\"fechaNacimiento\" class=\"form-control\" @bind-Value=\"@usuario.FechaNacimiento\" Placeholder=\"Introduce una fecha\" /\u003e\n        \u003cValidationMessage For=\"@(() =\u003e usuario.FechaNacimiento)\" /\u003e\n    \u003c/div\u003e\n\n    \u003cButton Color=\"ButtonColor.Success\" Type=\"ButtonType.Submit\"\u003eGuardar\u003c/Button\u003e\n\u003c/EditForm\u003e\n\n@if (mostrarAlerta)\n{\n    \u003cAlert Color=\"AlertColor.Success\" Dismissable=\"true\" class=\"mt-3\"\u003e\n        \u003cIcon Name=\"IconName.CheckCircleFill\" class=\"me-2\"\u003e\u003c/Icon\u003e\n        Usuario guardado correctamente\n    \u003c/Alert\u003e\n}\n\n@code {\n    private Usuario usuario = new();\n    private bool mostrarAlerta = false;\n\n    public class Usuario\n    {\n        [Required(ErrorMessage = \"El nombre es obligatorio\")]\n        public string Nombre { get; set; } = string.Empty;\n\n        [Required(ErrorMessage = \"Formato de correo no válido\")]\n        public string Email { get; set; } = string.Empty;\n\n        [Required(ErrorMessage = \"La fecha de nacimiento es obligatoria\")]\n        public DateOnly? FechaNacimiento { get; set; } = DateOnly.FromDateTime(DateTime.Now);\n    }\n\n    private async Task Guardar()\n    {\n        mostrarAlerta = true;\n    }\n}\n```\n\n### Comentarios\n\n* `\u003cEditForm\u003e`: Controla el formulario y valida el modelo.\n* `\u003cDataAnnotationsValidator\u003e`: Activa la validación basada en atributos.\n* `\u003cValidationMessage\u003e`: Muestra los mensajes de error específicos.\n* `\u003cDateInput\u003e`: Componente de BlazorBootstrap para fechas (`DateOnly`).\n* `\u003cAlert\u003e`: Muestra un mensaje de éxito tras guardar.\n\n\n**Paso 3. Modificar el archivo de navegación**\n\nAñade un enlace al nuevo componente en `Components/Layout/NavMenu.razor`:\n\n```razor\n\u003cdiv class=\"nav-item px-3\"\u003e\n    \u003cNavLink class=\"nav-link\" href=\"ejemplo4\"\u003e\n        \u003cspan class=\"bi bi-list-nested-nav-menu\" aria-hidden=\"true\"\u003e\u003c/span\u003e Ejemplo 4\n    \u003c/NavLink\u003e\n\u003c/div\u003e\n```\n\n---\n\n## Ejemplo 5. Listado de usuarios con Grid\n\nEste último ejemplo utiliza el componente `\u003cGrid\u003e` para mostrar, filtrar, ordenar y paginar registros dinámicamente.\n\n**Paso 1. Crear un nuevo componente Razor**\n\nCree un nuevo archivo Razor llamado `Ejemplo5.razor` en la carpeta `Pages` de su proyecto Blazor.\n\n**Paso 2. Añadir el código del ejemplo**\n\n```razor\n@page \"/ejemplo5\"\n@using System.Globalization\n@rendermode InteractiveServer\n\n\u003ch3\u003eEjemplo 5. Listado de usuarios\u003c/h3\u003e\n\n\u003cGrid TItem=\"Usuario\"\n      AllowFiltering=\"true\"\n      AllowPaging=\"true\"\n      AllowSorting=\"true\"\n      AllowSelection=\"true\"\n      Class=\"table table-hover table-bordered table-striped\"\n      DataProvider=\"UsuariosDataProvider\"\n      PageSize=\"5\"\n      Responsive=\"true\"\n      RowKeySelector=\"u =\u003e u.Id\"\n      SelectedItemsChanged=\"OnSelectedItemsChanged\"\n      SelectionMode=\"GridSelectionMode.Multiple\"\u003e\n\n    \u003cGridColumns\u003e\n        \u003cGridColumn TItem=\"Usuario\" HeaderText=\"ID\" PropertyName=\"Id\" SortKeySelector=\"u =\u003e u.Id\"\u003e\n            @context.Id\n        \u003c/GridColumn\u003e\n\n        \u003cGridColumn TItem=\"Usuario\" HeaderText=\"Nombre\" PropertyName=\"Nombre\" SortKeySelector=\"u =\u003e u.Nombre\"\u003e\n            @context.Nombre\n        \u003c/GridColumn\u003e\n\n        \u003cGridColumn TItem=\"Usuario\" HeaderText=\"Correo electrónico\" PropertyName=\"Email\" SortKeySelector=\"u =\u003e u.Email\"\u003e\n            @context.Email\n        \u003c/GridColumn\u003e\n\n        \u003cGridColumn TItem=\"Usuario\" HeaderText=\"Fecha de nacimiento\" PropertyName=\"FechaNacimiento\" SortKeySelector=\"u =\u003e u.FechaNacimiento\"\u003e\n            @context.FechaNacimiento.ToString(\"dd/MM/yyyy\", CultureInfo.InvariantCulture)\n        \u003c/GridColumn\u003e\n    \u003c/GridColumns\u003e\n\n\u003c/Grid\u003e\n\n\u003cdiv class=\"mt-3\"\u003e\n    \u003cstrong\u003eUsuarios seleccionados:\u003c/strong\u003e @selectedUsuarios.Count\n\u003c/div\u003e\n\n@if (selectedUsuarios.Any())\n{\n    \u003cul\u003e\n        @foreach (var user in selectedUsuarios)\n        {\n            \u003cli\u003e@user.Nombre (@user.Email)\u003c/li\u003e\n        }\n    \u003c/ul\u003e\n}\n\n@code {\n    private IEnumerable\u003cUsuario\u003e usuarios = default!;\n    private HashSet\u003cUsuario\u003e selectedUsuarios = new();\n\n    private async Task\u003cGridDataProviderResult\u003cUsuario\u003e\u003e UsuariosDataProvider(GridDataProviderRequest\u003cUsuario\u003e request)\n    {\n        if (usuarios is null)\n            usuarios = GetUsuarios(); // simula la obtención desde una base de datos o API\n\n        return await Task.FromResult(request.ApplyTo(usuarios));\n    }\n\n    private IEnumerable\u003cUsuario\u003e GetUsuarios()\n    {\n        return new List\u003cUsuario\u003e\n        {\n            new Usuario { Id = 1, Nombre = \"Ana Torres\", Email = \"ana.torres@example.com\", FechaNacimiento = new DateOnly(1990, 3, 14) },\n            new Usuario { Id = 2, Nombre = \"Luis García\", Email = \"luis.garcia@example.com\", FechaNacimiento = new DateOnly(1988, 7, 9) },\n            new Usuario { Id = 3, Nombre = \"Marta López\", Email = \"marta.lopez@example.com\", FechaNacimiento = new DateOnly(1995, 12, 25) },\n            new Usuario { Id = 4, Nombre = \"Carlos Pérez\", Email = \"carlos.perez@example.com\", FechaNacimiento = new DateOnly(1982, 5, 2) },\n            new Usuario { Id = 5, Nombre = \"Laura Sánchez\", Email = \"laura.sanchez@example.com\", FechaNacimiento = new DateOnly(1999, 10, 21) },\n            new Usuario { Id = 6, Nombre = \"Javier Ruiz\", Email = \"javier.ruiz@example.com\", FechaNacimiento = new DateOnly(1985, 1, 18) },\n            new Usuario { Id = 7, Nombre = \"Patricia Gómez\", Email = \"patricia.gomez@example.com\", FechaNacimiento = new DateOnly(1992, 11, 30) },\n        };\n    }\n\n    private Task OnSelectedItemsChanged(HashSet\u003cUsuario\u003e items)\n    {\n        selectedUsuarios = items is not null \u0026\u0026 items.Any() ? items : new();\n        return Task.CompletedTask;\n    }\n\n    public class Usuario\n    {\n        public int Id { get; set; }\n        public string Nombre { get; set; } = string.Empty;\n        public string Email { get; set; } = string.Empty;\n        public DateOnly FechaNacimiento { get; set; }\n    }\n}\n```\n\n### Comentarios\n\n* `\u003cGrid\u003e`: Tabla avanzada con filtrado, paginación y ordenación integradas.\n* `DataProvider`: Función que proporciona los datos al grid.\n* `RowKeySelector`: Identifica cada fila por su clave primaria.\n* `SelectionMode`: Permite selección múltiple o simple.\n* `SelectedItemsChanged`: Evento que se dispara al seleccionar filas.\n\n**Paso 3. Modificar el archivo de navegación**\n\nAñade un enlace al nuevo componente en `Components/Layout/NavMenu.razor`:\n\n```razor\n\u003cdiv class=\"nav-item px-3\"\u003e\n    \u003cNavLink class=\"nav-link\" href=\"ejemplo5\"\u003e\n        \u003cspan class=\"bi bi-list-nested-nav-menu\" aria-hidden=\"true\"\u003e\u003c/span\u003e Ejemplo 5\n    \u003c/NavLink\u003e\n\u003c/div\u003e\n```\n\n---\n\n## Ejercicios propuestos\n\n### Ejercicio 1. Añadir iconos a las tarjetas de usuario\n\nModifica el componente del **Ejemplo 3** para añadir un icono representativo según el rol del usuario. También puedes cambiar el color del borde de la tarjeta según el rol, por ejemplo, azul para administradores, verde para usuarios normales, etc.\n\n### Ejercicio 2. Validación adicional en el formulario\n\nAmplía el **Ejemplo 4** para incluir nuevos campos como teléfono, dirección, rol, foto, etc. Debe validar estos campos utilizando atributos de validación adicionales.\n\n### Ejercicio 3. Añadir eliminación en el Grid\n\nModifica el **Ejemplo 5** para incluir una columna con un botón que permita eliminar usuarios del listado.\n\n### Ejercicio 4. Integración de modal de confirmación\n\nCombina el **Ejemplo 2** (Modal) con el **Ejemplo 5** (Grid) para confirmar la eliminación de un usuario seleccionado.\n\n---\n\n## Referencias\n\n* [Documentación oficial de BlazorBootstrap](https://docs.blazorbootstrap.com/).\n* [Microsoft Docs: Componentes de Blazor Server](https://learn.microsoft.com/es-es/aspnet/core/blazor/components./?view=aspnetcore-9.0)\n* [Documentación oficial de Bootstrap 5](https://getbootstrap.com/docs/5.3/getting-started/introduction/).\n* [Repositorio en GitHub de BlazorBootstrap](https://github.com/vikramlearning/blazorbootstrap).\n* [Repositorio en GitHub con ejemplos para las clases de DWM](https://github.com/jaruiz1961-ual/EjemplosClaseDWM).\n* [Tutorial de Bootstrap 5 en W3Schools](https://www.w3schools.com/bootstrap5/index.php).\n\n[1]: https://demos.blazorbootstrap.com\n[2]: https://getbootstrap.com\n[3]: https://blazorise.com","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fjosejuansanchez%2Fblazorbootstrap","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fjosejuansanchez%2Fblazorbootstrap","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fjosejuansanchez%2Fblazorbootstrap/lists"}