Asp.Net MVC EditorTemplate Model is lost after Post - c#

I have a controller with two simple Methods:
UserController Methods:
[AcceptVerbs(HttpVerbs.Get)]
public ActionResult Details(string id)
{
User user = UserRepo.UserByID(id);
return View(user);
}
[AcceptVerbs(HttpVerbs.Post)]
public ActionResult Details(User user)
{
return View(user);
}
Then there is one simple view for displaying the details:
<% using (Html.BeginForm("Details", "User", FormMethod.Post))
{%>
<fieldset>
<legend>Userinfo</legend>
<%= Html.EditorFor(m => m.Name, "LabelTextBoxValidation")%>
<%= Html.EditorFor(m => m.Email, "LabelTextBoxValidation")%>
<%= Html.EditorFor(m => m.Telephone, "LabelTextBoxValidation")%>
</fieldset>
<input type="submit" id="btnChange" value="Change" />
<% } %>
As you can see, I use an editor template "LabelTextBoxValidation":
<%# Control Language="C#" Inherits="System.Web.Mvc.ViewUserControl<string>" %>
<%= Html.Label("") %>
<%= Html.TextBox(Model,Model)%>
<%= Html.ValidationMessage("")%>
Showing user information is no problem. The view renders perfectly user details.
When I submit the form, the object user is lost. I debugged on the row "return View(User);" in the Post Details method, the user object is filled with nullable values. If I dont use the editor template, the user object is filled with correct data. So there has to be something wrong with the editor template, but can't figure out what it is. Suggestions?

I would re-architect a little bit - change your LabelTextBoxValidation editor to an Html helper, and then make one EditorTemplate for your data model. That way you could do something like this:
<% using (Html.BeginForm("Details", "User", FormMethod.Post))
{%>
<fieldset>
<legend>Userinfo</legend>
<% Html.EditorFor(m => m); %>
</fieldset>
<input type="submit" id="btnChange" value="Change" />
<% } %>
And your editor template would be something like:
<%= Html.ValidatedTextBoxFor(m => m.Name); %>
<%= Html.ValidatedTextBoxFor(m => m.Email); %>
<%= Html.ValidatedTextBoxFor(m => m.Telephone); %>
where ValidatedTextBoxFor is your new html helper. To make that, it would be fairly easy:
public static MvcHtmlString ValidatedTextBoxFor<T>(this HtmlHelper helper, Expression thingy)
{
// Some pseudo code, Visual Studio isn't in front of me right now
return helper.LabelFor(thingy) + helper.TextBoxFor(thingy) + helper.ValidationMessageFor(thingy);
}
That should set the names of the form fields right I believe, as that seems like the source of the problem.
EDIT: Here is the code that should help you out:
public static MvcHtmlString ValidatedTextBoxFor<TModel, TValue>(this HtmlHelper<TModel> html, Expression<Func<TModel, TValue>> expression)
{
return MvcHtmlString.Create(
html.LabelFor(expression).ToString() +
html.TextBoxFor(expression).ToString() +
html.ValidationMessageFor(expression).ToString()
);
}

Related

Use model on SharePoint application page .aspx

I am working on SharePoint to create a Feedback questionnaire form using an application page that is basically a aspx page.
I wish to do this by emulating MVC as far as possible. I've set up my model in the code-behind:
public List<QuestionViewModel> modelQuestions = new List<QuestionViewModel>();
Next, I need to display each question and an appropriate input depending on the question type (e.g. single line, multi line, single selection, multiple selection).
I've got it displaying the questions correctly:
<fieldset class="what-went-wrong">
<% for (int i = 0; i < modelQuestions.Count; i++) { %>
<p>
<label for="QuestionText">
<% if (modelQuestions[i].Required) { %>
<span class="req-field indicator">*</span>
<% } %>
<%= modelQuestions[i].QuestionText %>
<% if (modelQuestions[i].Required) { %>
<span class="req-field right">* Required field</span>
<% } %>
</label>
</p>
<% } %>
</fieldset>
This give's me the question text. I'm now trying to construct the appropriate input, but this <% %> tags is not working for this:
<% if(modelQuestions[i].QuestionTypeId == QuestionType.SingleLine) { %>
<input id="modelQuestions_<% i %>" name="modelQuestions[<% i %>]" type="text" placeholder="<% modelQuestions[i].Placeholder %>" />
<% } %>
I can't seem to get it to construct the html element using details from the model (in the value for id, name, placeholder etc.)
Also, I've no idea how to go about posting this back to the server when I get to that point.
Is there any merit in continuing? Are there other controls/methods more appropriate to use in this case with aspx?
You cannot generate HTML markup like this. Even data-binding expressions will not help, because they bind ASP.NET controls' attributes values, not the plain output HTML in the page.
You should generate the markup in the "code behind", like this:
Page markup:
<div id='AnswersPanel'>
<div>
Page code behind:
protected void PageLoad(...)
{
AnswersPanel.InnerHtml = "";
AnswersPanel.InnerHtml += string.Format("<input id='modelQuestions_{0}' name='modelQuestions[{0}]' type='text' placeholder='{1}' />",
i.ToString(),
modelQuestions[i].Placeholder);
}

