HTML5 Validation in Blazor - c#

I am trying to get the built in HTML5 validation working in my Blazor application but it just gets ignored.
This is my Razor code
<form class="row">
<div class="form-row">
<label for="exampleFormControlInput1" class="form-label">Email address</label>
<input type="email" class="form-control" id="exampleFormControlInput1" placeholder="name#example.com" #bind="#details.Email">
</div>
<div class="form-row">
<label for="exampleFormControlInput1" class="form-label">Phone</label>
<input type="tel" class="form-control" id="exampleFormControlInput1" placeholder="name#example.com" #bind="#details.Phone">
</div>
<div class="form-row">
<button type="submit" class="btn btn-primary" #onclick="SubmitForm">Send</button>
</div>
</form>
</div>
#code {
private ContactDetails details = new ContactDetails();
public async void SubmitForm()
{
var result = await Http.PostAsJsonAsync("Contact", details);
}
}
I know I can use Blazors validation, but I want to just use the built in HTML5 validation for this as it is a very basic form. How do I stop the SubmitForm code from being run if the HTML5 validation is triggered?
If I remove the code from the SubmitForm() method obviously nothing then happens when it enters that method and it goes back to the webpage and the html5 validation messages are displayed, however clearly the method is still being fired. Is there a way to stop the code running if there are html5 validation errors?

Okay, I have worked out how to do this.
Simply add and onsubmit event to the Form tag and then remove the onclick from the button.
<form class="row" #onsubmit="SubmitForm">
This then works as expected.
Now there are clearly lots of benefits to using the built in Blazor forms components but sometimes just working with standard html forms is needed and thankfully there is a solution to do that.

Use Regex by adding this using #using System.Text.RegularExpressions;
Valid email : komaei#live.com
Invalid emails : komaei , komaei# , komaei#live.c
<div class="form-group row">
<label for="#nameof(createModel.Email)" class="col-sm-2 col-form-label text-right">Email : </label>
<div class="col-lg-7 col-sm-10">
<InputText type="email" class="form-control" placeholder="Email" required
id="#nameof(createModel.Email)" #bind-Value="createModel.Email">
</InputText>
</div>
</div>
#code {
private Customer createModel = new();
private async Task SubmitForm()
{
var emailPattern = #"^[A-Za-z0-9](([_\.\-]?[a-zA-Z0-9]+)*)#([A-Za-z0-9]+)(([\.\-‌​]?[a-zA-Z0-9]+)*)\.([A-Za-z]{2,})$";
Regex regex = new Regex(emailPattern);
if (regex.IsMatch(createModel.Email) == false)
{
await JsRuntime.InvokeVoidAsync("alert", "Email is not valid!");
return;
}
var response = await _http.PostAsJsonAsync("Customers/Create", createModel);
if (response.IsSuccessStatusCode) // 200
_navigationManager.NavigateTo("Customers");
else if (response.StatusCode == System.Net.HttpStatusCode.BadRequest)
await JsRuntime.InvokeVoidAsync("alert", response.Content.ReadAsStringAsync().Result);
}
}

Related

How can I make a Blazor form that only submits fields changed

