The value "some value" is invalid asp.net model binding validation - asp.net

Controllers:
public ActionResult EditTest()
{
return View(new EditTestViewModel("Is this a test?"));
}
[AcceptVerbs(HttpVerbs.Post)]
public ActionResult EditTest(EditTestViewModel test)
{
return View(new EditTestViewModel("Is this a test?"));
}
ViewModel:
public class EditTestViewModel
{
public String test { get; set; }
public EditTestViewModel(String test)
{
this.test = test;
}
}
View:
<% using (Html.BeginForm("EditTest", "Test", FormMethod.Post)) {%>
<%= Html.ValidationSummary(true) %>
<%= Html.TextBoxFor(model => model.test) %>
<%= Html.ValidationMessageFor(model => model.test) %>
<input type="submit" value="Save" />
<% } %>
Result when I click save (whether I edit the data or not):
The value "Is this a test?" is
invalid.
What is going on?

The first exception you will get when you run this code and submit the form is the following:
[MissingMethodException: No parameterless constructor defined for this object.]
That's because EditTestViewModel doesn't have a parameterless constructor you cannot use it like this.
The second problem with your code is that you are creating a new object in your POST action instead of reusing the one that's being passed as argument.
So here's how to fix:
View model:
public class EditTestViewModel
{
public String test { get; set; }
}
Controller:
public ActionResult EditTest()
{
var model = new EditTestViewModel
{
test = "Is this a test?"
}
return View(model);
}
[AcceptVerbs(HttpVerbs.Post)]
public ActionResult EditTest(EditTestViewModel test)
{
return View(model);
}
And if for some reason you wanted to edit the value in the POST action and this to reflect in the view you will need to remove it from the model state or the HTML helpers will pick the old value:
[AcceptVerbs(HttpVerbs.Post)]
public ActionResult EditTest(EditTestViewModel test)
{
model.test = "new value";
ModelState.Remove("test");
return View(model);
}

Related

How to send data from view to controller action in asp.net mvc?