Problems with Html.BeginForm() asp.net MVC 2

I have a SearchController.cs in my controller folder, it has an Action named Index. My search folder has a view named Index
The following code is in my /Controller/SearchController
private TEAM2BooksDBEntities _db = new TEAM2BooksDBEntities();
[HttpPost]
public ActionResult Index(string SearchFor)
{
var query = _db.Books.Where(em => em.title.Contains(SearchFor)).ToList();
return View(query);
}
The following code is in my /Home/Index
<% using(Html.BeginForm("Index","Search")){ %>
<%= Html.TextBox("SearchFor") %>
<input type="submit" value="Submit" />
<% }%>
But no matter what I do when I hit the submit button it just reloads the current page. I want it to send the contents of the "SearchFor" box as a parameter to the Index action in the Search controller. How can I fix this?
I also suggest try to use this.
<% using(Html.BeginForm("Index","Search",FormMethod.Post)){ %>
<%= Html.TextBox("SearchFor") %>
<input type="submit" value="Submit" />
<% }%>
Try this inside your action :
string SearchFor= Request.Form["SearchFor"];

how to read TextArea value and model in HttpPost in MVC2

I'm new in MVC2, so sorry for stupid question. I looked for nice answer, but can't find it. So my question is:
I have view:
<%# Page Title="" Language="C#" MasterPageFile="~/Views/Shared/Site.Master" Inherits="System.Web.Mvc.ViewPage<IEnumerable<MyProject.MyDB.MyProducts>>" %>
<%# Import Namespace="MyProject.MyDB" %>
<asp:Content ID="Content1" ContentPlaceHolderID="Content" runat="server">
<% using (Html.BeginForm())
{%>
<table>
<%
foreach (var item in Model)
{%>
<tr>
<td>
<%:item.name%>
</td>
<td>
<%:String.Format("{0:g}", item.date)%>
</td>
</tr>
<% } %>
</table>
<div>
<%:Html.TextArea("MyTextArea")%>
</div>
<p>
<input type="submit" value="Send" />
</p>
<% } %>
</asp:Content>
My controller is:
[HttpGet]
public ActionResult ViewMyProducts(int id)
{
List<MyProducts> myModel = GetMyProducts(id);
return View(myModel);
}
[HttpPost]
public ActionResult ViewMyProducts(/*???What should I put here???*/)
{
if(/*I want "MyTextArea" value here*/ == something && myModel/*from view*/.count==5}
{
//do something
}
return View(myModel);
}
So, in HttpPost I need myModel from view and value of "MyTextArea" from view. How can I get them?? I'll appreciate any help.
I would think that the following should work:
[HttpPost]
public ActionResult ViewMyProducts(string MyTextArea)
A helpful thing to do would be to explicitly call your Action in your Form - by changing this line:
<% using (Html.BeginForm())
to
<% using (Html.BeginForm("ViewMyProducts","ControllerName",HttpMethod.Post))
to ensure that the Submit action redirects it to the right Action.
As far as the model is concerned:
If you are just checking the Count - you could make a hidden field that returns the number of items in the "Model" like such:
<%: Html.Hidden("modelCount", Model.Count());
but if you want the entire Model - it would be need to be something like this:
<%: Html.Hidden("myModel", Model);
then you could further modify your Action to look something like this:
ViewMyProducts(string MyTextArea, int modelCount)
{
//...
}
or
ViewMyProducts(string MyTextArea, IEnumerable<MyProject.MyDB.MyProducts> myModel)
{
//...
}
Although you have access inside of the Controller to refresh the Model - so if you didn't need to pass back the entire thing you could still repopulate your view with a fresh call.
string myTextArea - or you could just check the FormCollection (I would recommend the named variable).
If you want to get the model back from the view, you will need to serialize it out as well in order to get back the values. If this is the case, I would convert the whole thing to a view model that either derives from your Model or has a public property that is your model, add a property for MyTextArea and then emit hidden input's for you model, named for the appropriate properties. Assuming that your model is persisted somewhere (database), I would just pass the key (id) and rehydrate the object from within the action result.
[HttpPost]
public ActionResult ViewMyProducts(ViewMyProductsViewModel viewModel)
{
if(viewModel.MyTextArea == "something" && (IEnumerable<foo>)myModel).Count()==5)) {
var model = repo.Get(myModel.First().Id);
// do something with the model
}
return View(viewModel);
}
<%# Page Title="" Language="C#" MasterPageFile="~/Views/Shared/Site.Master" Inherits="System.Web.Mvc.ViewPage<IEnumerable<MyProject.MyDB.MyProducts>>" %>
<%# Import Namespace="MyProject.MyDB" %>
<asp:Content ID="Content1" ContentPlaceHolderID="Content" runat="server">
<% using (Html.BeginForm())
{%>
<table>
<%
foreach (var item in Model)
{%>
<tr>
<td>
<input type="hidden" name="viewModel.Id" value="<%:item.id%>" />
<%:item.name%>
</td>
<td>
<%:String.Format("{0:g}", item.date)%>
</td>
</tr>
<% } %>
</table>
<div>
<%:Html.TextArea("MyTextArea")%>
</div>
<p>
<input type="submit" value="Send" />
</p>
<% } %>
</asp:Content>
First of all You are rendering item.name and item.date as a text not html control. So You won't be able to receive it in controller method parameters.

