Input field keeps switching between text and date in ASP.NET Core MVC application - model-view-controller

I'm having a problem with an input field switching between type=text (when it's not focused) and type=date (when it's focused). I'm using .NET Core 3.1 framework.
Model:
public class DocumentVersionModel
{
public int Id { get; set; }
public int DocumentModelId { get; set; }
[DataType(DataType.Date, ErrorMessage="Date only")]
[DisplayFormat(DataFormatString = "{0:yyyy-MM-dd}", ApplyFormatInEditMode = true)]
public DateTime CreationDate { get; set; }
[DataType(DataType.Date, ErrorMessage="Date only")]
[DisplayFormat(DataFormatString = "{0:yyyy-MM-dd}", ApplyFormatInEditMode = true)]
public DateTime LastChangeDate { get; set; }
[DataType(DataType.Date, ErrorMessage="Date only")]
[DisplayFormat(DataFormatString = "{0:yyyy-MM-dd}", ApplyFormatInEditMode = true)]
public DateTime DocumentDate { get; set; }
public string Content { get; set; }
public int Version { get; set; }
public DocumentModel Document { get; set; }
}
The format yyyy-MM-dd is needed for the database and is the one displayed when the input field isn't focused. The format I need to display the dates is mm/dd/yyyy, but I can't change the DateFormatString (and removing ApplyFormatInEditMode = true didn't work).
This is the form in the RazorPage:
<form asp-action="Edit">
<br />
<div class="row justify-content-center">
<div class="col-12 form-group">
<label>Titolo</label>
<input asp-for="Title" class="form-control" />
</div>
<div class="col">
<div asp-validation-summary="ModelOnly" class="text-danger"></div>
<input type="hidden" asp-for="Id" id="iddoc" />
<div class="form-group">
<label>Data Documento</label>
<input asp-for="DocumentVersion.FirstOrDefault(q => q.Version==Model.ActiveVersion).DocumentDate" class="form-control" />
<span asp-validation-for="DocumentVersion.FirstOrDefault(q => q.Version==Model.ActiveVersion).DocumentDate" class="text-danger"></span>
</div>
<div class="form-group">
<label>Data Creazione</label>
<input asp-for="DocumentVersion.FirstOrDefault(q => q.Version==Model.ActiveVersion).CreationDate" class="form-control" readonly="readonly" />
</div>
<div class="form-group">
<label>Data Ultima Modifica</label>
<input asp-for="DocumentVersion.FirstOrDefault(q => q.Version==Model.ActiveVersion).LastChangeDate" class="form-control" readonly="readonly" />
</div>
</div>
</form>
A little note about the form: if I use type="date" or "datetime" inside the input tag, the type keeps changing between text and date, but if I use "datetime-local" is shown correctly (but it displays the time too which I don't need).
This is the GIF of what happens:
https://imgur.com/YXKVAMO
This is the very bad and inefficient jQuery solution I used to "fix" the problem:
$('#DocumentDate, #LastChangeDate, #CreationDate').attr('type','date'); //when the page loads
$(document).click(function () {
if ($('#DocumentDate').not(':focus') || $('#LastChangeDate').not(':focus') || $('#CreationDate').not(':focus'))
$('#DocumentDate, #LastChangeDate, #CreationDate').attr('type','date');
});

So what is your current problem? Do you not want to use jquery to
achieve it?
I think the method you are using by jquery is implemented very well because you cannot change the date type input control to display the date format you want: link.
Another solution is to use Datepicker plugin for jquery UI, but this cannot avoid using jquery.
Here is the example based jquery UI Datepicker :
#model WebApplication_core_mvc.Models.DocumentVersionModel
#{
ViewData["Title"] = "DateEdit";
}
<h1>DateEdit</h1>
#section Scripts{
<link rel="stylesheet" href="//code.jquery.com/ui/1.12.1/themes/base/jquery-ui.css">
<script src="https://code.jquery.com/ui/1.12.1/jquery-ui.js"></script>
<script>
$(function () { // will trigger when the document is ready
$('.datepicker').datepicker();
$('.datepicker').datepicker("option", "dateFormat", "mm/dd/yy"); //Initialise any date pickers
});
</script>
}
<form asp-action="Edit">
<br />
<div class="row justify-content-center">
<div class="col-12 form-group">
<label>Titolo</label>
<input asp-for="DocumentModelId" class="form-control" />
</div>
<div class="col">
<div asp-validation-summary="ModelOnly" class="text-danger"></div>
<input type="hidden" asp-for="Id" id="iddoc" />
<div class="form-group">
<label>Data Documento</label>
#Html.TextBoxFor(model => model.CreationDate, new { #class = "form-control datepicker" })
<span asp-validation-for="CreationDate" class="text-danger"></span>
</div>
<div class="form-group">
<label>Data Creazione</label>
#Html.TextBoxFor(model => model.LastChangeDate, new { #class = "form-control datepicker" })
</div>
<div class="form-group">
<label>Data Ultima Modifica</label>
#Html.TextBoxFor(model => model.DocumentDate, new { #class = "form-control datepicker" })
</div>
</div>
</div>
</form>
Here is the result:

Related

Edit method in ASP.NET Core 6 MVC

I am building an e-store web app using ASP.NET Core 6 MVC. I am trying to do CRUD operations with the help of some tutorial, everything went smoothly, but when I try to edit a product, it makes a copy of that edited item, instead of just replacing it.
Also, it asks me to upload a new image even though I want to set it not to ask for a new one, and there is a default (noimage.png) set if there is no image uploaded. Here is the edit method, please tell me where am going wrong.
public async Task<IActionResult> Edit(int id, Product product)
{
ViewBag.CategoryId = new SelectList(_context.Category.OrderBy(x => x.Sorting),
"Id", "Name", product.CategoryId);
// if (ModelState.IsValid)
// {
product.Slug = product.Name.ToLower().Replace(" ", "-");
var slug = await _context.Product
.Where(x => x.Id != id)
.FirstOrDefaultAsync(x => x.Slug == product.Slug);
if (slug != null)
{
ModelState.AddModelError("", "The product already exists.");
return View(product);
}
// Not mandatory to upload an image when editing
if (product.ImageUpload != null)
{
string uploadsDir = Path.Combine(webHost.WebRootPath, "media/products");
// If the image is not noimage.png then remove the existing image and upload a new one
if (!string.Equals(product.Image, "noimage.png"))
{
string oldImagePath = Path.Combine(uploadsDir, product.Image);
if (System.IO.File.Exists(oldImagePath))
{
System.IO.File.Delete(oldImagePath);
}
}
// Upload new image
string imageName = Guid.NewGuid().ToString() + "_" + product.ImageUpload.FileName;
string filePath = Path.Combine(uploadsDir, imageName);
FileStream fs = new FileStream(filePath, FileMode.Create);
await product.ImageUpload.CopyToAsync(fs);
fs.Close();
product.Image = imageName;
}
_context.Update(product);
await _context.SaveChangesAsync();
TempData["Success"] = "The product has been edited!";
return RedirectToAction("Index");
// }
// return View(product);
}
Product class
public class Product
{
public int Id { get; set; }
[Required, MinLength(2, ErrorMessage = "Minimum length 2")]
public string? Name { get; set; }
public string? Slug { get; set; }
[Column(TypeName = "decimal (18,2)")]
public decimal Price { get; set; }
[Display(Name = "Category")]
[Range(1, int.MaxValue, ErrorMessage = "You must choose a category")] //specific validation for category
public int? CategoryId { get; set; }
[Required, MinLength(2, ErrorMessage = "Minimum length 4")]
public string? Description { get; set; }
public string? Image { get; set; }
//To make the connection
[ForeignKey("CategoryId")]
public virtual Category? Category { get; set; }
[NotMapped] //to not show in the DB
[ImageExtention]
public IFormFile? ImageUpload { get; set; }
}
Edit view
#model Product
#{
ViewData["Title"] = "Edit Product";
}
<h1>Edit Product</h1>
<div class="row">
<div class="col-md-10">
<form asp-action="Edit" enctype="multipart/form-data">
<div asp-validation-summary="ModelOnly" class="text-danger"></div>
#*<input type="hidden" asp-for="Image" />*#
<div class="form-group">
<label asp-for="Name" class="control-label"></label>
<input asp-for="Name" class="form-control" />
<span asp-validation-for="Name" class="text-danger"></span>
</div>
<div class="form-group">
<label asp-for="Description" class="control-label"></label>
<textarea asp-for="Description" class="form-control"></textarea>
<span asp-validation-for="Description" class="text-danger"></span>
</div>
<div class="form-group">
<label asp-for="Price" class="control-label"></label>
<input asp-for="Price" class="form-control" />
<span asp-validation-for="Price" class="text-danger"></span>
</div>
<div class="form-group">
<label asp-for="CategoryId" class="control-label"></label>
<select asp-for="CategoryId" asp-items="#ViewBag.CategoryId" class="form-control">
<option value="0">Choose a category</option>
</select>
<span asp-validation-for="CategoryId" class="text-danger"></span>
</div>
<div class="form-group">
<label asp-for="Image" class="control-label">Current Image</label>
<img src="~/media/products/#Model.Image" width="200" alt="" />
</div>
<div class="form-group">
<label asp-for="Image" class="control-label">New Product Image</label>
<input asp-for="ImageUpload" class="form-control" />
<img src="" id="imgpreview" class="pt-2" alt="" />
<span asp-validation-for="ImageUpload" class="text-danger"></span>
</div>
<div class="form-group">
<input type="submit" value="Edit" class="btn btn-primary" />
</div>
</form>
</div>
</div>
<div>
<a asp-action="Index">Back to List</a>
</div>
#section Scripts {
#{
await Html.RenderPartialAsync("_ValidationScriptsPartial");
}
<script>
$("#ImageUpload").change(function () {
readURL(this);
});
</script>
}
You need to make Id as Primary Key in Product
public class Product
{
[Key]
public int Id { get; set; }
//ommitted
}
You are not posting Id to Edit Method while Edit button clicked. So every time It is sending value of Id as 0. EntityFramework is considering it as new object.
Please add below code to cshtml view inside form. It will send correct value of existing product Id.
<input type="hidden" asp-for="Id" />