I developed a custom HtmlHelper extension method but that data is not
posting Action.
HtmlHelper extension class:
public static class TestHtmlHelper
{
public static MvcHtmlString CreateControl(this HtmlHelper helper, string tagName, IDictionary<string, string> attributes)
{
var newTag = new TagBuilder(tagName);
newTag.MergeAttributes(attributes, true);
return MvcHtmlString.Create(newTag.ToString(TagRenderMode.Normal));
}
public static string Image(this HtmlHelper helper, string id, string url, string alternateText, object htmlAttributes)
{
// Create tag builder
var builder = new TagBuilder("img");
// Create valid id
builder.GenerateId(id);
// Add attributes
builder.MergeAttribute("src", url);
builder.MergeAttribute("alt", alternateText);
builder.MergeAttributes(new RouteValueDictionary(htmlAttributes));
// Render tag
return builder.ToString(TagRenderMode.Normal);
}
}
//View code
#using (Html.BeginForm("Go","Home",FormMethod.Post))
{
IDictionary<string, string> d = new Dictionary<string, string>();
d.Add("type", "text");
d.Add("id", "text1");
d.Add("required", "required");
#Html.Raw(Html.CreateControl("input", d))
#Html.Raw(Html.Image("image1", "/Images/bullet.png", "bullet", new { border = "4px" }))
d = null;
d = new Dictionary<string, string>();
d.Add("type", "submit");
d.Add("value", "Go");
#Html.Raw(Html.CreateControl("input", d))
<span></span>
d = null;
d = new Dictionary<string, string>();
d.Add("value", "text");
d.Add("id", "span1");
d.Add("text", "required");
#Html.Raw(Html.CreateControl("span", d))
}
// Controller code
public ActionResult Index()
{
ViewBag.Message = "Modify this template to jump-start your ASP.NET MVC application.";
return View();
}
[HttpPost]
public ActionResult Go(string test)
{
return Content(test);
}
I didn't get data in string test. I want to submit that data to DB.
To get input values as parameters for an MVC action, you need to include NAME for the input types.
I do not see NAME for any input types in your code.
Also I do not see TEST in your code
For example, if your form is -
#using (Html.BeginForm("Submit","Ajax",FormMethod.Post))
{
<input type="text" name="Rami"/>
<input type="submit" value="Go"/>
}
Output ScreenShot -
Put your inputs inside a form tag. All the input data will be sent to the controller on form submit. Please see the example:
View:
#using (Html.BeginForm("Search", "Events"))
{
#Html.TextBox("name")
<input type="submit" value="Search" />
}
Controller:
public class EventsController: Controller
{
public ActionResult Search(string name)
{
//some operations goes here
return View(); //return some view to the user
}
}
If you need to work with more complex types just lern how to use models in ASP.NET MVC. Here is short example:
Razor:
#model UserModel
#using (Html.BeginForm("Search", "Events"))
{
#Html.TextBoxFor(m => m.FirstName)
#Html.TextBoxFor(m => m.LastName)
<input type="submit" value="Search" />
}
Controller:
public class EventsController: Controller
{
public ActionResult Search(UserModel model)
{
//some operations goes here
return View(); //return some view to the user
}
}
Model (C#):
public class UserModel
{
public string FirstName { get; set; }
public string LastName { get; set; }
}

Binding model with child list MVC 3 razor

I have the following model :
public class ContratoDetailsViewModel
{
[StringLength(50)]
[RegularExpression("^[a-z0-9_\\+-]+(\\.[a-z0-9_\\+-]+)*#[a-z0-9-]+(\\.[a-z0-9-]+)*\\.([a-z]{2,4})$")]
[DisplayName("E-Mail Adm.")]
public string emailAdm { get; set; }
}
public class ContratoDetailContainerViewModel
{
public ContratoDetailsViewModel contrato { get; set; }
public IList<ContratoModels.CCasinoViewModel> ccasinos { get; set; }
}
public class CCasinoViewModel
{
public short codigo { get; set; }
public List<SelectListItem> listCasinos { get; set; }
}
the following view :
#model ContratoModels.ContratoDetailContainerViewModel
#{
...
}
#using (Html.BeginForm(new { currentaction = ViewBag.mode }))
{
...
#Html.EditorFor(m => m.contrato.emailAdm, state1)<br />
#Html.EditorFor(m => m.ccasinos,"test")
<input type="submit" value="Save" />
}
in the folder "EditorTemplates" i have a template called "test.cshtml" :
#model List<ContratoModels.CCasinoViewModel>
#for (int i = 0; i < Model.Count(); i++)
{
#Html.DropDownListFor(m => m[i].codigo,Model[i].listCasinos)
}
My Controller post action is like this :
[HttpPost]
public ActionResult Details(ContratoModels.ContratoDetailContainerViewModel model, FormCollection form)
{
var contrato = model.contrato;
var casinos = model.ccasinos;
}
Before send the view ccasinos,codigo and listCasinos are initialised
when i am in debug mode i see the value of them... the form display work like a charm.
BUT ... when i submit the form the model.ccasinos is always null !! why ?
thank you very much for your reply.
note : I use a EditorFor with the child of my main model but if there is a better solution
for display and submit with MCV 3 I am interested ...
Try replacing:
#Html.EditorFor(m => m.ccasinos, "test")
with this:
#Html.EditorFor(m => m.ccasinos)
and then rename your test.cshtml template to CCasinoViewModel.cshtml and replace its contents with this:
#model CCasinoViewModel
#Html.DropDownListFor(x => x.codigo, Model.listCasinos)
Because the editor template is now named the same way as the type of the list, ASP.NET MVC will automatically render it for each element of this list so that you don't have to write loops.
Also you can safely remove the FormCollection argument from your action. It's completely useless when you are working with view models:
[HttpPost]
public ActionResult Details(ContratoModels.ContratoDetailContainerViewModel model)
{
var contrato = model.contrato;
var casinos = model.ccasinos;
...
}

Binding View Model from a form post with inner complex types

Ok, i got a viewmodel as follows:
public class PageViewModel
{
public Item Item { get; set; }
...
public PageViewModel
{ }
public PageViewModel(Item itemWebPage)
{
...
}
}
I use a form to edit this Item using a route like:
/Controller/Edit/43
The controller uses this code:
[AcceptVerbs("GET")]
public new ActionResult Edit(int id)
{
...
PageViewModel pageViewModel = new PageViewModel(...);
return PartialView(pageViewModel);
}
[AcceptVerbs(HttpVerbs.Post)]
public ActionResult Edit(int id, PageViewModel item)
{
if (ModelState.IsValid)
{
...
// Here, the item.Item.ID is null, I want it to map to the id of the route.
}
return PartialView("Edit", item);
}
What I want to achieve is the ID of the property: item.Item.ID to be bound to the ID from the (route)URL. However I can't get it to work. I tried using [Bind()] attribute.
I fixed it now using a hidden field in the form like so:
<%= Html.HiddenFor(model => model.Item.ID)%>
However this feels a bit icky. I'd like to know if there is a better cleaner way of doing this?
Personally I would set the ID parameter in the form declaration as follows:
<% using (Html.BeginForm("Edit", "YourController", FormMethod.Post, new { id = model.Item.ID })) { %>
...
<% } %>