Asp.Net MVC2 Clientside Validation and duplicate ID's problem

I'm using MVC2 with VS2010
I have a view that has two partial views in it: "Login" and "Register"
both partial views contains the Email address field i use the following in both partial views:
<%: Html.TextBoxFor(model => model.EmailAddress ) %><br /><%: Html.ValidationMessageFor(model => model.EmailAddress) %>
if i use both partial views on one page, it ends up having duplicate id's so validation happens across both views (even thopugh they are in seperate forms)
how can i go about eliminating this
For some controls you can specify the HTML attributes in an overload like this:
<%: Html.TextBoxFor(model => model.EmailAddress, new { id = 'my-unique-id" }) %>
<br />
<%: Html.ValidationMessageFor(model => model.EmailAddress, new { id = 'my-unique-id" }) %>
You can also either write your HTML by hand or use the older HTML helpers so you can add your own ID that way (you need to do this for Html.LabelFor() helpers)
<%: Html.TextBox( "EmailAddress", Model.EmailAddress, new { id = 'my-unique-id" } ) %>

Where is the erroneous "Id" coming from?

I'm experimenting with the MVC beta 2 and have the following method in a controller:
[HttpPost]
public ActionResult Create(Issue issue) {
if(TryUpdateModel(issue, "Model", new [] {"Title", "Description"})) {
ServiceCaller.PutIssue(issue);
return RedirectToAction("Index");
}
return RedirectToAction("Create");
}
TryUpdateModel always fails and ModelState has an error under a Key called "Id" which says that "A value is required". The View contains no Id input field and I understood that my "include" argument on the TryUpdateModel should have ignored anything other than the explicitly included fields anyway. Changing the method signature to the following fixes the problem but I'd like to understand how the "Id" field is getting included in the first place.
public ActionResult Create([Bind(Exclude = "Id")]Issue issue)
For completeness, here's the View (it's a shared view in the EditorTemplates folder):
<%# Control Language="C#" Inherits="System.Web.Mvc.ViewUserControl<Tracker.Processing.Model.Issue>" %>
<% using (Html.BeginForm(Model.Id > 0 ? "Edit" : "Create", "Issue", FormMethod.Post)) { %>
<p>
<%= Html.LabelFor(i => i.Title) %>
<%= Html.EditorFor(i => i.Title) %>
</p>
<p>
<%= Html.LabelFor(i => i.Description) %>
<%= Html.TextArea("Description", Model.Description, new{ style = "width: 100%;" }) %>
</p>
<p>
<input type="submit" value="Save" />
</p>
<% } %>
Any ideas?
Its attempting to match the Id in the route to the one on your object
as the one in the route is null and the Id on your object is not nullable you get the error
Irritating isn't it

Categories