Blazor  -  CRUD básico usando MudBlazor


 Hoje vamos criar um projeto Blazor e fazer o CRUD básico usando a biblioteca de componentes MudBlazor.

Existem muitos componentes prontos que podemos usar em nosso projetos e eu já apresentei a biblioteca Radzen Blazor e hoje vou mostrar como usar de forma prática a biblioteca MudBlazor.


Vamos criar um projeto Blazor Server e usar o EF Core Code-First para acessar e criar uma base de dados no SQL Server e fazer o CRUD básico em informações sobre Bolos usando a biblioteca MudBlazor.

Vamos precisar usar a ferramenta EF Core Tools para aplicar o Migrations e para verificar se ela esta instalada  podemos digitar o seguinte comando em um terminal: dotnet ef

Se a ferramenta não estiver instalada o comando para instalar é o seguinte:  
dotnet
tool install --global dotnet-ef

Com a ferramenta instalada podemos prosseguir.

Criando o projeto no VS 2022

Abra o VS 2022 e clique em New Project e a seguir selecione o template Blazor Server App e informe o nome BlazorBolos :

A seguir defina as configurações conforme abaixo e clique em Create:

Vamos limpar o projeto removendo os componentes Counter.razor, FetchData.razor, SurveyPrompt.razor e o conteúdo da pasta Data do projeto.

Temos que incluir no projeto os seguintes pacotes usando o comando : install-package <nome>

  1. Microsoft.EntityFrameworkCore.SQLServer
  2. Microsoft.EntityFrameworkCore.Design
  3. MudBlazor

O primeiro permite acessar o banco SQL Server e o segundo compartilha componentes com a ferramenta que realiza o Migrations e o terceiro vai fornecer os componentes que iremos usar em nosso projeto.

Agora precisamos incluir na tag <head> do arquivo _Layout.cshtml da pasta Pages do projeto as referências às folhas de estilo e fontes do MudBlazor:

@using Microsoft.AspNetCore.Components.Web
@namespace BlazorBolos.Pages
@addTagHelper *, Microsoft.AspNetCore.Mvc.TagHelpers

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="utf-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <base href="~/" />
   @* <link rel="stylesheet" href="css/bootstrap/bootstrap.min.css" />
    <link href="css/site.css" rel="stylesheet" />*@
    <link href="BlazorBolos.styles.css" rel="stylesheet" />
 
  <link href="https://fonts.googleapis.com/css?family=Roboto:300,400,500,
                                      700&display=swap" rel="stylesheet" />
   <link href="_content/MudBlazor/MudBlazor.min.css" rel="stylesheet" />

    <component type="typeof(HeadOutlet)" render-mode="ServerPrerendered" />
</head>
...

Neste mesmo arquivo vamos incluir a referência ao arquivo javascript da biblioteca:

      ...

    <script src="_framework/blazor.server.js"></script>
 
  <script src="_content/MudBlazor/MudBlazor.min.js"></script>
</body>
</html>

Para concluir no arquivo Program vamos incluir o registro do serviço da biblioteca incluindo a linha de código:

using BlazorBolos.Data;
using Microsoft.EntityFrameworkCore;
using MudBlazor.Services;

var builder = WebApplication.CreateBuilder(args);

// Add services to the container.
builder.Services.AddMudServices();

builder.Services.AddRazorPages();
builder.Services.AddServerSideBlazor();

...

Vamos incluir na pasta wwwroot do projeto uma pasta images e nesta pasta vamos incluir algumas imagens dos bolos que iremos exibir.

Com isso temos tudo pronto para usar o MudBlazor em nosso projeto.

Criando o modelo de domínio e o contexto

Na pasta Data do projeto vamos criar a classe Bolo que representa o nosso domínio:

namespace BlazorBolos.Data;

public class Bolo
{
    public int Id { get; set; }
    public string? Nome { get; set; }
    public string? Descricao { get; set; }
    public string? ImagemUrl { get; set; }
    public decimal Preco { get; set; }
}

A seguir vamos criar a classe de contexto AppDbContext que herda de DbContext:

using Microsoft.EntityFrameworkCore;

namespace BlazorBolos.Data;

public class AppDbContext : DbContext
{
    public AppDbContext(DbContextOptions<AppDbContext> options) : base(options)
    { }
    public DbSet<Bolo> Bolos { get; set; }