ModelState always valid

I've got something seemingly very simple not working.
I have got a model
public class Name: Entity
{
[StringLength(10), Required]
public virtual string Title { get; set; }
}
public class Customer: Entity
{
public virtual Name Name { get; set; }
}
a view model
public class CustomerViweModel
{
public Customer Customer { get; set; }
}
a view
<% using(Html.BeginForm()) { %>
<%= Html.LabelFor(m => m.Customer.Name.Title)%>
<%= Html.TextBoxFor(m => m.Customer.Name.Title)%>
<button type="submit">Submit</button>
<% } %>
and a controller
[HttpPost]
public ActionResult Index([Bind(Prefix = "Customer")] Customer customer)
{
if(ModelState.IsValid)
Save
else
return View();
}
No matter what I enter as the title (null, or a string > 10 chars), ModelState.IsValid is always true. The Title field in the Customer object has a value, so the data is being passed around, but not being validated?
Any clues?
In your View I don't see any text box or a field allowing to send data to the controller, only a label. Properties will not be validated if they are not posted. Add a textbox, leave it blank and your model won't be valid any more:
<%= Html.TextBoxFor(m => m.Customer.Name.Title)%>
UPDATE:
Here's the code I've used:
Model:
public class Name
{
[StringLength(10), Required]
public virtual string Title { get; set; }
}
public class Customer
{
public virtual Name Name { get; set; }
}
public class CustomerViewModel
{
public Customer Customer { get; set; }
}
Controller:
public class HomeController : Controller
{
public ActionResult Index()
{
return View();
}
[HttpPost]
public ActionResult Index([Bind(Prefix = "Customer")]Customer cs)
{
return View(new CustomerViewModel
{
Customer = cs
});
}
}
View:
<%# Page Language="C#" MasterPageFile="~/Views/Shared/Site.Master" Inherits="System.Web.Mvc.ViewPage<MyApp.Models.CustomerViewModel>" %>
<asp:Content ID="Content2" ContentPlaceHolderID="MainContent" runat="server">
<% using(Html.BeginForm()) { %>
<%= Html.LabelFor(m => m.Customer.Name.Title)%>
<%= Html.TextBoxFor(m => m.Customer.Name.Title)%>
<button type="submit">Submit</button>
<% } %>
</asp:Content>
When you submit this form a validation error is shown.
Remark1: I've omitted the Entity base class in the models as I don't how does it look.
Remark2: I've renamed the variable in the Index action to cs. I remember that there was some problems with this in ASP.NET MVC 1.0 when you had the prefix and the variable named the same but I am not sure whether this applies here and I think it was fixed.
Figured it out, it was becuase I'm referencing System.ComponentModel.DataAnnotations 3.6 instead of 3.5. From what I gather, 3.6 is for WCF RIA services only.

problems with ASP MVC + Html.DropDownList() using a ModelView Pattern