I have a Blazor form using .NET 6 and have it working with an API PUT endpoint that can update the database upon clicking a button. This takes all of the bound input fields in the form and submits them into the payload.
I've modified my endpoint such that if payload is missing any attributes, it sends null and doesn't update the particular database field, essentially ensuring that only the sent changes are the items that get updated.
However, I'm struggling to find an example in Blazor where the submit only sends input fields on the form that were modified. I'm new to Blazor and my original implementation just had a button that had an #onclick event, which then called the endpoint, passing the object that has data bound in the form. This works but I'd prefer that I understand how to only send the data modified to not have unrequired overwrites.
Here's a mockup of what I have (truncated for readability):
#inject IDataSetService DataSetService
#page "/GameData/Data/DataSetEdit"
#page "/GameData/Data/DataSetEdit/{IDRouteParam:int}"
<PageTitle>DataSet Edit</PageTitle>
<div class="content-wrapper">
<!-- Main content -->
<section class="content">
<div class="container-fluid">
<!-- Card -->
<div class="card">
<div class="card-header">
</div>
<form>
<div class="card-body">
<div class="form-group">
<label for="ID">ID</label>
<input type="text" class="form-control" id="ID" placeholder="12345" value="#dataSet.ID">
</div>
<div class="form-group">
<label for="IsActive">Is Active?</label>
<input type="text" class="form-control" id="IsActive" placeholder="IsActive" #bind-value="dataSet.IsActive">
</div>
<div class="form-group">
<label for="IsDeleted">Is Deleted?</label>
<input type="text" class="form-control" id="IsDeleted" placeholder="IsDeleted" #bind-value="dataSet.IsDeleted">
</div>
<div class="card-footer">
<button type="submit" class="btn btn-info" #onclick="() => UpdateDataSet()">Save</button>
<button type="submit" class="btn btn-danger">Cancel</button>
</div>
</form>
</div>
<!-- /.Card -->
}
</div>
</section>
</div>
#code {
private DataSet? dataSet = null;
private string message = string.Empty;
private bool ActiveChecked = false;
private bool DeletedChecked = false;
[Parameter]
public int IDRouteParam { get; set; }
protected override async Task OnParametersSetAsync()
{
message = "Loading DataSet.. ";
var result = await DataSetService.GetDataSetByID(IDRouteParam);
if (!result.Success)
{
message = result.Message;
}
else
{
dataSet = result.Data;
}
}
async void UpdateDataSet()
{
dataSet= await DataSetService.UpdateDataSet(dataSet);
}
}
I have been reading on changing this to an EditForm along with an OnValidSubmit, but I haven't come across an example that shows how to detect fields that were changed and how to use those to build the payload to send to the API.

Setting InputText Focus on start screen in Blazor

I am creating a Blazor PWA application using Visual Studio 2019. I use javascript to set focus to InputText fields during startup and certain key events.
function focusInput(id){
document.getElementById(id).focus();
var tb = document.querySelector("#" + id);
if (tb.select) {
tb.select();
}
}
and call it in my code behind like this
private string inputID = "user-id";
protected async override Task OnAfterRenderAsync(bool firstRender)
{
await jsInterop.InvokeVoidAsync("focusInput", inputID);
}
This is the razor page
<EditForm Model="#login" class="card card-body mt-2">
<div class="form-group row">
<label for="userid" class="col-sm-2 col-form-label">User ID</label>
<InputText id="#inputID" class="form-control" #bind-Value="#login.UserID" #onkeyup="(KeyboardEventArgs e) => KeyUpUserIDAsync(e)"/>
</div>
<div class="form-group row">
<label for="message" class="col-sm-2 col-form-label">Message</label>
<InputTextArea id="textarea-message" class="form-control" #bind-Value="#errorMessage" />
</div>
</EditForm>
It works great except when I run it and load the first page which is the Login Page. Instead of getting focus in the field, the focus instead stays in the URL bar. If I refresh the page the InputeText gets focus. Note that all the other pages I navigate to after logging in do not have this problem. Just the initial page. I wrote to the console to make sure it was being called and it was. I also tried using autofocus attribute but it does not work either.
I could make your code work by following the solution provided here:
How to set focus to InputText Element?
Index.razor
#page "/"
#inject IJSRuntime JSRuntime
<EditForm Model="#login" class="card card-body mt-2">
<div class="form-group row">
<label for="userid" class="col-sm-2 col-form-label">User ID</label>
<InputText id="#inputID" class="form-control" #bind-
Value="#login.UserID" #onkeyup="(KeyboardEventArgs e) =>
KeyUpUserIDAsync(e)" />
</div>
<div class="form-group row">
<label for="message" class="col-sm-2 col-form-
label">Message</label>
<InputTextArea id="textarea-message" class="form-control" #bind-
Value="#errorMessage" />
</div>
</EditForm>
#code {
public class LoginModel
{
public string UserID { get; set; }
}
public LoginModel login = new();
public string inputID = "user-id";
public string errorMessage = null;
protected override async Task OnAfterRenderAsync(bool firstRender)
{
if (firstRender)
{
await JSRuntime.InvokeVoidAsync("focusElement", inputID);
}
}
async Task KeyUpUserIDAsync(KeyboardEventArgs e)
{
}
}
_Host.cshtml
<script>
function focusElement(id) {
const element = document.getElementById(id);
element.focus();
}
</script>
I ran the application several times, and each time it loads the login input gets focus immediately without need for me to refresh the page.