    protected override void OnModelCreating(ModelBuilder modelBuilder)
    {
        modelBuilder.Entity<Bolo>().HasData(
            new Bolo
            {
                Id = 1,
                Nome = "Bolo1",
                Descricao = "Bolo1",
                Preco = 23.45m,
                ImagemUrl = "bolo1.jpg",
                Data = DateTime.Now.Date
            },
            new Bolo
            {
                Id = 2,
                Nome = "Bolo2",
                Descricao = "Bolo2",
                Preco = 21.15m,
                ImagemUrl = "bolo2.jpg"
            },
             new Bolo
             {
                 Id = 3,
                 Nome = "Bolo3",
                 Descricao = "Bolo3",
                 Preco = 43.55m,
                 ImagemUrl = "bolo3.jpg"
             },
            new Bolo
            {
                Id = 4,
                Nome = "Bolo4",
                Descricao = "Bolo4",
                Preco = 34.75m,
                ImagemUrl = "bolo4.jpg"
            }
        );
    }
}

Neste código definimos o mapeamento ORM e usando o método HasData estamos populando a tabela com dados iniciais de bolos.

Vamos definir a string de conexão com o banco de dados no arquivo appsettings.json:

{
  "ConnectionStrings": {
    "DefaultConnection": "Data Source=desktop-bhp8771\\sqlexpress;Initial Catalog=BlazorBolosDB;Integrated Security=True"
  },
  "Logging": {
    "LogLevel": {
      "Default": "Information",
      "Microsoft.AspNetCore": "Warning"
    }
  },
  "AllowedHosts": "*"
}

E finalmente vamos registrar o serviço do contexto no arquivo Program:

using BlazorBolos.Data;
using Microsoft.EntityFrameworkCore;
using MudBlazor.Services;

var builder = WebApplication.CreateBuilder(args);

// Add services to the container.
builder.Services.AddMudServices();

builder.Services.AddRazorPages();
builder.Services.AddServerSideBlazor();

builder.Services.AddDbContext<AppDbContext>(options =>
{
    options.UseSqlServer(builder.Configuration.GetConnectionString("DefaultConnection"));
});
....

Agora podemos aplicar o Migrations e criar o banco de dados BlazorBolosDB e a tabela Bolos contendo alguns dados. Para isso vamos dar os seguintes comandos usando a ferramenta EF Core Tools:

dotnet ef migrations add Inicial

e a seguir

dotnet ef database update

Agora podemos criar a interface com o usuário usando o MudBlazor.

Fazendo o CRUD com MudBlazor

Vamos iniciar alterando o código do componente MainLayout.razor incluindo o código a seguir neste arquivo:

@inherits LayoutComponentBase

<PageTitle>Bolos</PageTitle>

<MudThemeProvider />
<MudDialogProvider />
<MudSnackbarProvider />

<MudLayout>
    <MudAppBar Color="Color.Primary">
          <MudIconButton Icon="@Icons.Rounded.Cake" Color="Color.Inherit" />
          <MudText Typo="Typo.h4">Bolos da Jó </MudText>
    </MudAppBar>

    <MudMainContent class="mt-4">
        @Body
    </MudMainContent>
</MudLayout>

Aqui estamos usando o componente MudAppBar para criar uma barra de navegação exibindo um ícone e um texto - Bolo da Jó.

Executando o projeto teremos o resultado abaixo:

Vamos usar o arquivo Index.razor vamos definir o código abaixo que vai ser a página de apresentação da aplicação e que vai exibir a lista de bolos com alguns dos seus dados:

@page "/"
@inject AppDbContext _context;
@inject IDialogService _dialogService;

<MudContainer Class="d-flex justify-center mb-2">
    <MudFab Color="Color.Primary" Icon="@Icons.Material.Filled.Add" Size="Size.Large"
 IconSize="Size.Large"
     Label="Incluir Bolo" Class="ma-2" @onclick="(e => CreateAsync())" />
</MudContainer>