How to update value progress bar bootstrap from controller in ASP .NET Core

I have a table that receive email of my users for newsletter .I Show it on my dashboard with count .but now I want to show on progress bar and its percentage per last month
how do I do ? I create another view model for show count of some things
I can show count of them but I need show on progress bar too.
my viewmodel:
public class NewsLetterViewModel
{
public string Phone { get; set; }
public string Email { get; set; }
public DateTime CreateDate { get; set; }
}
You can try to use ViewBag to pass count and percentage.Here is a demo:
Action:
public IActionResult News()
{
ViewBag.Count = 0;
ViewBag.Percentage = 0;
return View();
}
[HttpPost]
public IActionResult News(NewsLetterViewModel n,int Count)
{
//you can pass the count and percentage with ViewBag here
ViewBag.Count= Count+ 1;
ViewBag.Percentage=25;
return View();
}
View:
<div>
Count:#ViewBag.Count
</div>
<div class="progress">
<div class="progress-bar" role="progressbar" style="width: #ViewBag.Percentage%;" aria-valuenow="#ViewBag.Percentage" aria-valuemin="0" aria-valuemax="100">#ViewBag.Percentage%</div>
</div>
<form method="post">
<input hidden name="Count" value="#ViewBag.Count" />
<div class="form-group">
<label asp-for="Phone" class="control-label"></label>
<input asp-for="Phone" class="form-control" />
<span asp-validation-for="Phone" class="text-danger"></span>
</div>
<div class="form-group">
<label asp-for="Email" class="control-label"></label>
<input asp-for="Email" class="form-control" />
<span asp-validation-for="Email" class="text-danger"></span>
</div>
<div class="form-group">
<label asp-for="CreateDate" class="control-label"></label>
<input asp-for="CreateDate" class="form-control" />
<span asp-validation-for="CreateDate" class="text-danger"></span>
</div>
<input type="submit"value="submit" />
</form>
result:

Asp-validation-summary="ModelOnly" does not work for compare data-validator

View Does displays Span validation but Validation summary(blank validation-summary div also not present).if i change Asp-validation-summary="All".I am not getting why it is not working with ModelOnly.
My Class
public class RegistrationViewModel
{
[Required]
[EmailAddress]
[MinLength(5)]
public string Email { get; set; }
[Required]
[DataType(DataType.Password)]
public string Password { get; set; }
[Required]
[DataType(DataType.Password)]
[Compare("Password")]
[Display(Name = "Confirm Password")]
public string VerifiedPassword { get; set; }
}
view
<form asp-action="Registration">
<div asp-validation-summary="ModelOnly" val class="text-danger"></div>
<div class="form-group">
<label asp-for="Email" class="control-label"></label>
<input asp-for="Email" class="form-control" />
<span asp-validation-for="Email" class="text-danger"></span>
</div>
<div class="form-group">
<label asp-for="Password" class="control-label"></label>
<input asp-for="Password" class="form-control" required />
<span asp-validation-for="Password" class="text-danger"></span>
</div>
<div class="form-group">
<label asp-for="VerifiedPassword" class="control-label"></label>
<input asp-for="VerifiedPassword" class="form-control" required/>
#*<span asp-validation-for="VerifiedPassword" class="text-danger"></span>*#
</div>
<div class="form-group">
<input type="submit" value="Register" class="btn btn-default" />
</div>
</form>
Add this to post method in the controller:
if (!ModelState.IsValid)
{
AddErrorsFromModel(ModelState.Values);
return View();
}
Also add using and AddErrorsFromModel to controller:
using Microsoft.AspNetCore.Mvc.ModelBinding;
private void AddErrorsFromModel(ModelStateDictionary.ValueEnumerable values)
{
foreach (ModelStateEntry modelState in values)
foreach (ModelError error in modelState.Errors)
{
ModelState.AddModelError(string.Empty, error.ErrorMessage.ToString());
}
}