How to create div element in Blazor

I'm writing an application using Blazor server side and .Net 5. I want when I write text in the search box it will show results in new divs. However, the search results are not displayed. When I debug the code from service works fine.
#page "/"
#using MovieSearch.Service
#using MovieSearch.Data
#using MovieSearch.Helpness
#inject IOMDbService service
<div class="movie-search">
<div class="search-container">
<form action="/">
<input type="text" placeholder="Find a movie" #bind-value="#searchTerm" />
<button type="submit" #onclick="FindMovies"><i class="fa fa-search"></i></button>
</form>
</div>
</div>
<div class="movies">
#if(moviesResult != null)
{
if(!moviesResult.Success)
{
foreach(var error in moviesResult.Errors)
{
<p>#error.Message</p>
}
}
else
{
foreach(var movie in moviesResult.Value)
{
<div class="movie">
<p>#movie.Title</p>
<img src="#movie.Poster" />
</div>
}
}
}
</div>
#code
{
private string searchTerm;
private Result<IEnumerable<Movie>> moviesResult;
async Task FindMovies()
{
moviesResult = await service.FindMovies(searchTerm);
}
}
What can I do to view the results?
Try changing this part of your code from
<form action="/">
<input type="text" placeholder="Find a movie" #bind-value="#searchTerm" />
<button type="submit" #onclick="FindMovies"><i class="fa fa-search"></i></button>
</form>
to just this
<input type="text" placeholder="Find a movie" #bind-value="#searchTerm" />
<button #onclick="FindMovies"><i class="fa fa-search"></i></button>
So in other words, remove the form tags, as well as removing the type on the button. I believe that blazor is now actually trying to post the form to whatever action you specified in the form as opposed to the onclick handler you assigned to your button.

post form without refresh .net core mvc