<MudGrid Justify="Justify.Center" class="pr-4 pl-4">

    @foreach (var bolo in bolos)
    {
        <MudItem xs="2">
            <MudCard>
                <MudCardHeader>
                    <CardHeaderContent>
                        <MudText Typo="Typo.body1">@bolo.Nome</MudText>
                        <MudText Typo="Typo.h6">R$ @bolo.Preco </MudText>
                    </CardHeaderContent>
                </MudCardHeader>
                <MudCardMedia Image="@bolo.ImagemUrl" width="150" Height="150"/>
                <MudCardContent>
                    <MudText Typo="Typo.body2">@bolo.Descricao</MudText>
                </MudCardContent>
                  <MudCardActions>
                   <MudIconButton Icon="@Icons.Filled.Edit" Color="Color.Primary"
                             @onclick="(e => UpdateAsync(bolo.Id))" />
                  <MudIconButton Icon="@Icons.Filled.Delete" Color="Color.Error"
                             @onclick="(e => DeleteAsync(bolo.Id))" />
                 </MudCardActions>
            </MudCard>
        </MudItem>
    }
</MudGrid>
@code {
    List<Bolo> bolos = new List<Bolo>();

    protected override async Task OnInitializedAsync()
    {
        bolos = await _context.Bolos.ToListAsync();
    }

    private async Task CreateAsync()
    {
      var parameters = new DialogParameters();
      parameters.Add("Bolo", new Bolo());

      var dialog = await _dialogService.Show<IncluiAtualizaBoloDialog>("Incluir",
                                            parameters).Result;

    

      if (dialog.Data != null)
      {
        Bolo novoBolo = dialog.Data as Bolo;
         _context.Bolos.Add(novoBolo);
         await _context.SaveChangesAsync();
         bolos.Insert(0, novoBolo);
       }
     }

     private async Task UpdateAsync(int id)
     {
        var parameters = new DialogParameters();
        var boloAtualizar = bolos.FirstOrDefault(_ => _.Id == id);

        parameters.Add("Bolo", boloAtualizar);

        var dialog = await _dialogService.Show<IncluiAtualizaBoloDialog>("Atualizar",
                                         parameters).Result;
   

        if (dialog.Data != null)
        {
           var boloAtualizado = dialog.Data as Bolo;
           _context.Bolos.Update(boloAtualizado);
           await _context.SaveChangesAsync();    

           bolos.Remove(boloAtualizar);
           bolos.Insert(0, boloAtualizado);
        }
      }

       private async Task DeleteAsync(int id)
       {
           bool? result = await _dialogService.ShowMessageBox(
                         "Confirma exclusão",
                         "A exclusão não pode ser desfeita!",
                         yesText: "Deleta", cancelText: "Cancela");

           if (result ?? false)
           {
               var boloRemover = await _context.Bolos.FindAsync(id);
              _context.Bolos.Remove(boloRemover);
               await _context.SaveChangesAsync();
               bolos.Remove(boloRemover);
           }
       }
   }

Neste código obtemos os dados dos bolos no método OnInitializedAsync e definimos os métodos :

Definimos nos eventos onclick a chamada a esses métodos que vão incluir, atualizar e deletar um bolo.

Vamos criar na pasta Pages o componente Razor IncluiAtualizaBoloDialog que usa um MudDialog e apresenta uma janela de diálogo para atualização e exclusão de um bolo.

<MudDialog>
    <DialogContent>
        <MudTextField T="string" Label="Nome" @bind-Value="bolo.Nome" Lines=2 />
        <MudTextField T="decimal" Label="Preco" @bind-Value="bolo.Preco" Format="C2" />
        <MudTextField T="string" Label="Descricao" @bind-Value="bolo.Descricao" Lines=3 />
        <MudTextField T="string" Label="ImagemUrl" @bind-Value="bolo.ImagemUrl" Lines=3 />
    </DialogContent>
    <DialogActions>
        <MudButton OnClick="Cancela">Cancela</MudButton>
        <MudButton Color="Color.Primary" OnClick="Submit">Ok</MudButton>
    </DialogActions>
</MudDialog>

@code {

    [CascadingParameter] MudDialogInstance MudDialog { get; set; }

    [Parameter] public Bolo bolo { get; set; } = new Bolo();

    private void Cancela()
    {
        MudDialog.Cancel();
    }

    private void Submit()
    {
        MudDialog.Close(DialogResult.Ok<Bolo>(bolo));
    }
 }

Agora é só alegria...

Executando o projeto teremos o seguinte resultado:

Pegue o projeto aqui:   BlazorBolos.zip (sem as referências)

"Jesus, porém, olhando para eles, disse: Para os homens é impossível, mas não para Deus, porque para Deus todas as coisas são possíveis."
Marcos 10:27

Referências:


José Carlos Macoratti