ASP.NET Core model binding

I'm trying to bind an edit action to a model which doesn't work. Here below the controller:
[Route("[controller]/[action]")]
public class CustomerController : Controller
{
private readonly IUnitOfWork _unitOfWork;
public CustomerController(IUnitOfWork unitOfWork)
{
_unitOfWork = unitOfWork;
}
[HttpGet("{_customerCode}")]
public IActionResult Edit(int _customerCode)
{
var customer = _unitOfWork.Customers.getCustomerByCode(_customerCode);
var customerDTO = Mapper.Map<CustomerModelDTO>(customer);
return View(customerDTO);
}
[HttpPost]
public IActionResult Edit(CustomerModelDTO model)
{
if (!ModelState.IsValid)
{
return View(model);
}
var _customer = _unitOfWork.Customers.getCustomerByCode(model.CustomerCode);
if (_customer==null)
{
return NotFound();
}
Mapper.Map(model, _customer);
_unitOfWork.Complete();
return RedirectToAction("Detail", new { customerCode = _customer.CustomerCode });
}
}
And here is the view I am using:
#model Almiz.Dtos.CustomerModelDTO
<form asp-action="Edit" method="post">
<div class="form-horizontal">
<h4>CustomerModelDTO</h4>
<hr />
<div asp-validation-summary="ModelOnly" class="text-danger"></div>
<div class="form-group">
<label asp-for="CustomerCode" class="col-md-2 control-label"></label>
<div class="col-md-10">
<input asp-for="CustomerCode" class="form-control" />
<span asp-validation-for="CustomerCode" class="text-danger" />
</div>
</div>
<div class="form-group">
<label asp-for="FirstName" class="col-md-2 control-label"></label>
<div class="col-md-10">
<input asp-for="FirstName" class="form-control" />
<span asp-validation-for="FirstName" class="text-danger" />
</div>
</div>
<div class="form-group">
<label asp-for="MiddleName" class="col-md-2 control-label"></label>
<div class="col-md-10">
<input asp-for="MiddleName" class="form-control" />
<span asp-validation-for="MiddleName" class="text-danger" />
</div>
</div>
<div class="form-group">
<label asp-for="LastName" class="col-md-2 control-label"></label>
<div class="col-md-10">
<input asp-for="LastName" class="form-control" />
<span asp-validation-for="LastName" class="text-danger" />
</div>
</div>
<div class="form-group">
<label asp-for="Telephone" class="col-md-2 control-label"></label>
<div class="col-md-10">
<input asp-for="Telephone" class="form-control" />
<span asp-validation-for="Telephone" class="text-danger" />
</div>
</div>
<div class="form-group">
<label asp-for="Mobile" class="col-md-2 control-label"></label>
<div class="col-md-10">
<input asp-for="Mobile" class="form-control" />
<span asp-validation-for="Mobile" class="text-danger" />
</div>
</div>
<div class="form-group">
<label asp-for="Email" class="col-md-2 control-label"></label>
<div class="col-md-10">
<input asp-for="Email" class="form-control" />
<span asp-validation-for="Email" class="text-danger" />
</div>
</div>
<div class="form-group">
<div class="col-md-offset-2 col-md-10">
<input type="submit" value="Save" class="btn btn-default" />
</div>
</div>
</div>
</form>
<div>
<a asp-action="Index">Back to Index</a>
</div>
#section Scripts {
#{await Html.RenderPartialAsync("_ValidationScriptsPartial");}
}
When I call the view and with the ID http://localhost:65001/Customer/Edit/1001, I get the all the information. However when I edit it and post the edited form I get resource not found error. Please help. Here below the model:
public class CustomerModelDTO
{
public int CustomerCode { get; set; }
public string FirstName { get; set; }
public string MiddleName { get; set; }
public string LastName { get; set; }
public double Telephone { get; set; }
public double Mobile { get; set; }
public string Email { get; set; }
}
I got it working. Here below the change.
[HttpPost("{customerCode}")]
public IActionResult Edit(int customerCode, CustomerModelDTO data)

