En este post vamos a ver cómo crear formularios en blazor y como enviaremos la información al backend para que este la procese.
Permitiéndonos así, mostrar diferentes mensajes al usuario en función de si los datos son correctos o no.
Índice
1 - Caso de Uso
Para este caso de uso vamos a añadir a nuestra web un formulario de contacto.
Para ello, lo primero que debemos hacer es crear en el backend un endpoint el cual reciba la información.
Algo sencillo como lo el siguiente objeto nos servirá para el ejemplo:
public class ContactDto
{
public string Name { get; set; }
public string Surname { get; set; }
public string Email { get; set; }
public string Message { get; set; }
}
Y en el endpoint un simple return true
nos funcionará por ahora, recuerda que aquí estamos testeando el front end, no el backend.
[ApiController]
[Route("api/[controller]")]
public class ContactController : Controller
{
[HttpPost]
public Task<ResultDto<ContactResponse>> Post(ContactDto Contacto)
{
return Task.FromResult(new ContactResponse()
{
MessageSent = true
}.Success()
.MapDto(x => x));
}
}
public class ContactResponse
{
public bool MessageSent { get; set; }
}
Nota: recuerda que en este proyecto utilizamos railway oriented programing.
2 - Creación de un formulario en blazor
Primero de todo debemos crear un componente para nuestro formulario, y ahí crearemos el formulario.
En este caso es un componente dentro de nuestra página principal, pero en muchas otras circunstancias un formulario sería una página única, por ejemplo la creación de un producto, o una reserva de un producto que son los elementos que solemos encontrar en el mundo laboral.
Lo primero que debemos hacer es crear una propiedad privada _contactoDto
que haga referencia al tipo que acabamos de crear, esta propiedad la vamos a utilizar para enlazar los campos en el formulario al propio objeto.
Además crearemos un método llamado Enviar
el cual se ejecutará cuando el usuario envíe el formulario.
Recuerda que podemos hacerlo tanto en el propio fichero .razor
utilizando el bloque @code
o en una partial class
. Personalmente me resulta más fácil ya que en caso de modificar algo es más sencillo de ver, en una partial class.
public partial class Contacto
{
private ContactDto _contact { get; set; } = new ContactDto();
private void Enviar()
{
}
}
3 - Definición de un formulario en blazor
Ahora únicamente debemos crear nuestro formulario. Para ello utilizaremos el componente que microsoft nos proporciona llamado EditForm
en él podemos utilizar el atributo Model
donde podemos indicar nuestra propiedad _contactoDto
. Así como el callback OnSubmit
que nos permite llamar al método que hemos indicado.
<h3>Contacto</h3>
<EditForm Model="_contact" OnSubmit="Enviar">
// Resto del formulario
<button type="submit">Enviar formulario </button>
</EditForm>
Como ves, no hemos indicado el atributo method que indicaremos en un formulario de HTML normal, eso es porque no va ser enviado de la misma forma, sino que al enviarlo, llamaremos a nuestro método y ahí es donde tendremos que escribir como enviaremos el formulario al back end.
3.1 - Elementos de un formulario en blazor
Para nuestros campos en el formulario también vamos a utilizar componentes que nos proporciona microsoft directamente con el lenguaje.
Pese a que podemos utilizar los propios elementos de html recomiendo utilizar los componentes de blazor, eso es debido a que así nos proporciona diferentes validaciones por defecto, por ejemplo si indicamos <inputnumber …>
es como si pusieramos <input type=”number”...>
, lo mismo para el tipo date, checkbox, etc.
Finalmente cada uno de estos componentes contienen una propiedad llamada @bind-Value
La cual nos permite enlazar ese campo en concreto con una propiedad de nuestro objeto.
<h3>Contacto</h3>
<EditForm Model="_contact" OnSubmit="Enviar">
<div class="form-group row">
<label for="name" class="col-md-2 col-form-label">Nombre:</label>
<div class="col-md-10">
<InputText id="name" class="form-control" @bind-Value="_contact.Name" />
</div>
</div>
<div class="form-group row">
<label for="surname" class="col-md-2 col-form-label">Apellido:</label>
<div class="col-md-10">
<InputText id="surname" class="form-control" @bind-Value="_contact.Surname" />
</div>
</div>
<div class="form-group row">
<label for="email" class="col-md-2 col-form-label">Email:</label>
<div class="col-md-10">
<InputText id="email" class="form-control" @bind-Value="_contact.Email" />
</div>
</div>
<div class="form-group row">
<label for="message" class="col-md-2 col-form-label">mensaje:</label>
<div class="col-md-10">
<InputTextArea rows="5" id="message" class="form-control" @bind-Value="_contact.Message" />
</div>
</div>
<button type="submit">Enviar formulario </button>
</EditForm>
Además, como puedes observar, podemos entrelazar elementos html dentro del componente EditForm
así como asignarles clases css.
Ahora en nuestro método Enviar
únicamente debemos hacer una llamada HTTP al backEnd, en donde indicaremos que es tipo POST.
public partial class Contacto
{
[Inject]
private IHttpClientFactory ClientFactory { get; set; }
private ContactDto _contact { get; set; } = new ContactDto();
private async Task Enviar()
{
HttpClient client = ClientFactory.CreateClient("BackendApi");
HttpResponseMessage result = await client.PostAsJsonAsync($"api/contact", _contact);
//Luego trabajaremos con el resultado
var contactResponse = await result.Content.ReadFromJsonAsync<ResultDto<ContactResponse>>();
}
}
4 - Validación de un formulario en blazor
Para validar un formulario tenemos varias opciones
4.1 - Validación manual de formularios en blazor
si hemos asignado el atributo @bind-Value
correctamente, cada propiedad de nuestro objeto tendrá asignado el valor que el usuario ha introducido, por lo tanto en nuestro método Enviar
podríamos comprobar que cada campo tiene los valores que necesitamos y si no lo hace, no enviar el formulario al back-end.
private async Task Enviar()
{
if (string.IsNullOrEmpty(_contact.Message))
{
//no enviamos la información
}
else
{
HttpClient client = ClientFactory.CreateClient("BackendApi");
HttpResponseMessage result = await client.PostAsJsonAsync($"api/contact", _contact);
//Luego trabajaremos con el resultado
var contactResponse = await result.Content.ReadFromJsonAsync<ResultDto<ContactResponse>>();
}
}
4.2 - Validación de formularios a través de anotaciones
Como forma adicional podemos validar los formularios utilizando anotaciones en nuestra clase.
Para ello lo primero que debemos hacer es instalar la librería System.ComponentModel.Annotations
. la cual nos permite incluir atributos a nuestras propiedades, los cuales van a servir para validar dichos elementos del formulario.
Por lo tanto en este caso de uso, necesitamos comprobar que tanto el nombre como el contenido del mensaje son obligatorios así como asegurarnos que el email es un email.
public class ContactDto
{
[Required(ErrorMessage = "Por favor indica tu nombre")]
public string Name { get; set; }
public string Surname { get; set; }
[RegularExpression("^[a-zA-Z0-9_\\.-]+@([a-zA-Z0-9-]+\\.)+[a-zA-Z]{2,6}$", ErrorMessage = "Debes indicar un email válido")]
public string Email { get; set; }
[Required(ErrorMessage = "El contenido del mensaje es obligatorio")]
public string Message { get; set; }
}
Ahora podemos modificar nuestro formulario, donde vamos a indicar que vamos a utilizar el componente <DataAnnotationsValidator/>
junto con ValidationMessage
, el cual mostrará el error que hemos indicado.
<h3>Contacto</h3>
<EditForm Model="_contact" OnSubmit="Enviar">
<DataAnnotationsValidator />
<div class="form-group row">
<label for="name" class="col-md-2 col-form-label">Nombre:</label>
<div class="col-md-10">
<InputText id="name" class="form-control" @bind-Value="_contact.Name" />
<ValidationMessage For="@(() => _contact.Name)" />
</div>
</div>
<div class="form-group row">
<label for="surname" class="col-md-2 col-form-label">Apellido:</label>
<div class="col-md-10">
<InputText id="surname" class="form-control" @bind-Value="_contact.Surname" />
</div>
</div>
<div class="form-group row">
<label for="email" class="col-md-2 col-form-label">Email:</label>
<div class="col-md-10">
<InputText id="email" class="form-control" @bind-Value="_contact.Email" />
<ValidationMessage For="@(() => _contact.Email)" />
</div>
</div>
<div class="form-group row">
<label for="message" class="col-md-2 col-form-label">mensaje:</label>
<div class="col-md-10">
<InputTextArea rows="5" id="message" class="form-control" @bind-Value="_contact.Message" />
<ValidationMessage For="@(() => _contact.Message)" />
</div>
</div>
<button type="submit">Enviar formulario </button>
</EditForm>
Y este sería el resultado que podemos observar.
Y si intentamos enviar el mensaje, no podremos hasta que arreglemos los errores.
Nota: Recibiremos el mismo resultado si intentamos llamar a la API directamente.
5- Trabajar con la respuesta del servidor
Finalmente cuando recibimos la respuesta del servidor debemos indicar al usuario que todo ha funcionado bien (o mostrar errores en caso de que existan).
Como has podido asumir en blazor no tenemos ajax como tenemos en webs convencionales para llamar de forma asíncrona al servidor. Aquí lo hemos realizado con nuestra llamada HTTP dentro de nuestro método Enviar
.
En nuestro caso devuelve ResultDto<contactoResponse>
lo que debemos hacer es comprobar si el resultado es true
y si lo es, mostrar un mensaje.
Personalmente cuando se trata de un formulario de contacto, me gusta ocultar dicho formulario y mostrar un mensaje en su lugar, para asegurar al usuario que su mensaje ha sido enviado.
Para ello lo primero que debemos hacer es crear dos estilos css, uno para mostrar y otro para ocultar el contenido.
<style>
.oculto{
display: none;
}
.visible{
display: inherit;
}
</style>
Y añadimos un bloque donde tendremos nuestro mensaje oculto, así como añadimos la regla css a nuestro formulario.
Pero no agregamos la regla como tal, sino que indicamos una variable la cual apunta a la regla css
<h3>Contacto</h3>
<div class="alert alert-success @MessageBoxCss" role="alert">
<h4 class="alert-heading">Mensaje enviado correctamente</h4>
<p>Gracias por contactar con el equipo de netmentor, nos pondremos en contacto lo mas rápido posible.</p>
</div>
<div class="@FormCss">
<EditForm Model="_contact" OnSubmit="Enviar" clas="@FormCss">
///Resto del formulario
<button type="submit">Enviar formulario </button>
</EditForm>
</div>
Por supuesto en nuestro código csharp debemos indicar dicha variable y le asignamos el valor que queremos que tengan tanto la caja de mensaje enviado como el formulario.
private string MessageBoxCss { get; set; } = "oculto";
private string FormCss { get; set; } = "visible";
Ahora únicamente debemos actualizar los valores cuando el mensaje ha sido enviado correctamente.
private async Task Enviar()
{
HttpClient client = ClientFactory.CreateClient("BackendApi");
HttpResponseMessage result = await client.PostAsJsonAsync($"api/contact", _contact);
ResultDto<ContactResponse> contactResponse = await result.Content.ReadFromJsonAsync<ResultDto<ContactResponse>>();
if (contactResponse.Value)
{
MessageBoxCss = "visible";
FormCss = "oculto";
}
}
Podemos observar cómo una vez el formulario ha sido enviado, únicamente el mensaje de enviado correctamente es visible.
Conclusión
En este post hemos visto cómo trabajar con formularios dentro de blazor así como sus validaciones, y como no hace falta correr javascript para ir actualizando los elementos de la página.
No olvidemos que los formularios son algo básico en nuestro día a día laboral.