I have a simple application in .NET Core MVC, with this simple form. When I try to post the form without refresh whole page only rest the form and got the result of done.
My code trial is:
<form asp-area="" asp-controller="ContactUs" asp-action="UserMessage" data-ajax="true">
<h5>Reach us quickly</h5>
<div class="row">
<span asp-validation-for="UserName" class="text-danger"></span>
<div class="col-sm-6 col-12">
<div class="form-group">
<input asp-for="UserName" class="form-control" name="UserName" placeholder="Enter name" required="required">
</div>
</div>
<span asp-validation-for="Email" class="text-danger"></span>
<div class="col-sm-6 col-12">
<div class="form-group">
<input asp-for="Email" class="form-control" name="Email" placeholder="Enter email" required="required">
</div>
</div>
</div>
<div class="row">
<span asp-validation-for="Msg" class="text-danger"></span>
<div class="col-12">
<div class="form-group">
<textarea asp-for="Msg" name="Msg" class="form-control" rows="7" cols="25" placeholder="Message"></textarea>
</div>
</div>
</div>
<div class="row">
<div class="col-sm-12 mt-3">
<button type="submit" class="btn solid-btn" id="btnContactUs">
Send Message
</button>
</div>
</div>
</form>
and my action is:
[HttpPost]
public async Task<IActionResult> UserMessage(ContactUs model)
{
try
{
if (ModelState.IsValid)
{
_context.Add(model);
await _context.SaveChangesAsync();
return NoContent();
}
}
catch (Exception e)
{
Console.WriteLine(e);
throw;
}
return NoContent();
}
I want to change return NoContent(); so I can show a message to the user that his message has sent or failed, without refreshing the whole page.
Your form tag is missing some other ajax attributes:
<form method="post"
data-ajax="true"
data-ajax-method="post"
data-ajax-url="#Url.Page("UserMessage", "ContactUs")"
data-ajax-loading="#loading"
data-ajax-mode="replace"
data-ajax-update="#updateDiv">
<!-- form controls -->
<button type="submit" class="btn solid-btn" id="btnContactUs">Send Message</button>
<span id="loading" style="display:none;"> <i class="fas fa-spinner fa-spin"></i></span>
</form>
<div id="updateDiv">
<!-- ajax content will load here -->
<!-- you can put the form inside this div -->
<!-- so after submit the result will replace the form controls -->
</div>
<!-- include jquery libraries -->
#section Scripts {
<script src="https://cdn.jsdelivr.net/npm/jquery#3.5.1/dist/jquery.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/jquery-ajax-unobtrusive#3.2.6/dist/jquery.unobtrusive-ajax.min.js"></script>
}
The backend method can return the result as below:
public async Task<ContentResult> OnPostUserMessageAsync(ContactUs model)
{
try
{
if (ModelState.IsValid)
{
_context.Add(model);
await _context.SaveChangesAsync();
return Content("done");
}
}
catch (Exception e)
{
Console.WriteLine(e);
throw;
}
return Content("something went wrong...");
}
See Ajax Request Using Inline Attributes
If you don't want the page to be replaced, you'll have to use ajax to post the data via javascript. Posting a form in a browser will replace the current html with the result of the controller, that's just how forms work.
It is also common practice to redirect any successful POST to a different page, so that the user can refresh that page without processing the POST request twice.
thanks for #Laz Ziya i have used his code and modify form to be
instead of
<form method="post"
data-ajax="true"
data-ajax-method="post"
data-ajax-url="#Url.Page("UserMessage", "ContactUs")"
data-ajax-loading="#loading"
data-ajax-mode="replace"
data-ajax-update="#updateDiv">
to be
<form asp-area="" asp-controller="ContactUs" asp-action="UserMessage" data-ajax-loading="#loading" data-ajax-mode="replace" data-ajax-update="#updateDiv" data-ajax-success="Success" data-ajax-failure="Failure" data-ajax="true">
for post form without refresh ,you need to use ajax in your code.

WebAPI core IFormFile always showing null