Display Friendly Error Message When using Decorators in Models

New to ASP.NET Core.
I have a view:
<form asp-action="LogIn">
<div class="form-horizontal">
<hr />
<div asp-validation-summary="ModelOnly" class="text-danger"></div>
<div class="form-group">
<label asp-for="EmailAddress" class="col-md-2 control-label"></label>
<div class="col-md-10">
<input asp-for="EmailAddress" class="form-control" style="min-width:350px" />
<span asp-validation-for="EmailAddress" class="text-danger"></span>
</div>
</div>
<div class="form-group">
<label asp-for="Password" class="col-md-2 control-label"></label>
<div class="col-md-10">
<input asp-for="Password" class="form-control" style="min-width:350px" />
<span asp-validation-for="Password" class="text-danger"></span>
</div>
</div>
<div class="form-group">
<div class="col-md-offset-2 col-md-10">
<input type="submit" value="Enter" class="btn btn-default" />
</div>
</div>
</div>
</form>
I have this Model for it:
public class Subscription
{
[Key]
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
public long SubscriptionId { get; set; }
[Display(Name = "Email Address")]
[Required]
[RegularExpression(#"\w+([-+.']\w+)*#\w+([-.]\w+)*\.\w+([-.]\w+)*")]
public string EmailAddress { get; set; }
[NotMapped]
public string Password { get; set; }
}
So, when a User types in an email address that is not validated by the regular expression I get the error message:
How do i over-ride this 'default' error message to say (for example):
Email Address is Invalid
?
You need to add ErrorMessage property to the RegularExpression attribute like this-
[RegularExpression(#"\w+([-+.']\w+)*#\w+([-.]\w+)*\.\w+([-.]\w+)*", ErrorMessage = "Your friendly message goes here")]

Resources