Blazor Server - CRUD com EFCore com Modal Dialog - IV


Neste artigo vamos criar uma aplicação Blazor Server e realizar um CRUD usando o EF Core e os recursos do jQuery e Bootstrap.

Continuando a terceira parte do artigo vamos agora incluir a validação, salvar os dados e usar o JavaScript Interop para gerenciar as janelas modais.

Incluindo a validação

Para validar a entrada dos dados no formulário modal primeiro teremos que usar o recurso Data Annotations no modelo de domínio. Isso já foi feito na definição do modelo como vemos a seguir:

using System;
using System.ComponentModel.DataAnnotations;
namespace Blazor_Tarefas.Data
{
    public class Tarefa
    {
        [Key]
        public int Id { get; set; }
        [Required(ErrorMessage = "Informe o nome da tarefa")]
        [StringLength(30, ErrorMessage = "Nome é muito longo.")]
        public string Nome { get; set; }
        [Required(ErrorMessage = "O status é obrigatório")]
        [MaxLenght(30)]
        public string Status { get; set; }
        [Required(ErrorMessage = "A data de conclusão tem que ser informada")]
        public DateTime DataConclusao { get; set; }
    }
}

Também precisamos adicionar o componente DataAnnotationsValidator que anexa suporte de validação usando anotações de dados.

Para exibir as mensagens de validação, vamos usar o componente ValidationSummary. Ambos os componentes são adicionados no componente TarefaDetalhe.razor dentro do componente EditForm.

...
<EditForm Model="@TarefaObject" OnValidSubmit="@HandleValidSubmit">

         <DataAnnotationsValidator />
          <ValidationSummary />

                        <div class="form-group">
                            <label for="nome">Tarefa</label>
                            <input type="hidden" @bind-value="@TarefaObject.Id" />
                            <InputText id="name" class="form-control" @bind-Value="@TarefaObject.Nome" />
                        </div>
                        <div class="form-group">
                            <label for="status">Status</label>
                            <InputSelect id="Summary" class="form-control" @bind-Value="TarefaObject.Status">
                                <option value="">Selecione</option>
                                @foreach (var status in TarefaStatusLista)
                                {
                                    <option value="@status">@status</option>
                                }
                            </InputSelect>
                        </div>
                        <div class="form-group">
                            <label for="dataConclusao">Conclusão em</label>
                            <input type="date" id="addition" name="math" @bind-value="@TarefaObject.DataConclusao" />
                        </div>
                        <button type="submit" class="btn btn-primary">Enviar</button>
                        <button type="button" class="btn btn-secondary" data-dismiss="modal">Cancelar</button>
 </EditForm>
...

Após essas implementações ao rodar a aplicação e tentar submeter o formulário com dados inválidos veremos as mensagens de erro conforme a figura a seguir:

Podemos ver os erros de validação na seção ValidationSummary. Se você quiser mostrar a mensagem de validação ao lado de cada controle em vez de um resumo, remova o componente ValidationSummary e inclua o seguinte padrão de código próximo a cada elemento input :

<ValidationMessage For="@(() => TarefaObject.Nome)" />

O resultado obtido é o mostrado na figura abaixo:

Salvando os dados e usando o JS Interop

Após realizar a validação dos dados podemos implementar a funcionalidade para salvar os dados no banco de dados.

Isso é feito no método HandleValidSubmit que é acionado quando o formulário é enviado com êxito após passar na validação. Assim temos que adicionar a lógica “salvar” neste método.

...

  private async void HandleValidSubmit()
  {
       await service.Add(TarefaObject);
  }
...

Com isso os dados já serão salvos no banco de dados mas você vai notar que a janela de diálogo modal continua aberta.

Precisamos chamar um código JavaScript a partir do código .NET para fechar a caixa de diálogo. Para chamar a função JavaScript a partir do C#, usamos o recurso IJSRuntime.  E vamos ter que injetar esse serviço no componente:

@inject IJSRuntime jsRuntime

Assim vamos criar o método CloseTarefaModal que será invocado no método HandleValidSubmit() logo após os dados serem salvos:

...

  private async void HandleValidSubmit()
  {
       await service.Add(TarefaObject);
       await CloseTarefaModal();
  }
...

A seguir vamos definir o método CloseTarefaModal:

@inject ITarefasService service
@inject IJSRuntime jsRuntime
...
@code {
        [Parameter]
        public Tarefa TarefaObject { get; set; }
        List<string> TarefaStatusLista = new List<string>() { "Nova", "Em Progresso", "Concluída" };
        private async Task CloseTarefaModal()
        {
            await jsRuntime.InvokeAsync<object>("CloseModal", "tarefaModal");
        }
        private async void HandleValidSubmit()
        {
            await service.Add(TarefaObject);
            await CloseTarefaModal();
        }
    }

O método InvokeAsync<T> usa um identificador para a função JavaScript que você deseja invocar junto com qualquer número de argumentos serializáveis JSON.

Aqui chamamos a função JavaScript 'CloseModal' para a janela ''tarefaModal'.

A seguir vamos definir o método JavaScript para fechar a janela e para abrir a janela no arquivo  _Host.cshtml.

...
   <script src="_framework/blazor.server.js"></script>
    <script>
        function ShowModal(modalId) {
            $('#' + modalId).modal('show');
        }
        function CloseModal(modalId) {
            $('#' + modalId).modal('hide');
        }
    </script>
</body>
</html>

Neste código a função ShowModal() vai abrir a janela e CloseModal() vai fechar a janela modal.

A comunicação entre os componentes

Agora os dados estão sendo salvos e a janela modal está sendo fechada, mas não consiguimos ver o item recém-adicionado na lista de tarefas.

Para ver os dados temos que atualizar o navegador.

Isso ocorre porque temos que informar ao componente pai para se atualizar para exibir o novo conjunto de dados que foram incluídos a partir do componente filho.

Os componentes podem oferecer retornos de chamada (CallBacks) que os componentes pais podem usar para reagir a eventos gerados por seus componentes filhos.

Vamos implementar isso.

No componente filho TarefaDetalhe vamos declarar um parâmetro Action e definir a Action com o nome DadosAlterados e a invocar no método HandleValidSubmit():

         [Parameter]
         public Action DadosAlterados { get; set; }

        private async void HandleValidSubmit()
        {
            await service.Add(TarefaObject);
            await CloseTarefaModal();
            DadosAlterados?.Invoke();

        }

A seguir podemos fazer com que o componente Pai - ListaTarefa.razor - trata o evento DadosAlterados da seguinte forma:

<TarefaDetalhe TarefaObject=tarefaObject DadosAlterados="@DadosAlterados"></TarefaDetalhe>
@page "/listatarefas"
  ...
    private async void DadosAlterados()
    {
        listaTarefas = await service.Get();
        StateHasChanged();
     }
}

Note que declaramos no componente Pai a Action @DadosAlterados definida no componente Filho.

O método StateHasChanged notifica o componente de que seu estado foi alterado. Quando aplicável, chamar StateHasChanged  faz com que o componente seja renderizado novamente. Este método é chamado automaticamente para métodos EventCallBack.

Agora podemos adicionar um novo registro após a validação, fechar a caixa de diálogo e ao salvar vamos atualizar o componente pai.

Neste momento ao executar o projeto teremos o seguinte resultado :

Na próxima parte do artigo vamos continuar implementando as demais funcionalidades do CRUD.

"Quanto lhe for possível, não deixe de fazer o bem a quem dele precisa"
Provérbios 3:27

Referências:


José Carlos Macoratti