I have one front-end in angular 4, and one back-end in ASP.NET Core WebAPI, the functionality of them is, send data and an curriculum vitae of one people to one database which is SQL Server, but... always the IFormFile of method who catch the data and send file to the database is null.
I've already tried all type of solution that I found on the internet but none of them worked for me.
as response of the Post method i receive this exception
An unhandled exception occurred while processing the request.
NullReferenceException: Object reference not set to an instance of an object.
WebApplication.Controllers.CandidateController+<Post>d__4.MoveNext() in CandidateController.cs, line 60
Down here I pasted parts of the code of the project who do this.
github link for complete code:
front-end code
back-end code
HTML
<form class="col-md-6 offset-md-3" method="POST" enctype="multipart/form-data" #form="ngForm" (ngSubmit)="onSubmit(form)">
<div class="form-group row">
<label for="name" class="col-sm-2 col-form-label">Name</label>
<div class="col-sm-10">
<input type="text" class="form-control" placeholder="Nome completo" id="name" name="name" ngModel />
</div>
</div>
<div class="form-group row">
<label for="email" class="col-sm-2 col-form-label">Email</label>
<div class="col-sm-10">
<input type="email" class="form-control" id="email" placeholder="Email" name="email" ngModel />
<small id="emailHelp" class="form-text text-muted">
We'll never share your email with anyone else.
</small>
</div>
</div>
<div class="form-group row">
<label for="country" class="col-sm-2 col-form-label">Country</label>
<div class="col-sm-10">
<input type="text" class="form-control" id="country" placeholder="Country" name="country" ngModel />
</div>
</div>
<div class="form-group row">
<label for="state" class="col-sm-2 col-form-label">State</label>
<div class="col-sm-10">
<input type="text" class="form-control" id="state" placeholder="Estado" name="state" ngModel />
</div>
</div>
<div class="form-group row">
<label for="city" class="col-sm-2 col-form-label">City</label>
<div class="col-sm-10">
<input type="text" class="form-control" id="city" placeholder="Cidade" name="city" ngModel />
</div>
</div>
<div class="form-group row">
<label for="file" class="col-sm-2 col-form-label">Curriculum</label>
<div class="col-sm-10">
<input type="file" id="file" name="file" ngModel />
</div>
</div>
<div class="container text-center">
<button type="submit" class="btn btn-outline-dark">Submit</button>
</div>
</form>
Angular 4
onSubmit(form: NgForm) {
const { file } = form.value;
delete form.value.file;
var data = new FormData();
data.append('Candidates', JSON.stringify(form.value));
data.append('file', file);
console.log(form.value);
console.log(file);
const headers = new Headers();
headers.append('Access-Control-Allow-Origin', '*');
const options = new RequestOptions({headers: headers});
this.http.post("http://localhost:54392/api/candidates", data, options)
.subscribe(
data => {
console.log("Foi");
},
error => {
console.log("Não foi");
});
}
C#
[HttpPost("candidates")]
public async Task<IActionResult> Post(IFormFile file)
{
var json = HttpContext.Request.Form["Candidates"];
var jsonTextReader = new JsonTextReader(new StringReader(json));
var candidate = new JsonSerializer().Deserialize<Candidate>(jsonTextReader);
if (!ModelState.IsValid)
return BadRequest();
using (var memoryStream = new MemoryStream())
{
await file.OpenReadStream().CopyToAsync(memoryStream);
candidate.CurriculumVitae = memoryStream.ToArray();
}
await dataBase.AddAsync(candidate);
dataBase.SaveChanges();
return Ok(candidate);
}
Upload images in Angular 4 without a plugin provides a walkthrough of the process you're attempting to achieve. It uses #ViewChild to grab a reference to the file input in the DOM and then uses that when building up the FormData. In your scenario, this involves a few changes, as follows:
Replace ngModel on the file input in your HTML with #file. This creates a template reference variable that can be accessed inside your component when using #ViewChild in the next step.
Add #ViewChild('file') fileInput; to the component, above your constructor. This links the #file template reference variable to your component code.
Remove the following code from your component:
const { file } = form.value;
delete form.value.file;
The file property will no longer exist at this point, so there's nothing to delete.
Replace data.append('file', file); with the following:
let fileBrowser = this.fileInput.nativeElement;
if (fileBrowser.files && fileBrowser.files[0]) {
data.append("file", fileBrowser.files[0]);
}
This last block of code grabs a handle on the file and appends it to your FormData.
You can name the variables and template reference variable whatever you want. The only thing that needs to be specific is the string value file used in data.append must match the variable name used for your C# IFormFile variable.
As an aside, you can also remove your custom Headers and RequestOptions as setting Access-Control-Allow-Origin as a request header is doing nothing here. This header should be set on the response, by the server.
I earlier had this issue, Got Fixed by removing. 'Content-Type', 'application/json; charset=utf-8'
And Also you need to ensure you have same parameter send and received both at client and server
Add the [FromBody] attribute. You can read more here: Binding formatted data from the request body
Change public async Task<IActionResult> Post(IFormFile file) to public async Task<IActionResult> Post([FromBody] IFormFile file)

Categories