So I'm trying to create a basic register page where the user needs to fill out all the fields in order to proceed. But the issue I'm facing is that when I click "Create" to finish the process, it just refreshes the page and there are no errors shown, even if I don't fill out any of the fields.
This is the Controller
public IActionResult Register()
{
return View();
}
This is the form
<form>
<div asp-validation-summary="All" class="text-danger"></div>
<div class="form-group">
<label asp-for="Username" class="control-label"></label>
<input asp-for="Username" class="form-control" />
<span asp-validation-for="Username" class="text-danger"></span>
</div>
<div class="form-group">
<label asp-for="Password" class="control-label"></label>
<input asp-for="Password" class="form-control" />
<span asp-validation-for="Password" class="text-danger"></span>
</div>
<div class="form-group">
<label asp-for="ConfirmPassword" class="control-label"></label>
<input asp-for="ConfirmPassword" class="form-control" />
<span asp-validation-for="ConfirmPassword" class="text-danger"></span>
</div>
<div class="form-group form-check">
<label class="form-check-label">
<input class="form-check-input" asp-for="Agree" /> #Html.DisplayNameFor(model => model.Agree)
</label>
</div>
<div class="form-group">
<input type="submit" value="Create" class="btn btn-primary" />
</div>
</form>
And obviously the model
public class RegisterAccountModel
{
[Required(ErrorMessage = "This field is required.")]
[StringLength(60, MinimumLength = 3)]
[DataType(DataType.Text)]
public string Username { get; set; }
[Required(ErrorMessage = "This field is required.")]
[DataType(DataType.Password)]
public string Password { get; set; }
[Required(ErrorMessage = "This field is required.")]
[DataType(DataType.Password)]
[Compare("Password", ErrorMessage = "The Password and Confirm password must match.")]
public string ConfirmPassword { get; set; }
[Required(ErrorMessage = "This field is required.")]
//[DataType(DataType.Password)]
public bool Agree { get; set; }
}
Why is the page just refreshing without showing any errors when I click the "Create" button?
You need to use the model state valid to start the valuation
public IActionResult Register()
{
if (ModelState.IsValid) { //checking model state
// send to the original action
return RedirectToAction("Index");
}
return View();
}
Related
I'm quite new to ASP.NET Core MVC and I'm having trouble retrieving a DateTime value from the database into the 'Edit' razor view.
I can use the scaffolded views to create a new Activity Item and this displays correctly in the 'Index' list, and in the 'Details' view, but when I attempt to 'Edit' the entry the DateTime value doesn't pull through from the database.
I've done plenty of reading but the main thing I seem to get in search results is information about JQuery Datepickers.
Any advice on where to look, how to resolve would be very much appreciated.
Here is my model:
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.ComponentModel.DataAnnotations;
using System.ComponentModel.DataAnnotations.Schema;
namespace MISDataRepo.Models
{
[Table("Activity",Schema = "coir")]
public partial class ActivityItem
{
public ActivityItem()
{
ActivityIdentifier = new HashSet<ActivityIdentifier>();
}
[Key]
public int ActivityItemId { get; set; }
[Required(ErrorMessage = "A valid Activity Name is required.")]
[Display(Name = "Activity Name")]
[StringLength(100)]
public string ActivityName { get; set; }
[Required]
[Display(Name = "Activity Type")]
public int ActivityTypeId { get; set; }
[Required]
[Display(Name = "Date Activity Created")]
[DataType(DataType.Date)]
[DisplayFormat(DataFormatString = "{0:dd/MM/yyyy}", ApplyFormatInEditMode = true)]
public DateTime DateCreated { get; set; }
[Display(Name = "Date Activity Modified")]
[DataType(DataType.Date)]
[DisplayFormat(DataFormatString = "{0:dd/MM/yyyy}", ApplyFormatInEditMode = true)]
public DateTime? DatetModified { get; set; }
[Required]
[Display(Name = "Created By (Employee ID)")]
[RegularExpression("^[1-9][0-9]{6}$", ErrorMessage = "A valid Employee ID is required!")]
public int? CreatedBy { get; set; }
[Display(Name = "Project Co-Ordinator (Employee ID)")]
[RegularExpression("^[1-9][0-9]{6}$", ErrorMessage = "A valid Employee ID is required!")]
public int? PC { get; set; }
[DefaultValue(true)]
public bool Live { get; set; }
public virtual ActivityType ActivityType { get; set; }
public virtual ICollection<ActivityIdentifier> ActivityIdentifier { get; set; }
}
}
Here is the view:
#model MISDataRepo.Models.ActivityItem
#{
ViewData["Title"] = "Edit";
Layout = "~/Views/Shared/_Layout.cshtml";
}
<h1>Edit</h1>
<h4>ActivityItem</h4>
<hr />
<div class="row">
<div class="col-md-4">
<form asp-action="Edit">
<div asp-validation-summary="ModelOnly" class="text-danger"></div>
<input type="hidden" asp-for="ActivityItemId" />
<div class="form-group">
<label asp-for="ActivityName" class="control-label"></label>
<input asp-for="ActivityName" class="form-control" />
<span asp-validation-for="ActivityName" class="text-danger"></span>
</div>
<div class="form-group">
<label asp-for="ActivityTypeId" class="control-label"></label>
<select asp-for="ActivityTypeId" class="form-control" asp-items="ViewBag.ActivityTypeId"></select>
<span asp-validation-for="ActivityTypeId" class="text-danger"></span>
</div>
<div class="form-group">
<label asp-for="DateCreated" class="control-label"></label>
<input asp-for="#Html.DisplayFor(a => a.DateCreated)" class="form-control" />
<span asp-validation-for="DateCreated" class="text-danger"></span>
#*<input type="hidden" asp-for="DateCreated" type="date" placeholder="Enter Date Created" value="#Model.DateCreated" />*#
</div>
<div class="form-group">
<label asp-for="DatetModified" class="control-label"></label>
<input asp-for="#Html.DisplayFor(a => a.DatetModified)" class="form-control" />
<span asp-validation-for="DatetModified" class="text-danger"></span>
</div>
<div class="form-group">
<label asp-for="CreatedBy" class="control-label"></label>
<input asp-for="CreatedBy" class="form-control" />
<span asp-validation-for="CreatedBy" class="text-danger"></span>
</div>
<div class="form-group">
<label asp-for="PC" class="control-label"></label>
<input asp-for="PC" class="form-control" />
<span asp-validation-for="PC" class="text-danger"></span>
</div>
<div class="form-group form-check">
<label class="form-check-label">
<input class="form-check-input" asp-for="Live" /> #Html.DisplayNameFor(model => model.Live)
</label>
</div>
<div class="form-group">
<input type="submit" value="Save" class="btn btn-primary" />
</div>
</form>
</div>
</div>
<div>
<a asp-action="Index">Back to List</a>
</div>
#section Scripts {
#{await Html.RenderPartialAsync("_ValidationScriptsPartial");}
}
Here are the 'Edit' methods of the controller
// GET: ActivityItems/Edit/5
public async Task<IActionResult> Edit(int? id)
{
if (id == null)
{
return NotFound();
}
var activityItem = await _context.ActivityItem.FindAsync(id);
if (activityItem == null)
{
return NotFound();
}
ViewData["ActivityTypeId"] = new SelectList(_context.ActivityType, "ActivityTypeId", "ActivityTypeName", activityItem.ActivityTypeId);
return View(activityItem);
}
// POST: ActivityItems/Edit/5
// To protect from overposting attacks, enable the specific properties you want to bind to, for
// more details, see http://go.microsoft.com/fwlink/?LinkId=317598.
[HttpPost]
[ValidateAntiForgeryToken]
//public async Task<IActionResult> Edit(int id, [Bind("ActivityItemId,ActivityName,ActivityTypeId,DateCreated,DatetModified,CreatedBy,PC,Live")] ActivityItem activityItem)
public async Task<IActionResult> Edit(int id, [Bind("ActivityItemId,ActivityName,ActivityTypeId,DatetModified,CreatedBy,PC,Live")] ActivityItem activityItem)
{
if (id != activityItem.ActivityItemId)
{
return NotFound();
}
if (ModelState.IsValid)
{
try
{
_context.Update(activityItem);
await _context.SaveChangesAsync();
}
catch (DbUpdateConcurrencyException)
{
if (!ActivityItemExists(activityItem.ActivityItemId))
{
return NotFound();
}
else
{
throw;
}
}
return RedirectToAction(nameof(Index));
}
ViewData["ActivityTypeId"] = new SelectList(_context.ActivityType, "ActivityTypeId", "ActivityTypeName", activityItem.ActivityTypeId);
return View(activityItem);
}
But when I attempt to 'Edit' the entry the DateTime value doesn't pull
through from the database.
Yes, the issue you are having with the your View is pretty obvious due to your HTML Helper atrribute that is #Html.DisplayFor and the Property you have defined within your Model ActivityItem. You are probably getting following issue.
Problem:
How To Resolve:
Either you could use ViewModel or you can redefine your property public DateTime DateCreated { get; set; } by get rid of your annotations. However, I would prefer to use ViewModel. On the other hands, use the property like asp-for="DateCreated" within your edit view and get rid of your additional HTML helper class #Html.DisplayFor. Follow the below steps.
View Model:
public class ActivityItemViewModel
{
public int ActivityItemId { get; set; }
public string ActivityName { get; set; }
public DateTime DateCreated { get; set; }
public DateTime? DatetModified { get; set; }
}
Note: While loading your Edit view you certainly doesn't require annotations so you can ommit that.
View :
In view you are using additional HTML helper class #Html.DisplayFor which is not required in this scenario. You could try as following:
#model DotNet6MVCWebApp.Models.ActivityItemViewModel
#{
ViewData["Title"] = "Edit";
}
<h1>Edit</h1>
<h4>ActivityItem</h4>
<hr />
<div class="row">
<div class="col-md-4">
<form asp-action="Edit">
<div asp-validation-summary="ModelOnly" class="text-danger"></div>
<input type="hidden" asp-for="ActivityItemId" />
<div class="form-group">
<label asp-for="ActivityName" class="control-label"></label>
<input asp-for="ActivityName" class="form-control" />
<span asp-validation-for="ActivityName" class="text-danger"></span>
</div>
<div class="form-group">
<label asp-for="DateCreated" class="control-label"></label>
<input asp-for="DateCreated" class="form-control" />
<span asp-validation-for="DateCreated" class="text-danger"></span>
</div>
<div class="form-group">
<label asp-for="DatetModified" class="control-label"></label>
<input asp-for="DateCreated" class="form-control" />
<span asp-validation-for="DatetModified" class="text-danger"></span>
</div>
<div class="form-group">
<input type="submit" value="Save" class="btn btn-primary" />
</div>
</form>
</div>
</div>
<div>
<a asp-action="ActivityList">Back to List</a>
</div>
Output:
I've been away from ASP.NET MVC for a while so forgotten some of the basics.
I have scoured SO for an answer, but none really seem to apply/work so this may seem like a duplicate question but it's really not, perhaps I just can't see the wood through the trees. I know I'm missing something obvious but cant remember what
I have a partial that I pass the model to that updates a property on the model (AddressDetails & ContactDetails).
Main page
<form asp-action="Create">
<div asp-validation-summary="ModelOnly" class="text-danger"></div>
<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
#await Html.PartialAsync("../AddressDetails/Create.cshtml", Model)
#await Html.PartialAsync("../ContactDetails/Create.cshtml", Model)
<div class="form-group">
<input type="submit" value="Create" class="btn btn-primary" />
</div>
</form>
And partial page
#model CareHome.Models.CareHomes
<div class="form-group">
<h4>AddressDetails</h4>
<hr />
</div>
<div asp-validation-summary="ModelOnly" class="text-danger"></div>
<div class="form-group">
<label asp-for="AddressDetails.NumberStreetName" class="control-label"></label>
<input asp-for="AddressDetails.NumberStreetName" class="form-control" />
<span asp-validation-for="AddressDetails.NumberStreetName" class="text-danger"></span>
</div>
<div class="form-group">
<label asp-for="AddressDetails.Locality" class="control-label"></label>
<input asp-for="AddressDetails.Locality" class="form-control" />
<span asp-validation-for="AddressDetails.Locality" class="text-danger"></span>
</div>
<div class="form-group">
<label asp-for="AddressDetails.Town" class="control-label"></label>
<input asp-for="AddressDetails.Town" class="form-control" />
<span asp-validation-for="AddressDetails.Town" class="text-danger"></span>
</div>
<div class="form-group">
<label asp-for="AddressDetails.PostCode" class="control-label"></label>
<input asp-for="AddressDetails.PostCode" class="form-control" />
<span asp-validation-for="AddressDetails.PostCode" class="text-danger"></span>
</div>
This is working fine when I post data back to the controller
However, I want to reuse the partial which means I want to replace
#model CareHome.Models.CareHomes
in the partial with the property class (see further below) that the model uses.
So when I change it to
main
<div class="row">
<div class="col-md-4">
<form asp-action="Create">
<div asp-validation-summary="ModelOnly" class="text-danger"></div>
<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
#await Html.PartialAsync("../AddressDetails/Create.cshtml", Model.AddressDetails)
#await Html.PartialAsync("../ContactDetails/Create.cshtml", Model.ContactInfo)
<div class="form-group">
<input type="submit" value="Create" class="btn btn-primary" />
</div>
</form>
</div>
</div>
note that im passing the property through to the partial now not the model
#model CareHome.Models.AddressDetails
<div class="form-group">
<h4>AddressDetails</h4>
<hr />
</div>
<div asp-validation-summary="ModelOnly" class="text-danger"></div>
<div class="form-group">
<label asp-for="NumberStreetName" class="control-label"></label>
<input asp-for="NumberStreetName" class="form-control" />
<span asp-validation-for="NumberStreetName" class="text-danger"></span>
</div>
<div class="form-group">
<label asp-for="Locality" class="control-label"></label>
<input asp-for="Locality" class="form-control" />
<span asp-validation-for="Locality" class="text-danger"></span>
</div>
<div class="form-group">
<label asp-for="Town" class="control-label"></label>
<input asp-for="Town" class="form-control" />
<span asp-validation-for="Town" class="text-danger"></span>
</div>
<div class="form-group">
<label asp-for="PostCode" class="control-label"></label>
<input asp-for="PostCode" class="form-control" />
<span asp-validation-for="PostCode" class="text-danger"></span>
</div>
iv now changed the partial to use
#model CareHome.Models.AddressDetails
but when I post this to the controller it comes back null
I tried a million variations on the binding
// POST: CareHomes/Create
// To protect from overposting attacks, enable the specific properties you want to bind to.
// For more details, see http://go.microsoft.com/fwlink/?LinkId=317598.
[HttpPost]
[ValidateAntiForgeryToken]
//Create([Bind("CareHomesId,Name,ContactName,ContactNumber")] CareHomes careHomes)
public async Task<IActionResult> Create([Bind( "CareHomes,AddressDetails,ContactDetails")] CareHomes careHomes)
{
if (ModelState.IsValid)
{
_context.Add(careHomes);
await _context.SaveChangesAsync();
return RedirectToAction(nameof(Index));
}
ViewData["AddressDetailsId"] = new SelectList(_context.AddressDetails, "AddressDetailsId", "NumberStreetName", careHomes.AddressDetailsId);
ViewData["ContactDetailsId"] = new SelectList(_context.ContactDetails, "ContactDetailsId", "ContactName", careHomes.ContactDetailsId);
return View(careHomes);
}
but when I evaluate the ModelState I can see it's always missing. As the propertys of the model bind ok when i pass the model though why do they then not bind when i pass the property though
my classes are like so
using System.ComponentModel.DataAnnotations;
using System.ComponentModel.DataAnnotations.Schema;
namespace CareHome.Models
{
public class CareHomes
{
[Required]
[Key, DatabaseGenerated(DatabaseGeneratedOption.Identity)]
public int CareHomesId { get; set; }
[Required]
[Column(TypeName = "VARCHAR(256)")]
[StringLength(256, MinimumLength = 3)]
public string Name { get; set; }
public int? AddressDetailsId { get; set; }
public AddressDetails AddressDetails { get; set; }
public int? ContactDetailsId { get; set; }
public ContactDetails ContactInfo { get; set; }
public ICollection<Staff>? StaffMembers { get; set; }
}
}
and one of the properties in question
using System.ComponentModel.DataAnnotations;
using System.ComponentModel.DataAnnotations.Schema;
namespace CareHome.Models
{
public class AddressDetails
{
[Required]
[Key, DatabaseGenerated(DatabaseGeneratedOption.Identity)]
public int AddressDetailsId { get; set; }
[Required]
[Column(TypeName = "VARCHAR(256)")]
[StringLength(256, MinimumLength = 3)]
[Display(Name = "House No & Street Name")]
public string NumberStreetName { get; set; }
[Column(TypeName = "VARCHAR(256)")]
[StringLength(256, MinimumLength = 3)]
public string? Locality { get; set; }
[Required]
[Column(TypeName = "VARCHAR(256)")]
[StringLength(256, MinimumLength = 3)]
public string Town { get; set; }
[Required]
[Column(TypeName = "VARCHAR(16)")]
[StringLength(16, MinimumLength = 4)]
[RegularExpression(#"^(([A-Z]{1,2}\d[A-Z\d]?|ASCN|STHL|TDCU|BBND|[BFS]IQQ|PCRN|TKCA) ?\d[A-Z]{2}|BFPO ?\d{1,4}|(KY\d|MSR|VG|AI)[ -]?\d{4}|[A-Z]{2} ?\d{2}|GE ?CX|GIR ?0A{2}|SAN ?TA1)$", ErrorMessage = "Please enter a valid UK post code in upper case")]
public string PostCode { get; set; }
public CareHomes? CareHomes { get; set; }
}
}
I have tried adding bind animations like
[BindProperty]
to the property and adding hidden fields in the partual
#Html.HiddenFor(m => m.AddressDetailsId)
#Html.HiddenFor(m => m.AddressDetails)
As per some suggestions from some of the many SO searches I did, but no dice....so please...what am I missing?
I even tried #html.EditorFor but that seems to have the same problem
EDIT
Using #Jonesopolis suggestion I can see from the form being posted back when it uses the model:
?this.Request.Form.ToArray()
{System.Collections.Generic.KeyValuePair<string, Microsoft.Extensions.Primitives.StringValues>[11]}
...
[4]: {[AddressDetails.CareHomes, {}]}
[5]: {[AddressDetailsId, {}]}
[6]: {[AddressDetails.NumberStreetName, {sad}]}
[7]: {[AddressDetails.Locality, {sad}]}
[8]: {[AddressDetails.Town, {wales}]}
[9]: {[AddressDetails.PostCode, {CF83 8RD}]}
vs when i pass the property
?this.Request.Form.ToArray()
{System.Collections.Generic.KeyValuePair<string, Microsoft.Extensions.Primitives.StringValues>[11]}
...
[4]: {[CareHomes, {}]}
[5]: {[AddressDetailsId, {0}]}
[6]: {[NumberStreetName, {test street}]}
[7]: {[Locality, {}]}
[8]: {[Town, {wales}]}
[9]: {[PostCode, {CF83 8RD}]}
so clearly the "AddressDetails" is missing so MVC cant map the propery to the CareHomes class object on the binding because the property name is missing. So i know what the issue is not how to fix it though, How do I set the property name on the partual propertys so they map back to the parent object class. I though about a costom binder but not having much luck figuring that one out.
On a side note, intrestingly enough if in the partent model I do this :
#Html.EditorFor(m => m.AddressDetails.NumberStreetName)
then bind like so
public async Task<IActionResult> Create([Bind(include: "CareHomes,AddressDetails")] CareHomes careHomes)
I can at least get the EditorFor to pull though on the parent
Finally worked it out, seems model binding wasn't the issue, I just had to set the id and name properties on the form controls in the partial to match that of the object on the model action, e.g. id="AddressDetails_NumberStreetName" name="AddressDetails.NumberStreetName"
so adding
<div class="form-group">
<label asp-for="NumberStreetName" class="control-label"></label>
<input asp-for="NumberStreetName" id="AddressDetails_NumberStreetName" name="AddressDetails.NumberStreetName" class="form-control" />
<span asp-validation-for="NumberStreetName" class="text-danger"></span>
</div>
<div class="form-group">
<label asp-for="Locality" class="control-label"></label>
<input asp-for="Locality" id="AddressDetails_Locality" name="AddressDetails.Locality" class="form-control" />
<span asp-validation-for="Locality" class="text-danger"></span>
</div>
<div class="form-group">
<label asp-for="Town" class="control-label"></label>
<input asp-for="Town" id="AddressDetails_Town" name="AddressDetails.Town" class="form-control" />
<span asp-validation-for="Town" class="text-danger"></span>
</div>
<div class="form-group">
<label asp-for="Postcode" class="control-label"></label>
<input asp-for="Postcode" id="AddressDetails_Postcode" name="AddressDetails.Postcode" class="form-control" />
<span asp-validation-for="Postcode" class="text-danger"></span>
</div>
allows it to properly map to the contoller model
public async Task<IActionResult> Create([Bind(include: "CareHomes, Name,AddressDetails, ContactInfo")] CareHomes careHomes)
I worked it out when I put the partial mark up in the main form and looked at the HTML markup and compared it to when it was a partial. I hope this helps someone else someday
A modal I am working on has some very strange behavior when testing. When the "Email" field fails validation, it clears the "Comment" text area, but leaves all other inputs alone and does not clear them.
Below is the modal page:
#model Project_Name.ViewModels.ProspectViewModel
<!-- Modal -->
<div class="modal fade" id="add-prospect" tabindex="-1" role="dialog" aria-labelledby="addProspectLabel" aria-hidden="true">
<div class="modal-dialog" role="document">
<div class="modal-content">
<form asp-action="Prospect">
<input name="IsValid" type="hidden" value="#ViewData.ModelState.IsValid.ToString()" />
<div class="modal-header">
<h5 class="modal-title" id="addContactLabel">Add Prospect</h5>
<button type="button" class="close" data-dismiss="modal" aria-label="Close">
<span aria-hidden="true">×</span>
</button>
</div>
<div class="modal-body">
<div class="form-group">
<label asp-for="firstName"></label>
<input asp-for="firstName" class="form-control" />
<span asp-validation-for="firstName" class="text-danger"></span>
</div>
<div class="form-group">
<label asp-for="lastName"></label>
<input asp-for="lastName" class="form-control" />
<span asp-validation-for="lastName" class="text-danger"></span>
</div>
<div class="form-group">
<label asp-for="companyName"></label>
<input asp-for="companyName" class="form-control" />
<span asp-validation-for="companyName" class="text-danger"></span>
</div>
<div class="form-group">
<label asp-for="email"></label>
<input type="email" asp-for="email" class="form-control" />
<span asp-validation-for="email" class="text-danger"></span>
</div>
<div class="form-group">
<label asp-for="cellPhone"></label>
<input type="tel" asp-for="cellPhone" class="form-control" />
<span asp-validation-for="cellPhone" class="text-danger"></span>
</div>
<div class="form-group">
<label asp-for="comment"></label>
<textarea asp-for="comment" class="form-control" />
<span asp-validation-for="comment" class="text-danger"></span>
</div>
</div>
<div class="modal-footer">
<button type="button" class="btn btn-secondary" data-dismiss="modal">Close</button>
<button type="button" class="btn btn-default" data-save="modal">Save</button>
</div>
</form>
</div>
</div>
</div>
Here is the model it uses, if that helps:
public class ProspectViewModel
{
public ProspectViewModel()
{
}
public Guid id { get; set; }
public string userId { get; set; }
[Display(Name = "Company Name")]
public string companyName { get; set; }
[Required(ErrorMessage = "First Name is Required")]
[Display(Name ="First Name")]
public string firstName { get; set; }
[Required(ErrorMessage = "Last Name is Required")]
[Display(Name = "Last Name")]
public string lastName { get; set; }
[EmailAddress]
[Display(Name = "Email")]
public string email { get; set; }
[Phone]
[Display(Name = "Cell Phone")]
public string cellPhone { get; set; }
[Display(Name = "Note")]
public string comment { get; set; }
[Display(Name = "Connect Meeting")]
public DateTime? meetingDate { get; set; }
public DateTime createdDate { get; set; }
public string createdBy { get; set; }
public bool isActive { get; set; }
}
Ive cant seem to find any other info on this issue. Thank you in advance.
EDIT: Here is the controller code that opens and posts the modal:
public IActionResult Prospect()
{
var model = new ProspectViewModel();
return PartialView("_AddProspect", model);
}
[HttpPost]
public IActionResult Prospect(ProspectViewModel model)
{
if(model.email == null && model.cellPhone == null)
{
ModelState.AddModelError("email", "Email or Cell Phone is required!");
}
if (ModelState.IsValid)
{
var userId = _userManager.GetUserId(User);
var user = User.Identity.Name;
var prospect = _mapper.Map<Prospect>(model);
prospect.createdDate = DateTime.Now;
prospect.createdBy = user;
prospect.modifiedDate = DateTime.Now;
prospect.modifiedBy = user;
prospect.userId = userId;
prospect.isActive = true;
_unitOfWork.Prospects.Add(prospect);
_unitOfWork.Complete();
TempData["SuccessMessage"] = "Prospect successfully added!";
}
return PartialView("_AddProspect", model);
}
So I'm still unsure WHY it did this, but it appears that since the "comment" field was a text-area element and not an input, and every failed validation of the other input fields would clear the text-area but not the other inputs. When I changed the element to be an , it stopped clearing on other inputs' failed validation.
Here is the code for Edit:
[HttpGet]
public async Task<IActionResult> Edit(string id)
{
var roles = _roleManager.Roles.ToList();
ViewBag.Roles = new SelectList(roles, "Id", "Name");
ApplicationUser user = await _userManager.FindByIdAsync(id);
if (user == null)
{
ViewBag.ErrorMessage = $"User with Id = {id} cannot be found";
return NotFound();
}
var userRole = _userManager.GetRolesAsync(user).ToString();
var userViewModel = new EditUserViewModel
{
UserId=user.Id,
Username=user.UserName,
FirstName=user.FirstName,
LastName=user.LastName,
Email=user.Email,
UserPIN=user.UserPIN,
Address=user.Address,
TelephoneNumber=user.PhoneNumber,
Roles = userRole
};
return View(userViewModel);
}
[HttpPost]
public async Task<IActionResult> Edit(EditUserViewModel userViewModel)
{
var user = await _userManager.FindByIdAsync(userViewModel.UserId);
if (user == null)
{
ViewBag.ErrorMessage = $"User with Id = {userViewModel.UserId} cannot be found";
return NotFound();
}
else
{
user.UserName = userViewModel.Username;
user.FirstName = userViewModel.FirstName;
user.LastName = userViewModel.LastName;
user.Email = userViewModel.Email;
user.UserPIN = userViewModel.UserPIN;
user.Address = userViewModel.Address;
user.PhoneNumber = userViewModel.TelephoneNumber;
var result = await _userManager.UpdateAsync(user);
_dbContext.SaveChanges();
if (result.Succeeded)
{
return RedirectToAction("Index");
}
foreach (var error in result.Errors)
{
ModelState.AddModelError("", error.Description);
}
return View(userViewModel);
}
}
This is my view model for Edit:
using System;
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations;
using System.Linq;
using System.Threading.Tasks;
namespace Project.Models.UserViewModels
{
public class EditUserViewModel
{
public string UserId { get; set; }
[Required]
[StringLength(50, MinimumLength = 6,
ErrorMessage = "Username cannot be shorter than 6 characters and longer than 50.")]
public string Username { get; set; }
[Required]
public string FirstName { get; set; }
[Required]
public string LastName { get; set; }
[Required]
[EmailAddress]
[Display(Name = "Email")]
public string Email { get; set; }
[Required]
[StringLength(10, MinimumLength = 10,
ErrorMessage = "User PIN must be 10 characters long")]
public string UserPIN { get; set; }
[Required]
[StringLength(10, MinimumLength = 10,
ErrorMessage = "Telephone number must be 10 characters long")]
public string TelephoneNumber { get; set; }
[Required]
public string Address { get; set; }
[Required]
public string Roles { get; set; }
}
}
And this is my View:
#model Project.Models.UserViewModels.EditUserViewModel
<h1>Edit User</h1>
<div class="col-md-4">
<form asp-action="Edit" asp-controller="Admin" method="post">
<div asp-validation-summary="ModelOnly" class="text-danger"></div>
<div class="form-group">
<label asp-for="FirstName"> First name</label>
<input asp-for="FirstName" class="form-control" />
<span asp-validation-for="FirstName" class="text-danger"></span>
</div>
<div class="form-group">
<label asp-for="LastName"> Last name</label>
<input asp-for="LastName" class="form-control" />
<span asp-validation-for="LastName" class="text-danger"></span>
</div>
<div class="form-group">
<label asp-for="Username"> Username</label>
<input asp-for="Username" class="form-control" />
<span asp-validation-for="Username" class="text-danger"></span>
</div>
<div class="form-group">
<label asp-for="UserPIN">User PIN</label>
<input asp-for="UserPIN" class="form-control" />
<span asp-validation-for="UserPIN" class="text-danger"></span>
</div>
<div class="form-group">
<label asp-for="Email"></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="TelephoneNumber"></label>
<input asp-for="TelephoneNumber" class="form-control" />
<span asp-validation-for="TelephoneNumber" class="text-danger"></span>
</div>
<div class="form-group">
<label asp-for="Roles">Role</label>
<select asp-for="Roles" class="form-control" asp-items="#ViewBag.Roles"></select>
<span asp-validation-for="Roles" class="text-danger"></span>
</div>
<div class="form-group">
<label asp-for="Address">Address</label>
<input asp-for="Address" class="form-control" />
<span asp-validation-for="Address" class="text-danger"></span>
</div>
<div class="form-group">
<input type="submit" value="Save" class="btn btn-primary" />
</div>
</form>
</div>
<div>
<a asp-action="Index">Back to User list</a>
</div>
I create an asp.net core application using Identity. Now I am customizing the project. I wrote AddUser and role and Delete Actions and they are working.
I get HTTP ERROR 404 exception when I try to edit my user in UserList.
I add _dbContext.SaveChanges(), but it is still not working. The rest CRUD operations are working properly like I said earlier.
The code that saves your model depends on the presence of the userViewModel.UserId information. But this is not present in the .cshtml code. So when the model is passed back to the controller in the HttpPost there is no data and the _userManager.FindByIdAsync will not find any user. The fix is simple, just add somewhere in the .cshtml an hidden field for the UserId
<form asp-action="Edit" asp-controller="Admin" method="post">
<div asp-validation-summary="ModelOnly" class="text-danger"></div>
<div> <input asp-for="UserId" class="d-none" /></div>
....
It is an hard fact to remember that the istance of the ViewModel passed to the view from the HttpGet action is not the same instance passed back to the HttpPost action. It is a new ViewModel instance and it is populated from the fields present in the View. If you forget a field then there is no value in the model passed back to the post action.
You have a fairly explicit error there in that 404 NotFound.
There's either a mismatch between how you're creating and storing the user, or in how you're attempting to retrieve it from _userManager, particularly:
_userManager.FindByIdAsync(userViewModel.UserId)
When debugging the Edit action, does the view model property UserId have a value?
If so, does the value match what UserId is set to when the user is created?
Either modify the data at creation, or supply more data during editing, to create a succesful match
I am facing a problem submitting a form in ASP.NET-core 2.2.1 MVC. I realized that the form does not submit when I click on the submit button. I have confirmed that the validation token is there and I have indicated the action path.
I generated the form from a ViewModel. Here is the view Model below
public class SignUpViewModel
{
[Required]
public string Surname { get; set; }
[Required,Display(Name ="Other Names")]
public string Othernames { get; set; }
[EmailAddress,Required,Display(Name ="Email Address")]
public string EmailAddress { get; set; }
[Display(Name ="Phone Number"),DataType(DataType.PhoneNumber),MinLength(11,ErrorMessage ="Phone number should be a valid GSM Mobile number"),Required]
public string PhoneNumber { get; set; }
[Required]
[StringLength(100, ErrorMessage = "The {0} must be at least {2} and at max {1} characters long.", MinimumLength = 6)]
[DataType(DataType.Password)]
[Display(Name = "Password")]
public string Password { get; set; }
[DataType(DataType.Password)]
[Display(Name = "Confirm password")]
[Compare("Password", ErrorMessage = "The password and confirmation password do not match.")]
public string ConfirmPassword { get; set; }
public Gender Gender { get; set; }
[Display(Name ="State of Residence"),Required]
public int? ResidenceState { get; set; }
[Display(Name ="Local Government of Residence")]
public int? ResidenceLga { get; set; }
[Display(Name ="Date of Birth"),DataType(DataType.Date)]
public DateTime? DateOfBirth { get; set; }
}
The form is here
<form asp-action="signup" method="POST">
<div asp-validation-summary="ModelOnly" class="text-danger"></div>
<div class="form-group">
<label asp-for="Surname" class="control-label"></label>
<input asp-for="Surname" class="form-control" />
<span asp-validation-for="Surname" class="text-danger"></span>
</div>
<div class="form-group">
<label asp-for="Othernames" class="control-label"></label>
<input asp-for="Othernames" class="form-control" />
<span asp-validation-for="Othernames" class="text-danger">
</span>
</div>
<div class="form-group">
<label asp-for="EmailAddress" class="control-label"></label>
<input asp-for="EmailAddress" class="form-control" />
<span asp-validation-for="EmailAddress" class="text-danger"></span>
</div>
<div class="form-group">
<label asp-for="Password" class="control-label"></label>
<input asp-for="Password" class="form-control" />
<span asp-validation-for="Password" class="text-danger"></span>
</div>
<div class="form-group">
<label asp-for="ConfirmPassword" class="control-label"></label>
<input asp-for="ConfirmPassword" class="form-control" />
<span asp-validation-for="ConfirmPassword" class="text-danger">
</span>
</div>
<div class="form-group">
<label asp-for="Gender" class="control-label"></label>
<select asp-for="Gender" class="form-control" asp-
items="#Html.GetEnumSelectList<Gender>()">
<option selected="selected" value="">--Select--</option>
</select>
<span asp-validation-for="Gender" class="text-danger"></span>
</div>
<div class="form-group">
<label asp-for="ResidenceState" class="control-label"></label>
<select asp-for="ResidenceState" class="form-control" asp-
items="ViewBag.StateId">
<option value="">-Select-</option>
</select>
<span asp-validation-for="ResidenceState" class="text-danger">
</span>
</div>
<div class="form-group">
<label asp-for="ResidenceLga" class="control-label"></label>
<select asp-for="ResidenceLga" class="form-control"><option
value="">-Select State First-</option></select>
<span asp-validation-for="ResidenceLga" class="text-danger">
</span>
</div>
<div class="form-group">
<label asp-for="DateOfBirth" class="control-label"></label>
<input asp-for="DateOfBirth" class="form-control" />
<span asp-validation-for="DateOfBirth" class="text-danger">
</span>
</div>
<div class="form-group">
<input type="submit" value="Register" class="btn btn-primary" />
</div>
</form>
The Controller is as follows:
public IActionResult Signup()
{
ViewData["StateId"] = new SelectList(_context.States, "Id", "Name");
return View();
}
[HttpPost]
//[ValidateAntiForgeryToken]
public async Task<ActionResult> Signup(SignUpViewModel model)
{
if (EmailExists(model.EmailAddress))
{
ModelState.AddModelError("", "This email address is already registered with us.");
}
if (ModelState.IsValid)
{
try
{
Enrollee enrollee = new Enrollee {
Surname=model.Surname,
OtherNames=model.Othernames,
Email=model.EmailAddress,
Gender=model.Gender,
StateId=model.ResidenceState,
LgaId=model.ResidenceLga,
DateOfBirth=model.DateOfBirth
};
var result = await _userManager.CreateAsync(enrollee, model.Password);
if (result.Succeeded)
{
//send password the new staff's email
var body = "<p>Email From {0} ({1})</p><p>Message:</p><p>{2}</p>";
var baseurl = $"{Request.Scheme}://{Request.Host}{Request.PathBase}";
var loginurl = baseurl + "/User/Login";
var msg = "Congratulations! " + model.Othernames + "," + "<br />" + "You have just been registered on the eSchool portal." + "<br />" + "Use this email as your login username and the password you registered with to login to the portal:" + "<br />" + "<br />" + "Click <strong><a href='" + loginurl + "'>here</a></strong> to login" + "\r" + "\r";
var message = string.Format(body, "eSchool Portal", "registration#eschoolnigeria.com", msg);
await _emailSender.SendEmailAsync(model.EmailAddress, "Registration on eSchool portal", message);
return RedirectToAction(nameof(Signup));
}
foreach (var error in result.Errors)
{
ModelState.AddModelError(string.Empty,
error.Description);
}
}
catch (Exception e)
{
ModelState.AddModelError("",e.Message.ToString());
}
}
else
{
ModelState.AddModelError(string.Empty, "Something went wrong
with the process. Please retry");
}
return RedirectToAction(nameof(Signup));
}
I will appreciate any guide to the error making the form not to submit.
Thank you
In your [HttpPost] Signup(...) Action you are always returning RedirectToAction(nameof(Signup)) which will cause you to end up back on the same page with a blank form