Neste post vamos iniciar uma série de tutoriais para mostrar como criar uma aplicação Blazor Server e realizar o CRUD usando o EF Core e outros recursos no ambiente do .NET 6 e Net 7. |
Vamos criar uma aplicação Blazor Server e gerenciar
informações sobre tarefas do dia a dia que desejamos controlar e realizar a
manutenção dessas informações fazendo um CRUD.
O objetivo é recordar como usar os seguintes recursos com o Blazor no .NET 6/7:
Como pré-requisitos temos
Assim, vamos ao trabalho...
Criando o projeto Blazor Server
No VS 2022 vamos criar um projeto Blazor usando o template abaixo com o nome BlazorTarefas :
Após criar o projeto
vamos remover os componentes e arquivos que não vamos usar.
Vamos incluir no
projeto os seguintes pacotes Nuget:
Para incluir você pode usar o menu Tools -> ...-> Manage Nuget Package for Solutions usando a guia Browse.
Vamos criar no projeto a pasta Data e criar a classe Tarefa que é o nosso domínio :
public class Tarefa { [Key] public int Id { get; set; }
[Required] [Required] [Required] |
Vamos criar na pasta Data o arquivo de contexto ApplicationDbContext que faz o mapeamento ORM entre a entidade Tarefa e a tabela Tarefas no SQL Server:
using Microsoft.EntityFrameworkCore; namespace BlazorTarefas.Data; public class ApplicationDbContext : DbContext public DbSet<Tarefa>? Tarefas { get; set; } public override int SaveChanges() |
No arquivo appsettings.json defina a string de conexão com a instância do seu SQL Server local :
{ "ConnectionStrings": { "DefaultConnection": "Data Source=.;Initial Catalog=BlazorTarefasDB;Integrated Security=True" }, ... |
Definimos o nome do banco de dados como BlazorTarefasDB usando a autenticação do Windows.
Não podemos esquecer de registrar o contexto no container DI na classe Program:
... // Add services to the container. var connectionString = builder.Configuration.GetConnectionString("DefaultConnection");
builder.Services.AddDbContext<ApplicationDbContext>(options => |
Agora para aplicar o Migrations e gerar o banco e as tabelas podemos usar os comandos:
Criando o serviço para acessar os dados
Para simplificar o projeto não irei criar um repositório e um serviço contendo a lógica da aplicação que consome este repositório.
Vou criar o serviço para acessar os dados que vai atuar como um repositório. (Em uma aplicação mais complexa não é aconselhável fazer isso)
Assim, crie no projeto a pasta Services e nesta pasta crie a interface ITarefaService e sua implementação na classe concreta TarefaService :
public interface ITarefaService { Task<List<Tarefa>> Get(); Task<Tarefa> Get(int id); Task<Tarefa> Add(Tarefa tarefa); Task<Tarefa> Update(Tarefa tarefa); Task<Tarefa> Delete(int id); } |
Classe TarefaService :
using BlazorTarefas.Data; using Microsoft.EntityFrameworkCore; namespace BlazorTarefas.Services; public class TarefaService : ITarefaService public TarefaService(ApplicationDbContext context) public async Task<Tarefa> Add(Tarefa tarefa) public async Task<Tarefa> Delete(int id) public async Task<List<Tarefa>> Get() public async Task<Tarefa> Get(int id) public async Task<Tarefa> Update(Tarefa tarefa) |
Agora vamos registrar este serviço no container DI na classe Program:
... builder.Services.AddTransient<ITarefaService, TarefaService>();
var app = builder.Build(); |
Pronto ! já podemos realizar operações com tarefas.
Blazor - Exibindo as tarefas
Agora podemos criar na pasta Pages o componente razor Tarefas.razor onde vamos definir o código para exibir as tarefas cadastradas e permitir a criação, edição e exclusão de tarefas.
@page "/tarefas"
@inject ITarefaService service <h3>Lista de Tarefas</h3> @if (tarefas == null) |
Este código basicamente define uma tabela com dois botões de comando e no método OnInitializedAsync invoca o método Get do Serviço obtendo uma lista de tarefas que vamos exibir na interface.
Antes de executar vamos ajustar o componente Index.razor para exibir uma imagem:
@page "/" <PageTitle>Index</PageTitle> <h2>Tarefas</h2>
|
E no componente NavMenu.razor vamos criar apenas duas opções no menu :
... <div class="nav-item px-3"> <NavLink class="nav-link" href="" Match="NavLinkMatch.All"> <span class="oi oi-home" aria-hidden="true"></span> Home </NavLink> </div> <div class="nav-item px-3"> <NavLink class="nav-link" href="tarefas"> <span class="oi oi-plus" aria-hidden="true"></span> Tarefas </NavLink> </div> ... |
Executando o projeto teremos:
As duas tarefas que estamos exibindo foram incluidas diretamente na tabela Tarefas no SQL Server.
Componentes Child
Antes de
prosseguirmos com as implementações das tarefas do CRUD, precisamos conhecer os
componentes filhos. Os aplicativos Blazor são baseados em componentes que são
blocos de construção reutilizáveis, e podem ser um controle individual ou um
bloco com vários controles. Essas classes de componentes são escritas em razor
markup.
Os componentes podem incluir outros componentes e podemos adicionar um
componente dentro de outros usando o nome do componente em uma sintaxe HTML.
Vamos usar este conceito para criar modais de bootstrap como componentes filhos
para as caixas de diálogo Adicionar/Editar e Confirmar.
Criando o componente TarefaDetalhes
Vamos criar o componente filho TarefaDetalhes onde vamos usar os recursos do EditForm do Blazor para definir um formulário onde o usuário vai poder interagir com a interface incluindo e alterando dados das tarefas.
Assim podemos definir um formulário em um aplicativo Blazor usando o componente "EditForm".
O mecanismo Blazor valida apenas o valor da propriedade do modelo de entrada definido no componente "EditForm", e para isso o Blazor fornece um componente DataAnnotationsValidator que informa ao mecanismo Blazor para validar um modelo usando Data Annotations.
O Blazor fornece dois componentes para exibir erros de validação de modelo na tela.
O componente Blazor EditForm fornece retorno de chamada de evento OnValidSubmit que é acionado quando um formulário é enviado com êxito sem nenhum erro de validação. O retorno de chamada do evento OnInvalidSubmit é acionado quando um formulário é enviado com um erro de validação.
O Blazor também fornece um conjunto de componentes de entrada integrados que recebem e validam a entrada do usuário. Esses componentes validam o conteúdo quando são alterados ou quando um formulário é enviado.
A seguir temos os componentes de entradas integrados ao EditForm :
Como o componente EditForm renderiza um elemento HTML <form> padrão, é realmente possível usar elementos de formulário HTML padrão, como <input> e <select>, em nossa marcação, mas, como no componente EditForm, é recomendado usar os vários Controles de entrada do Blazor, porque eles vêm com funcionalidades adicionais, como validação.
Assim vamos criar na pasta Pages o componente TarefaDetalhes.razor :
@inject ITarefaService service <div class="modal" tabindex="-1" role="dialog" id="tarefaModal"> <div class="modal-dialog" role="document"> <div class="modal-content"> <div class="modal-header"> <h5 class="modal-title">@Cabecalho</h5> <button type="button" class="close" data-dismiss="modal" aria-label="Close"> <span aria-hidden="true">×</span> </button> </div> <div class="modal-body"> <EditForm Model="@TarefaObj" OnValidSubmit="@HandleValidSubmit"> <div class="form-group mt-2 mb-2"> <label for="Nome">Nome</label> <input type="hidden" @bind-value="@TarefaObj.Id" /> <InputText id="name" class="form-control" @bind-Value="@TarefaObj.Nome" /> </div> <div class="form-group mb-2"> <label for="status">Status</label> <InputSelect id="Summary" class="form-control" @bind-Value="TarefaObj.Status"> <option value="">Selecione</option> @foreach (var status in TarefaStatus) { <option value="@status"> @status </option> } </InputSelect> </div> <div class="form-group mb-3 mt-3"> <label for="ConclusaoEm">Conclusão em : </label> <input type="date" id="addition" name="math" @bind-value="@TarefaObj.ConclusaoEm" /> </div> <button type="submit" class="btn btn-primary">Envia</button> <button type="button" class="btn btn-secondary" data-dismiss="modal">Cancela</button> </EditForm> </div> </div> </div> </div> @code { [Parameter] [Parameter] List<string> TarefaStatus = new List<string>() { "Nova", "Em andamento", "Concluída" }; private async void HandleValidSubmit() |
No código acima,
temos um formulário definido usando o componente EditForm
que tem um modelo
TarefaObj
que é passado do componente pai (Tarefas). As propriedades do modelo são
vinculadas aos controles de entrada usando o bind-value
e o método HandleValidSubmit é acionado quando o
formulário é enviado com sucesso.
A seguir vamos ajustar o código do componente
Tarefas.razor e declarar o componente filho
TarefasDetalhes dentro do componente Tarefas
e passando inicialmente um objeto Tarefa vazio. Vamos definir também um
cabeçalho para exibir na operação :
@page "/tarefas"
@inject ITarefaService service @if (tarefas == null) < div><input type="button" data-toggle="modal" data-target="#tarefaModal" class="btn btn-primary" value="Nova Tarefa" @onclick="(() => InitializeTarefaObject())" /> </div> <TarefaDetalhes TarefaObj=tarefaObject></TarefaDetalhes>
} protected override async Task OnInitializedAsync() |
Observe que incluímos a <div> onde temos um botão - Nova Tarefa - que ao ser clicado aciona o método InitializedTarefaObject que cria um novo objeto Tarefa vazio.
Note também que incluimos a tag <TarefaDetalhes ...> para exibir os detalhes do componente filho.
Executando o projeto novamente e agora clicando no botão Nova Tarefa teremos o resultado a seguir:
Muuito bom !!!
Na proxima parte do artigo vamos continuar realizando a validação da entrada do usuário, salvar os dados no banco de dados e atualizar os dados na página para exibir um novo registro. ...
"Porque os que são
segundo a carne inclinam-se para as coisas da carne; mas os que são segundo o
Espírito para as coisas do Espírito. Porque a inclinação da carne é morte; mas a
inclinação do Espírito é vida e paz."
Romanos 8:5,6
Referências: