So I have a ViewModel and the properties are all validated by having required attributes on them. When I submit the form it doesn't throw any error messages, i.e. name field is required.
I think I have figured where the problem is. Its in the submit button, how do I get the button to hit the HttpPost method of the ActionList because at the moment its not hitting that, hence the modelstate is not being validated.
View:
<h2>#ViewBag.Title</h2>
<hr>
<div class="well">
#using (Html.BeginForm("BookingDetails", "Booking"))
{
#Html.ValidationSummary(true)
<div class="form-group">
#Html.DisplayNameFor(m => m.FirstName)
#Html.TextBoxFor(m => m.FirstName, new { #class = "form-control" })
#Html.ValidationMessageFor(m => m.FirstName)
</div>
<div class="form-group">
#Html.DisplayNameFor(m => m.Surname)
#Html.TextBoxFor(m => m.Surname, new { #class = "form-control" })
#Html.ValidationMessageFor(m => m.Surname)
</div>
<div class="form-group">
#Html.DisplayNameFor(m => m.EmailAddress)
#Html.TextBoxFor(m => m.EmailAddress, new { #class = "form-control" })
#Html.ValidationMessageFor(m => m.EmailAddress)
</div>
<div class="form-group">
#Html.DisplayNameFor(m => m.MobileNumber)
#Html.TextBoxFor(m => m.MobileNumber, new { #class = "form-control" })
#Html.ValidationMessageFor(m => m.MobileNumber)
</div>
<div class="form-group">
#Html.DisplayNameFor(m => m.NumberOfPeople)
#Html.TextBoxFor(m => m.NumberOfPeople, new { #class = "form-control" })
#Html.ValidationMessageFor(m => m.NumberOfPeople)
</div>
<div class="form-group">
#Html.DisplayNameFor(m => m.Date)
#Html.TextBoxFor(m => m.Date, new { #class = "form-control" })
#Html.ValidationMessageFor(m => m.Date)
</div>
<div>
#Html.DisplayNameFor(m => m.Time)
#Html.TextBoxFor(m => m.Time, new { #class = "form-control" })
#Html.ValidationMessageFor(m => m.Time)
</div>
}
</div>
<div class="form-group">
#Html.ActionLink("Book my table", "BookingDetails", "Booking" new { #class ="btn btn-primary" })
</div>
Controller:
// GET:
public ActionResult BookingDetails()
{
return View();
}
// Post
[HttpPost]
public ActionResult BookingDetails(BookingDetailsViewModel model)
{
if(ModelState.IsValid)
{
return RedirectToAction("BookingConfirmation");
}
return View(model);
}
You have two problems that I can see.
Firstly, your button isn't a submit button. Its a link that sits outside of the form. You must move it into your form and make it a submit button (or write some javascript that submits the form on click):
<div>
#Html.DisplayNameFor(m => m.Time)
#Html.TextBoxFor(m => m.Time, new { #class = "form-control" })
#Html.ValidationMessageFor(m => m.Time)
</div>
<input type="submit" class="btn btn-primary" /> <!-- move it inside the form -->
#* ^^^^ submit *#
} <!-- end of your form is here -->
Also, Html.ValidationSummary(true) will hide all of your property errors from your Validation Summary. This may not be what you want. If you run into that issue.. remove true from the call.
Related
I have a edit view whose textboxes needs to be assigned with some fields after i click an edit button against a record in a table in another view.
<tbody>
#foreach (var client in this.Model.ClientsList)
{
<tr>
<td>#client.Name</td>
<td>#client.PhoneNo</td>
<td>#client.Address</td>
<td>#client.ProjectNo</td>
<td class="text-center">
<a href="../Downloads/Clients/#client.Logo" target="_blank">
<i class="fa fa-file-text"></i>
</a>
</td>
<td>#Html.ActionLink("Edit", "Edit")</td>
</tr>
}
</tbody>
This is the Edit view
#using (Html.BeginForm("Edit","Clients", FormMethod.Post, new { enctype = "multipart/form-data" }))
{
#Html.AntiForgeryToken()
<div class="form-horizontal">
<h4>Clients</h4>
<hr />
#Html.ValidationSummary(true)
#Html.HiddenFor(model => model.ID)
<div class="form-group">
#Html.LabelFor(model => model.Name, new { #class = "control-label col-md-2" })
<div class="col-md-10">
#Html.EditorFor(model => model.Name)
#Html.ValidationMessageFor(model => model.Name)
</div>
</div>
<div class="form-group">
#Html.LabelFor(model => model.Address, new { #class = "control-label col-md-2" })
<div class="col-md-10">
#Html.EditorFor(model => model.Address)
#Html.ValidationMessageFor(model => model.Address)
</div>
</div>
<div class="form-group">
#Html.LabelFor(model => model.PhoneNo, new { #class = "control-label col-md-2" })
<div class="col-md-10">
#Html.EditorFor(model => model.PhoneNo)
#Html.ValidationMessageFor(model => model.PhoneNo)
</div>
</div>
<div class="form-group">
#Html.LabelFor(model => model.Logo, new { #class = "control-label col-md-2" })
<div class="col-md-10">
#Html.EditorFor(model => model.Logo)
#Html.ValidationMessageFor(model => model.Logo)
</div>
</div>
<div class="form-group">
#Html.LabelFor(model => model.ProjectNo, new { #class = "control-label col-md-2" })
<div class="col-md-10">
#Html.EditorFor(model => model.ProjectNo)
#Html.ValidationMessageFor(model => model.ProjectNo)
</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>
}
I tried my best but couldn't do it. So asking here. I also tried putting it an object but that also didn't work. I know it can be shared via query strings but that's not very efficient.
I assume that you want to edit the client data so You can pass data to another view by passing in TModelValue as third params like this
#Html.ActionLink("Edit","Edit", new { ID = client.Id })
Hope it will help.
Regrads.
Simply You can do this way
#Html.ActionLink("Edit", // <-- Link text
"url/GetClientDataById/2" // <-- Action Method Name
)
Preferred Way
#Html.ActionLink("Edit", "url/GetClientDataById", new { Id = client.Id })
public string GetClientDataById(int Id)
{
//perform your DB logic here and then return your object to view
}
I want to align 2 test box and drop-down in a same row and display labels below those text boxes. How I do that.
I want to display something like this
In my code text boxes are displaying one below the other.
<div class="form-horizontal ">
#Html.TextBoxFor(model => model.City, new{ placeholder = "City" })
#Html.ValidationMessageFor(model => model.City)
<div><small class="form-text text-muted">City</small></div>
#Html.DropDownListFor(x => x.State, PressRoom.Helpers.StateNamesList.GetStatesList(), new { placeholder = "--Select State--" })
#Html.ValidationMessageFor(model => model.State)
<div><small class="form-text text-muted">State</small></div>
#Html.TextBoxFor(model => model.ZipCode, new { placeholder = "Zip Code" })
#Html.ValidationMessageFor(model => model.ZipCode)
<div><small class="form-text text-muted">Zip</small></div>
</div>
With bootstrap4, you can achieve that using either form-row or just regular row css class.
<form>
<!-- you can use "row" class here -->
<div class="form-row">
<div class="col">
#Html.TextBoxFor(x => x.City, new { #class = "form-control" })
#Html.ValidationMessageFor(x => x.City)
#Html.Labelfor(x => x.City, new { #class = "text-muted small" })
</div>
<div class="col">
#Html.DropDownListFor(x => x.State,
PressRoom.Helpers.StateNamesList.GetStatesList(),
"-- Select a state --",
new { #class = "form-control" })
#Html.ValidationMessageFor(x => x.State)
#Html.Labelfor(x => x.State, new { #class = "text-muted small" })
</div>
<div class="col">
#Html.TextBoxFor(x => x.ZipCode, new { #class = "form-control" })
#Html.ValidationMessageFor(x => x.ZipCode)
#Html.Labelfor(x => x.ZipCode, new { #class = "text-muted small" })
</div>
</div>
</form>
And it will look like:
This question already has answers here:
What is a NullReferenceException, and how do I fix it?
(27 answers)
Closed 6 years ago.
I have a form, which gets some values from model. Then, in POST the data user entered are handled and user is redirected to another page. However everytime I post the form, I get null-reference exeption. Where do I make a mistake?
None of the other questions asking about this didn´t solve my problem, that´s why I am asking again with my specific code.
The exeptions are at foreach loops - Model.Cart, Model.ShippingOptionsm etc.
#model CheckoutViewModel
#{
ViewBag.Title = "Checkout";
Layout = "~/Views/Shared/_Layout.cshtml";
}
<h2>Checkout</h2>
<table class="table">
<thead>
<tr>
<td>#</td>
<td>Name</td>
<td>Quantity</td>
<td>Price</td>
<td>Total price</td>
</tr>
</thead>
#foreach (CartItem i in Model.Cart)
{
<tr>
<td>#Html.Action("ProductPosition", "Cart", new { item = i })</td>
<td>#i.Name</td>
<td>#i.Quantity</td>
<td>#i.Price €</td>
<td>#i.Total €</td>
</tr>
}
</table>
<h3>Total: #Html.Action("TotalPrice", "Cart") €</h3>
#using (Html.BeginForm("Checkout", "Cart"))
{
#Html.AntiForgeryToken()
<h2>Address</h2>
<div class="form-group">
#Html.LabelFor(m => m.Name)
#Html.TextBoxFor(m => m.Name, new { #class = "form-control" })
#Html.ValidationMessageFor(m => m.Name)
</div>
<div class="form-group">
#Html.LabelFor(m => m.Address)
#Html.TextBoxFor(m => m.Address, new { #class = "form-control" })
#Html.ValidationMessageFor(m => m.Address)
</div>
<div class="form-group">
#Html.LabelFor(m => m.City)
#Html.TextBoxFor(m => m.City, new { #class = "form-control" })
#Html.ValidationMessageFor(m => m.City)
</div>
<div class="form-group">
#Html.LabelFor(m => m.PostNumber)
#Html.TextBoxFor(m => m.PostNumber, new { #class = "form-control" })
#Html.ValidationMessageFor(m => m.PostNumber)
</div>
<div class="form-group">
#Html.LabelFor(m => m.State)
#Html.TextBoxFor(m => m.State, new { #class = "form-control" })
#Html.ValidationMessageFor(m => m.State)
</div>
<h2>Shipping</h2>
foreach (var i in Model.ShippingOptions)
{
<div class="radio">
#Html.RadioButtonFor(m => m.ShippingOption, i.Id) #i.Name - #i.Price €
</div>
}
#Html.ValidationMessageFor(m => m.ShippingOption);
<h2>Payment</h2>
foreach (var i in Model.PaymentOptions)
{
<div class="radio">
#Html.RadioButtonFor(m => m.PaymentOption, i.Id) #i.Name
</div>
}
#Html.ValidationMessageFor(m => m.PaymentOption);
<button type="submit" class="btn btn-primary">Continue</button>
}
#section scripts
{
#Scripts.Render("~/bundles/jqueryval")
}
Controller:
public ActionResult Checkout()
{
if (Session["cart"] == null)
return RedirectToAction("Index");
var checkout = new CheckoutViewModel()
{
Cart = (List<CartItem>)Session["cart"],
PaymentOptions = _context.PaymentOptions.ToList(),
ShippingOptions = _context.ShippingOptions.ToList()
};
return View(checkout);
}
[HttpPost]
[ValidateAntiForgeryToken]
public ActionResult Checkout(CheckoutViewModel m)
{
if (!ModelState.IsValid)
return View();
//TODO: handle data
return RedirectToAction("OrderSuccess");
}
Add model declaration at the top of the page
#model CheckoutViewModel
I have a form with and one of the fields for the model is complex, so I created a custom editor for that field.
Here is the part of the editor in the main form:
<div class="form-group">
<label class="col-md-3 control-label">#i18n.Translations</label>
<div class="control-group col-md-9">
#Html.EditorFor(m => m.Translations)
</div>
</div>
Here is the Custom Editor:
#using XXX.i18n
#using XXX.ViewModels
#model XXX.ViewModels.ServiceTranslation
#{
bool translationNameValid = Html.ViewData.ModelState.IsValidField(Html.IdFor(m => m.TranslationName).ToString());
bool translationDescriptionValid = Html.ViewData.ModelState.IsValidField(Html.IdFor(m => m.TranslationDescription).ToString());
}
<div class="form-group">
#Html.HiddenFor(m => m.EntityLanguageId)
#Html.HiddenFor(m => m.Language)
#Html.HiddenFor(m => m.TranslationId.Left)
#Html.HiddenFor(m => m.TranslationId.Right)
<div class="col-md-6">
<div class="input-group #(translationNameValid ? null : "state-error")">
<span class="input-group-addon">#Model.Language</span>
#Html.TextBoxFor(m => m.TranslationName, new { #placeholder = i18n.Name, #class = "form-control" })
</div>
<div class="#(translationNameValid ? null : "state-error")">
<em for="#Html.IdFor(m => m.TranslationName)" class="invalid">#Html.ValidationMessage("TranslationName")</em>
</div>
</div>
<div class="col-md-6 #(translationDescriptionValid ? null : "state-error")">
#Html.TextBoxFor(m => m.TranslationDescription, new { #placeholder = i18n.Description, #class = "form-control" })
<em for="#Html.IdFor(m => m.TranslationDescription)" class="invalid">#Html.ValidationMessage("TranslationDescription")</em>
</div>
</div>
Also, I should mention that the main form is inside a partial and the TranslationName and TranslationDescription are 2 required fields (and are annotated with [Required] in the ViewModel)
The #Html.ValidationMessage return the required field message with the textboxes are empty.
My problem is that the translationDescriptionValid and the translationNameValid are always true and they should return false when the fields are blank.
I've used this method in other forms with success.
What am I doing wrong here?
Having faced this issue (I wanted to allow an edit by using a bootstrap modal window, i'm using MVC4 and entity framework), when I want to save my changes, I have this error message since I'm using the modal window :
Store update, insert, or delete statement affected an unexpected number of rows (0). Entities may have been modified or deleted since entities were loaded. Refresh ObjectStateManager entries.
Here are my actions :
[HttpGet]
public ActionResult EditPerson(long id)
{
var person = db.Persons.Single(p => p.Id_Person == id);
ViewBag.Id_ProductPackageCategory = new SelectList(db.ProductPackageCategories, "Id_ProductPackageCategory", "Name", person.Id_ProductPackageCategory);
return PartialView("_EditPerson", person);
}
[HttpPost]
public ActionResult EditPerson(Person person)
{
ViewBag.Id_ProductPackageCategory = new SelectList(db.ProductPackageCategories, "Id_ProductPackageCategory", "Name", person.Id_ProductPackageCategory);
if (ModelState.IsValid)
{
ModelStateDictionary errorDictionary = Validator.isValid(person);
if (errorDictionary.Count > 0)
{
ModelState.Merge(errorDictionary);
return View(person);
}
db.Persons.Attach(person);
db.ObjectStateManager.ChangeObjectState(person, EntityState.Modified);
db.SaveChanges();
return View("Index");
}
return View(person);
}
My partial view :
#model BuSIMaterial.Models.Person
<div class="modal-header">
<button type="button" class="close" data-dismiss="modal" aria-hidden="true">×</button>
<h3 id="myModalLabel">Edit</h3>
</div>
<div>
#using (Ajax.BeginForm("EditPerson", "Person", FormMethod.Post,
new AjaxOptions
{
InsertionMode = InsertionMode.Replace,
HttpMethod = "POST",
UpdateTargetId = "table"
}))
{
#Html.ValidationSummary()
#Html.AntiForgeryToken()
<div class="modal-body">
<div class="editor-label">
First name :
</div>
<div class="editor-field">
#Html.TextBoxFor(model => model.FirstName, new { maxlength = 50 })
#Html.ValidationMessageFor(model => model.FirstName)
</div>
<div class="editor-label">
Last name :
</div>
<div class="editor-field">
#Html.TextBoxFor(model => model.LastName, new { maxlength = 50 })
#Html.ValidationMessageFor(model => model.LastName)
</div>
<div class="editor-label">
National number :
</div>
<div class="editor-field">
#Html.EditorFor(model => model.NumNat, new { maxlength = 11 })
#Html.ValidationMessageFor(model => model.NumNat)
</div>
<div class="editor-label">
Start date :
</div>
<div class="editor-field">
#Html.TextBoxFor(model => model.StartDate, new { #class = "datepicker", #Value = Model.StartDate.ToString("yyyy/MM/dd") })
#Html.ValidationMessageFor(model => model.StartDate)
</div>
<div class="editor-label">
End date :
</div>
<div class="editor-field">
#if (Model.EndDate.HasValue)
{
#Html.TextBoxFor(model => model.EndDate, new { #class = "datepicker", #Value = Model.EndDate.Value.ToString("yyyy/MM/dd") })
#Html.ValidationMessageFor(model => model.EndDate)
}
else
{
#Html.TextBoxFor(model => model.EndDate, new { #class = "datepicker" })
#Html.ValidationMessageFor(model => model.EndDate)
}
</div>
<div class="editor-label">
Distance House - Work (km) :
</div>
<div class="editor-field">
#Html.EditorFor(model => model.HouseToWorkKilometers)
#Html.ValidationMessageFor(model => model.HouseToWorkKilometers)
</div>
<div class="editor-label">
Category :
</div>
<div class="editor-field">
#Html.DropDownList("Id_ProductPackageCategory", "Choose one ...")
#Html.ValidationMessageFor(model => model.Id_ProductPackageCategory) <a href="../ProductPackageCategory/Create">
Add a new category?</a>
</div>
<div class="editor-label">
Upgrade? :
</div>
<div class="editor-field">
#Html.EditorFor(model => model.Upgrade)
#Html.ValidationMessageFor(model => model.Upgrade)
</div>
</div>
<div class="modal-footer">
<button class="btn btn-inverse" type="submit">Save</button>
</div>
}
Any idea on what's going on?
Try this first, just above #Html.ValidationSummary() in the partial view where you have the modal head, body and footer, place:
#Html.HiddenFor(model => model.PersonId) // or.Id whatever's in your model
This creates a hidden field in your view and sets model ID i.e. PK.