Reputation: 605
I'm trying to create form from such model:
class NewContractorModel
{
//...
public PhotoModel photos { get; set; }
//...
}
class PhotoModel
{
public List<Photo> f { get; set; }
}
From controller I do some manipulation (actually I removed some photos from the collection) on the model object and put them into the view page using this:
return new View("SomeView", model);
I've tried to create inputs (lets say hidden inputs) for each Photo.
for (int i = 0; i < Model.photos.f.Count; ++i)
{
@Html.HiddenFor(m => m.photos.f[i].Uri)
@Html.HiddenFor(m => m.photos.f[i].ThumbnailUri)
@Html.HiddenFor(m => m.photos.f[i].SmallThumbnailUri)
@Html.TextBoxFor(m => m.photos.f[i].Description, new { placeholder = "Dodaj opis" })
}
But as I noticed that this doesnt work because it dismiss all of model modifications (it still stores all Photos in List despite the fact that I've removed them in Controler method).
Then I tried this code:
for (int i = 0; i < Model.photos.f.Count; ++i)
{
Photo photo = Model.photos.f[i];
<input id="photos_f_@{@i}__Uri" name="photos.f[@{@i}].Uri" type="hidden" value="@photo.Uri"/>
<input id="photos_f_@{@i}__ThumbnailUri" name="photos.f[@{@i}].ThumbnailUri" type="hidden" value="@photo.ThumbnailUri"/>
<input id="photos_f_@{@i}__SmallThumbnailUri" name="photos.f[@{@i}].SmallThumbnailUri" type="hidden" value="@photo.SmallThumbnailUri"/>
<input id="photos_f_@{@i}__Description" name="photos.f[@{@i}].Description" placeholder="Dodaj opis" type="text" value="@photo.Description"/>
}
...and this time IT WORKS!
Can anyone explain me what is the difference between those two parts of code? I've tried to swich this code more than ten times and it always work the same so it's not my fault. ;) I think that there is a bug in HtmlHelper methods but is there any walk-around ? I'd like to use helpers methods instead of raw html.
EDIT: This is simplified controller class.
public class MyController
{
private NewContractorModel _model = null;
protected override void OnActionExecuting(ActionExecutingContext filterContext)
{
_model = SerializationUtility.Deserialize(Request.Form["Data"]) as NewContractorModel;
if (_model == null)
_model = TempData["Data"] as NewContractorModel;
if (_model == null)
_model = new NewContractorModel() as NewContractorModel;
TryUpdateModel(_model);
}
protected override void OnResultExecuted(ResultExecutedContext filterContext)
{
if (filterContext.Result is RedirectToRouteResult)
TempData["Data"] = _model;
}
private bool CheckModel(object model)
{
Type type = model.GetType();
PropertyInfo[] properties = type.GetProperties();
foreach (PropertyInfo p in properties)
{
object[] attr = p.GetCustomAttributes(true);
foreach (object a in attr)
{
if (a is ValidationAttribute)
{
object value = p.GetValue(model, null);
if (!((ValidationAttribute)a).IsValid(value))
return false;
}
}
}
return true;
}
protected ActionResult SelectPage(string delPhoto)
{
if (!CheckModel(_model))
{
// Do some action
}
//.....
foreach (ZAY.Database.Photo p in _model.photos.f)
{
if (p.Uri == Request["delPhoto"])
{
_model.photos.f.Remove(p);
break;
}
}
//.....
return View("SomeView", _model);
}
}
I noticed that inside lambdas the model looks just like after TryUpdateModel call (before modifications). If I don't use lambdas the model is modified... :/
And also my Photo class (generated from EntityFramework - so there is nothing interesting) and also simplified:
public class Photo : EntityObject
{
[Required]
public string Uri { get; set; }
[Required]
public string ThumbnailUri { get; set; }
[Required]
public string SmallThumbnailUri { get; set; }
public string Description { get; set; }
}
I'm sorry that I'm writing only such small snippets but the whole code is more complicated - there is only the most interesting part of it.
Upvotes: 3
Views: 1211
Reputation: 605
This is the answer to my problem:
I wonder why it is not mentioned in documentation... :/
Upvotes: 1
Reputation: 79033
From your description, I don't really understand what's going wrong in your first sample. But you certainly have a problem with the scope of the loop variable i
.
Since the expression m => m.photos.f[i]
involves closures, it will be evaluated at a later time, at a time when the for loop has already finished. The expression captures the variable i
(and not the value of the variable i
). When it is eventually evaluated, it finds the value Model.photos.f.Count
in the variable i
. So all hidden fields and textboxes will use the same invalid value of i.
Your second code sample avoids this problem by using a local variable within the for loop.
Upvotes: 0