Recently I posted a question about the html helper dropdownlist and got it working (here). But now I have decided it was alot smarter to switch to ModelView Patterns so I have acces to strongly typed methods in my views etc. What I did was I made some adjustments to the code in my other topic in the following way:
VacatureFormViewModel:
public class VacaturesFormViewModel
{
public Vacatures Vacature { get; private set; }
public SelectList EducationLevels { get; private set; }
public SelectList Branches { get; private set; }
public SelectList CareerLevels { get; private set; }
Repository repository;
// Constructor
public VacaturesFormViewModel(Vacatures vacature)
{
this.Vacature = vacature;
this.repository = new Repository();
this.EducationLevels = new SelectList(repository.GetAllEducationLevels(),"ID","Name",vacature.EducationLevels);
this.Branches = new SelectList(repository.GetAllBranches(),"ID","Name",vacature.Branches);
this.CareerLevels = new SelectList(repository.GetAllCareerLevels(), "ID", "Name", vacature.CareerLevels);
}
}
BanenController:
//
// GET: /Banen/Create
public ActionResult Create()
{
Vacatures vacature = new Vacatures();
return View(new VacaturesFormViewModel(vacature));
}
//
// POST: /Banen/Create
[AcceptVerbs(HttpVerbs.Post)]
public ActionResult Create(Vacatures vacatureToAdd)
{
if (ModelState.IsValid)
{
try
{
// TODO: Add insert logic here
repository.AddToVacatures(vacatureToAdd);
repository.SaveChanges();
// Return to listing page if succesful
return RedirectToAction("Index");
}
catch (Exception e)
{
return View();
}
}
}
And my Create.aspx view (part of it):
<% using (Html.BeginForm()) {%>
<fieldset>
<legend>Fields</legend>
<p>
<label for="Title">Title:</label>
<%= Html.TextBox("Title", Model.Vacature.Title) %>
<%= Html.ValidationMessage("Title", "*") %>
</p>
<p>
<label for="Content">Content:</label>
<%= Html.TextArea("Content", Model.Vacature.Content) %>
<%= Html.ValidationMessage("Content", "*") %>
</p>
<p>
<label for="EducationLevels">EducationLevels:</label>
<%= Html.DropDownList("EducationLevels", Model.EducationLevels)%>
<%= Html.ValidationMessage("EducationLevels", "*") %>
</p>
<p>
<label for="CareerLevels">CareerLevels:</label>
<%= Html.DropDownList("CareerLevels", Model.CareerLevels)%>
<%= Html.ValidationMessage("CareerLevels", "*")%>
</p>
<p>
<label for="Branches">Branches:</label>
<%= Html.DropDownList("Branches", Model.Branches)%>
<%= Html.ValidationMessage("Branches", "*")%>
</p>
<p>
<input type="submit" value="Save" />
</p>
</fieldset>
<% } %>
For guiding I have used the NerdDinner tutorial by ScottGu and I have read various topics here.
My question is if it is possible to let MVC ASP set my careerlevel, educationlevel and branche (dropdownlists) automatically as it currently is returning an ID string which is not what I want. When I change the creation of the SelectList to:
this.CareerLevels = new SelectList(repository.GetAllCareerLevels(), vacature.CareerLevels);
So without the "ID" and "Name" it does not save either (I guess it is still returned as a string in the post method, and not the object itself) and next to this, it lists in the view as: vacature.EducationLevels etc. So not the Names but the object itself is listed.
Final question
So, in short, my question is if it is possible to use this approach to set my branche, educationallevel and careerlevel. So not automatically?
In which case I still have to use things like:
[AcceptVerbs(HttpVerbs.Post)]
public ActionResult Create(FormCollection form)
{
Vacatures vacatureToAdd = new Vacatures();
// Retrieve the education level by its ID
if (!form["EducationLevels"].Equals(""))
{
Guid educationID = new Guid(form["EducationLevels"]);
vacatureToAdd.EducationLevels = repository.GetEducationLevelByID(educationID);
}
In my controller? Or are there other, smoother options.
Edited to use Guid:
With dropdownlists I use a slightly different approach. It might be possible to get your viewmodel working, but for me it is easier this way:
public class VacaturesFormViewModel
{
public IEnumerable<SelectListItem>EducationLevels{ get; set; }
public Guid EducationLevelID{ get; set; }
}
The EducationLevelID will have the selected id of your Dropdown.
This is the view:
<%= Html.DropDownList("EducationLevelID", Model.EducationLevels)%>
Controller
IEnumerable<SelectListItem> educationLevelList =
from level in GetLevelList()
select new SelectListItem
{
Text = level .Name,
Value = level.Uid.ToString()
};
model.EducationLevels = educationLevelList ;
I'm not for sure but I think you should create model binders. (David Hayden wrote an simple model binder)
You could bind educationID parameter automatically:
[AcceptVerbs(HttpVerbs.Post)]
public ActionResult Create(Guid? educationID, FormCollection form)
{
Vacatures vacatureToAdd = new Vacatures();
if (educationID != null)
{
vacatureToAdd.EducationLevels =
repository.GetEducationLevelByID(educationID.Value);
}

Resources