ASP.NET MVC Binding issue - asp.net-mvc

I have an object with a property called "name".
This object has a sub object that has a property called "name" as well.
Transaction.name
Transaction.TransactionItem
TransactionItem.name
I bind Transaction object to a partial control as usual:
Html.TextBox("name", Model.name)%>
Model is a Transaction object.
And I bind TransactionItems:
<% if (Model.mtTransactionItem != null)
{
foreach (var item in Model.mtTransactionItem)
{ %>
<tr>
<td>
<%= Ajax.ActionLink(item.name, "ShowItem", new { id = item.id }, new AjaxOptions { UpdateTargetId = "dialog-form" })%>
</td>
And when I update the one of the transaction items through an ajax call I pass the entire transaction object to the partial view.
When I debug I check the Model.name property, and it has a proper value.
But on the page shows the name of TransactionItem value instead of the name of Transaction value.
What am I doing wrong?
I have checked this problem in MVC 1.0 and MVC 2.0 framework.

Your description is not clear to me, but I'll give you good advice. Instead of creating fields like that:
Html.TextBox("name", Model.name)
use
Html.TextBox("transaction.name", Model.Name)
and then
ActionResult Save(Transaction transaction);
Value of prefix has to be the same as parameter in function.
If you show components for items on the same page, use
Html.TextBox("transactionitems[i].name", Model.name)
or for one item
Html.TextBox("transactionitem.name", Model.name)
Don't use the same field name for different components on page, because it can cause problems with ModelState. Read about using prefixes, this will propably save some of your problems.
Also remember that with MVC 2 you have DataAnnotations, which make creating forms even easier.

Related

How to assign a dynamic value to the label helper in asp.net MVC

I am working on a Asp.net MVC 2.0 web application. In my form , i have non editable fields , so i wanted to display them as labels rather than textboxes.
I am strongly binding my model with view. So, i need to associate this label with one of the fields in model.
This is what i am trying to do:
<%=html.LabelFor(model=>model.changedby)%>
<%=html.DisplayFor(model=>model.changedby,XYZ)%>
But, it is displaying nothing..Please help
Updated2:
What basically i am trying to do is a add operation. i have a create view and that view has a form.
I am strongly binding this view with model.So , that i can directly associate form fields with the model Properties.
Ex:
<label> Name</label> <%=Html.TextBoxFor(m=>m.name)
So, what ever i type into the textbox , it will be stored in m.name in the model.
If the text entered is "Avinash" , then m.name gives value "Avinash"
I think i am correct upto this extent:
Similarly..
I have a field which is readonly , the user can not change the value of it.
<label>Changed On</label> <label> DateTime.Now </label>
How to bind m.ChangedOn with the labels values(DateTme.Now)
so that it will result in m.Changedon as DateTime.now
Updated3:
This is what i am writing..
<td >
<%=Html.LabelFor(Model=>Model.CreatedOn) %>:
</td>
<td>
<%=Html.HiddenFor(Model=>Model.CreatedOn) %>
</td>
You no need to wrap with <label>, since MVC LabelFor will automatically create those html tags for you
So change this
<label>
<%=html.LabelFor(model=>model.changedby)%>
</label>
to
<%= Html.LabelFor(model=>model.changedby) %>
Update:
If you want to send the data to the server while you post your form, make sure you have a data in a form fields. Only form fields like input,select,textarea are posted to server and not display tag value like label, span, div, etc
Still if you need to post the label value, you can use hidden with it
<%= Html.LabelFor(model=>model.changedby) %>
<%= Html.HiddenFor(model=>model.changedby) %>
If you have both, your hidden field will posted to server, which contains the same value
Controller code
public ActionResult Index()
{
MyviewModel model=new MyviewModel();
model.ChangedOn=DateTime.Now;
return View(model);
}
For Save
public ActionResult Save(MyviewModel model)
{
model.ChangedOn; // this property will show you hidden value
//If you need current time, since the rendered time was old. Its good to assign like below
//model.ChangedOn=DateTime.Now
}
use this
<%= Html.DisplayFor(model => model.changedby) %>

Losing ViewModel Data after POST

I don't see this problem too often but I've got a .cshtml that uses a layout. In the layout I've got:
#using (Html.BeginForm(null, null, FormMethod.Post, new { #class = "someCssClass", #id = "UserForm" }))
{
...rest of the code
}
My main .cshtml using this layout has the model defined at the top as we always do:
#model CarViewModel
#{
Layout = "~/Views/Shared/_CarLayout.cshtml";
}
When It gets back to my action method, I get nulls for all values of the model:
public ActionResult Cars(CarViewModel model)
{
carBL.RemoveCars(model.CarIds, model.DealerId);
...
}
Not sure what I need to do here and why this is happening. Usually I just get it back successfully via autobind. It seems to me when the model is used via RAzor in the markup- that gets posted back fine with the returned ViewModel but if I'm not using those fields, it doesn't...so I assume that's how that works and if I don't use them in mark-up I need to send them back as hidden values then to force the persistence since I am not using x fields from the ViewModel (Which would have automatically persisted those fields if I had used them in the form)?
If the values are not bound to a form field, they will come back null.
in the form use the below for things like ID fields.
#Html.HiddenFor(x => x...)
A quick test, to see if the form is being posted correctly would be to modify the signature of your action:
public ActionResult Cars(FormCollection form)
{
...
}
If form is not populated then you have an issue with the form post. As a side, note you could accomplish this when reviewing the post data of the form with a tool like FireBug, Chrome Dev tools or Fiddler if you prefer.
If the form is posting correctly, then I you should check to make sure the name's of the input fields on the form align with the names of the CarViewModel you are expecting.
Not sure if this has been resolved yet, but this is how I do it (partial code):
#model MyProject.ViewModels.MyViewModel
#using (Html.BeginForm())
{
<table>
<tr>
<td>First Name:</td>
<td>#Html.TextBoxFor(x => x.FirstName, new { maxlength = "50" })
#Html.ValidationMessageFor(x => x.FirstName)
</td>
</tr>
</table>
<button id="btnSave" type="submit">Save</button>
<button id="btnCancel" type="button">Cancel</button>
}
Then my action method to handle the HTTP post request:
[HttpPost]
public ActionResult Create(MyViewModel viewModel)
{
// Check for null on viewModel
// Do what needs to be done
}
Doing it this way should not let you loose your values filled in on the form/view.

asp.net sending a List<> of items to controller

I have the following form:
<li>
<% using (Html.BeginForm("TestMethod", "MyController", FormMethod.Post, new {id = "TestMethod"}))
{%>
<%= Html.Hidden("model", Model.MyListOfObjects) %>
<%}%>
Test
</li>
And the javascript function for the onclick is as follows:
function SubmitForm() {
document.forms["TestMethod"].submit();
}
I am trying to pass the list of objects from the view into the controller, but i have yet managed to get this to work. My Controller function is:
[Authorize]
[HttpPost]
public ActionResult TestMethod(List<Objects> model)
{
dynamic Expando = new ExpandoObject();
Expando.test = model;
return View(Expando );
}
When I view the List of objects in the debugger it always displays "System.Collections.Generic.List`1[]" with no actual objects inside.
So my question is what should I be doing to pass a List of objects into a controller?
I have also tried:
<% using (Html.BeginForm("TestMethod", "MyWork", FormMethod.Post, new {id = "TestMethod"}))
{%>
<% int itemx = 0; %>
<% foreach (var x in Model.MyListOfObjects)
{%>
<%= Html.Hidden("model"+"["+itemx+"]", x) %>
<%itemx++; %>
<% } %>
<%}%>
You cannot just put List<object> as action parameter and expect the model binder to be able to automagically guess what object types you want to put there. You will need to write a custom model binder if you wanted to handle multiple sub-types as illustrated in this post.
And if you want to use a single type for the list such as List<MyViewModel> then simply loop through each element of the list (respecting the convention) and for each element build a hidden field for each property that you want to bind.
But since those are hidden fields, I guess that the user is not supposed to modify them. In this case those hidden fields have nothing to do in your view. Let's not reinvent the ViewState that we were all so happy to get rid of when we moved to ASP.NET MVC from classic WebForms. Simply put a hidden field containing an unique id that will allow you to refetch the corresponding list elements in the POST action given this unique id from wherever you fetched them initially (your database or something I suppose).
You need to have one hidden element for each object in the list, and named model[0], model[1], etc.

asp.net mvc ViewModel in a View with List of objects has old list items displayed after removing in controller

I have poked around but not found out what the ViewModel, or TempData or how my objects are being persisted for my form.
I display a custom view model in an asp.net MVC view, I edit a list of objects in that view and display them all inside a dynamic grid inside an html form, then submit the changes. When I get back to the controller I check existing objects vs the forms submitted object list and update the objects.
When I redisplay the form, objects that were deleted still show up and have values in the textbox inside the html elements, so it is asp popluating the fileds and not a browser cache. I have a checkbox that displays next to the row if it is an existing object already and those checkboxes are submitted to the controller as an array of values (the id of the object to remove).
So I delete the object, pull clean ones out of the database and set the list in the viewmodel with the newly retrieved data. However, the form shows the old object still, but there is no delete checkbox next to them so they were not retrieved from the database.
How do I fix that? I tried tweaking the methods output cache (not a browser issue as the DB ID key does not exists anymore ... no delete checkbox). I tried making a new view model an explicitly setting variables before sending to the view...no go.
My solution for now was to redirect to the get method after I edit all of the objects (simpleObject in the example) and start completely over.
A simplified example is as follows:
public class CustomViewModel {
List<SimpleObject> objects {get;set;}
}
public class SimpleObect {
public int iA {get;set;}
public int AddonHistID {get;set;}
}
Controller:
[HTTPGet] // get method and displays 2 objects by default
public ActionResult whatever( string something){
CustomViewModel form = new CustomViewModel ();
form.objects = new List<SimpleObject>();
form.objects.Add( new SimpleObect());
form.objects.Add( new SimpleObect());
return View( form)
}
[HttpPost]
public ActionResult whatever( string something, CustomViewModel form){
// adjust objects to show current objects aftering saving changes (reload and rebind to ModelView)
form.objects = getObjectsAfterChange( something); // just gets objects from db after all changes are made in this controller action
return View( form);
}
View:
<% using( Html.BeginForm()) { %>
<table width="800" id="SearchAddonsResults">
foreach( SimpleObject addonHist in Model.objects )
{
++iOdd;
string cssClass = (iOdd % 2 == 0 ? "rowOdd" : "rowEven");
%>
<tr class="<%= cssClass %>">
<td>
<%if (addonHist.AddonID > 0)
{ %>
<input type="checkbox" name="RemoveAddon" id="RemoveAddon" value="<%= addonHist.AddonID.ToString() %>" />
<% } %>
<%= addonHist.AddonHistID.ToString() %>
</td>
<td><%= addonHist.iA.ToString() %></td>
</tr>
<% } %>
</table>
<% }; //endform %>
I think this might help you get the results you expect.
Phil Haack's Blog:
http://haacked.com/archive/2008/10/23/model-binding-to-a-list.aspx
Which is linked to from:
How to model bind to a List<ViewModel>?
Complex model binding to a list
How ASP.NET MVC: How can I bind a property of type List<T>?
So I delete the object, pull clean ones out of the database
and set the list in the viewmodel with the newly retrieved
data. However, the form shows the old object still,
This makes no sense. Are you absolutely sure that this line
form.objects = getObjectsAfterChange( something);
in your HttpPost method is retrieving the right information?
I would start by inspecting the value that getObjectsAfterChange( something) is returning . I suspect it's returning more than you think and that's where the problem is (rather than on the render of the view.)

ASP.NET MVC 2 Html.TextAreaFor not updating on Edit

I have a form that lets the user enter in some text. It will be longer than a few characters so I want to use a TextArea instead of a TextBox.
The Html.TextBoxFor works without issue, and the Html.TextAreaFor works when I create the entry, but does not store the new value when I edit it and shows whatever the value was before I went to edit it upon saving.
On Page:
<div>
<label>Work Performed:</label>
<%: Html.TextAreaFor(model => model.WorkPerformed)%>
<%: Html.ValidationMessageFor(model => model.WorkPerformed) %>
</div>
Code Behind for Create:
maintPerformed.MaintDate = DateTime.Parse(Request.Form["MaintDate"]);
maintPerformed.WorkPerformed = Request.Form["WorkPerformed"];
maintPerformedRepository.Add(maintPerformed);
maintPerformedRepository.Save();
return RedirectToAction("Details", new { id = maintPerformed.ID });
Code Behind for Edit:
maintPerformed.MaintDate = DateTime.Parse(Request.Form["MaintDate"]);
maintPerformed.WorkPerformed = Request.Form["WorkPerformed"];
maintPerformedRepository.Save();
return RedirectToAction("Details", new { id = maintPerformed.ID });
What am I missing on the edit side of things?
In both cases you are redirecting to the Details action. So make sure that maintPerformedRepository.Save() actually does something and most importantly in your Details action look what value is fetched from the data store. I suspect that either the database is not updated or in your Details action you are fetching a wrong value for your model.
Remark: Instead of writing the ugly DateTime.Parse(Request.Form["MaintDate"]); and Request.Form["WorkPerformed"]; pass your view model as argument to your controller action and let the model binder populate it from the request values.

Resources