What's up with this? The viewmodel variable is a bool with value true.
<%= Html.HiddenFor(m => m.TheBool) %>
<%= Html.Hidden("IsTimeExpanded",Model.TheBool) %>
<input type="hidden" value="<%=Model.TheBool%>" name="TheBool" id="TheBool">
Results in:
<input id="TheBool" name="TheBool" value="False" type="hidden">
<input id="TheBool" name="TheBool" value="False" type="hidden">
<input value="True" name="TheBool" id="TheBool" type="hidden">
What am I doing wrong? Why don't the helpers work as intended?
1) use different (unique) ids
2) don't use this helper, use
<input type="hidden" name="the-name"
value="<%= Html.AttributeEncode(Model.TheBool) %>" id="TheBool_1216786" />
As answered here the problem is that HTML helpers by default use the posted values (if available) then refer to the model. Personally I don't think this makes a whole bunch of sense and now wonder how many other bugs lie in wait throughout our platform.
Anyway, the solution posted in the aforementioned answer will solve the problem, just add this line before you return from the controller:
ModelState.Remove("TheBool")
And yes, it's a bit rubbish because you can only use a string reference... but it does work.
Here's an example in razor:
html:
#Html.HiddenFor(x => Model.TheBool, new { #id = "hdnBool" })
javascript:
alert($('#hdnBool').val());
model:
public class MyModel()
{
public bool TheBool{ get; set; }
}
I had similar and ended up getting round it like this.
The situation is the user wants a Save and then confirm save scenario....
I chose to use the solution below rather than
ModelSate.Remove("OperationConfirmed");
(which does work) as I feel it is more intuative....
#{
string btnSaveCaption = "Save Changes";
if (Model.OperationConfirmed)
{
btnSaveCaption = "Confirm Save Changes";
#Html.Hidden("OperationConfirmed", true)
}
}
Related
I am looking for a solution which creates dynamic properties to be create in Model.
I want to use them in my View and Controller.
Can any one have idea, how to create it?
I am having scenario in my project where one page will be having options to be lets say Profile2, Profile5 etc.
Profile 2 can have two URLs to be submit from user.
Profile 5 can have five URLs to be submit from user.
and
So on........
Is there any solution or alternative to do this????
He Amit , For your situation "I am having scenario in my project where one page will be having options to be lets say Profile2, Profile5 etc.
Profile 2 can have two URLs to be submit from user.
Profile 5 can have five URLs to be submit from use"
You want to put this url in your properties ok.
SO do one thing create a property like this.
public List<string> urlList {get;set;}
use this in your property andd add url in the list.
you can add n no of urls.
That is exactly what ViewBag is for. Its a dynamic property on Controller and View.
public ActionResult SomeAction()
{
ViewBag.Message = "Hello, world";
}
<p>#ViewBag.Message</p>
This will allow you to send anonymous property values from your Controller to your View. However, if you're looking to post different numbers of values (urls in your example), you should use an IList as your model.
#model IList<string>
#for (int i = 0; i < Model.Count; i++)
{
#Html.EditorFor(model => model[i])
}
Your model should probably store the values in a list. Here is an example explaining how to display and save data for a list property.
How to interact with List<t> in MVC
see this is what i have done as an alternative.
Make all divs and other fields in MODEL and use jQuery to work around.
I guess this is an alternative, but not exactly what i want. Still looking for answer. I post this as this can be helpful to some one in future.
Please check below.
<div>
#for (var i = 0; i < ProfileCount; i++)
{
<label>
URL:</label>
<input type="text" id=#string.Format("URL{0}", i) />
<label>
CheckName:</label>
<input type="text" id=#string.Format("URL{0}CheckName", i) />
<label>
Run Check From:</label>
#Html.DropDownList(string.Format("URL{0}Region", i), (IEnumerable<SelectListItem>)ViewBag.Regions)
<br />
<span id=#string.Format("URL{0}Result", i)></span>
<input type="button" value="Create check" id=#string.Format("URL{0}CheckSetup", i) onclick="getResponseFromUrl('#string.Format("URL{0}')", i);" />
<input type="button" value="Delete check" id=#string.Format("URL{0}Delete", i) onclick="DeleteCheck('#string.Format("URL{0}')", i);"
style="display: none" />
<input type="hidden" id=#string.Format("URL{0}Hidden", i) />
<br />
<br />
<br />
}
</div>
I have some problems with ASP.NET MVC’s default model binder. The View contains HTML like this:
<input name="SubDTO[0].Id" value="1" type="checkbox">
<input name="SubDTO[1].Id" value="2" type="checkbox">
This is my simplified ‘model’:
public class SubDTO
{
public virtual string Id { get; set; }
}
public class DTO
{
public List<SubDTO> SubDTOs { get; set; }
public DTO()
{
SubDTOs = new List< SubDTO>();
}
}
All this works fine if the user selects at least the first checkbox (SubDTO[0].Id). The controller ‘receives’ a nicely initialised/bound DTO. However, if the first check box is not selected but only, for example, SubDTO[1].Id the object SubDTOs is null. Can someone please explain this ‘strange’ behaviour and how to overcome it? Thanks.
Best wishes,
Christian
PS:
The controller looks like this:
[Transaction]
[AcceptVerbs(HttpVerbs.Post)]
public RedirectToRouteResult Create(DTO DTO)
{
...
}
PPS:
My problem is that if I select checkbox SubDTO[0].Id, SubDTO[1].Id, SubDTO[2].Id SubDTOs is initialised. But if I just select checkbox SubDTO[1].Id, SubDTO[2].Id (NOT the first one!!!) SubDTOs remains null. I inspected the posted values (using firebug) and they are posted!!! This must be a bug in the default model binder or might be missing something.
This behavior is "by design" in html. If a check-box is checked its value is sent to the server, if it is not checked nothing is sent. That's why you get null in your action and you'll not find value in the posted form either. The way to workaround this is to add a hidden field with the same name and some value AFTER the check-box like this:
<input name="SubDTO[0].Id" value="true" type="checkbox">
<input name="SubDTO[0].Id" value="false" type="hidden">
<input name="SubDTO[1].Id" value="true" type="checkbox">
<input name="SubDTO[1].Id" value="false" type="hidden">
In this way if you check the check-box both values will be sent but the model binder will take only the first. If the check-box is not checked only the hidden field value will be sent and you\ll get it in the action instead of null.
I think this post on Scott Hanselman's blog will explain why. The relevant line is:
The index must be zero-based and unbroken. In the above example, because there was no people[2], we stop after Abraham Lincoln and don’t continue to Thomas Jefferson.
So, in your case because the first element is not returned (as explained by others as the default behaviour for checkboxes) the entire collection is not being initialized.
Change the markup as follows:
<input name="SubDTOs" value="<%= SubDTO[0].Id %>" type="checkbox">
<input name="SubDTOs" value="<%= SubDTO[1].Id %>" type="checkbox">
What's being returned by your original markup is an unrelated set of parameters, i.e. like calling RedirectToRouteResult Create(SubDTO[0].id, SubDTO[1].id, ..., SubDTO[n].id) which is clearly not what you want, you want an array returned into your DTO object so by giving all the checkboxes the same name the return value to your function will be an array of ids.
EDIT
Try this:
<input name="SubDTO[0].Id" value="<%= SubDTO[0].Id %>" type="checkbox">
<input name="SubDTO[0].Id" value="false" type="hidden">
<input name="SubDTO[1].Id" value="<%= SubDTO[1].Id %>" type="checkbox">
<input name="SubDTO[1].Id" value="false" type="hidden">
You have to return something to make sure there is an element for each index, I suspect that any gap will cause a problem so I'd suggest using a 'null' ID, for example 0 or -1 and then process that out later in your code. Another answer would be a custom model binder.
There is always the alternate option of adding a property to your class that takes an array of strings and creates the SubDTO array from that.
public List<string> SubDTOIds
{
get { return SubDTO.Select(s=>s.Id).ToList(); }
set
{
SubDTOs = new List< SubDTO>();
foreach (string id in value)
{
SubDTOs.Add(new SubDTO { Id = id });
}
}
}
or something like that
I have an HTML table where each row has buttons which toggle status bits in the database for each row. Assuming Javascript is not an option, what would be the "best practice" way of handling this?
I'm currently handling it by wrapping each row in a form like this:
<table>
<tr>
<td>
<form action="/FooArea/BarController/BazAction" id="frm0" name="frm0" method="post">
<span>Item 1</span>
<input type="submit" value="Toggle1" name="submitButton" />
<input type="submit" value="Toggle2" name="submitButton" />
<input type="hidden" name="itemID" value="1" />
</form>
</td>
</tr>
<tr>
<td>
<form action="/FooArea/BarController/BazAction" id="frm1" name="frm1" method="post">
<span>Item 2</span>
<input type="submit" value="Toggle1" name="submitButton" />
<input type="submit" value="Toggle2" name="submitButton" />
<input type="hidden" name="itemID" value="2" />
</form>
</td>
</tr>
</table>
And the post method looks something like this:
string buttonName = Request.Form["submitButton"];
if (!String.IsNullOrEmpty(buttonName ))
{
int itemID = Convert.ToInt32(Request.Form["itemID"]);
switch (buttonName )
{
case "Toggle1":
DoSomething(itemID);
break;
case "Toggle2":
DoSomethingElse(itemID);
break;
}
}
Any better suggestions? Is having 50 forms on a page cool? I dunno.. let me know what you are doing in this case.
The best way to handle POST scenarios without JavaScript is, as several others have stated, a tiny form with only the necessary values, and only one submit button. Basically, what you should do is to create a helper method that creates a form with the necessary POST values in hidden fields and a submit button. For example, you could have a method you use like this:
<%= Html.PostLink("FooArea/BarController/BazAction", "Toggle1", new List<KeyValuePair<string, string>>{ new KeyValuePair<string, string>("itemId", 1), new KeyValuePair("action", "option1") }); %>
It looks pretty verbose, but I've tried to make it as generic as possible. You can probably create the List<KeyValuePair<string, string>> in the controller when you render the view, so you only have to call something
<%= Html.PostLink("FooArea/BarController/BazAction", "Toggle1", Model.Values) %>
In the action method that handles the post, you bind to the posted FormCollection, and retrieve the values of itemId and action to determine what to do, instead of checking for Request.Form values.
An implementation of the helper method might look like this:
public static string PostLink(this HtmlHelper helper, string postAction, string submitText, IEnumerable<KeyValuePair<string, string>> postValues)
{
var form = new TagBuilder("form");
// Setup basic properties like method, action
form.Attributes.Add("method", "post");
form.Attributes.Add("action", postAction);
// Instantiate a stringbuilder for the inner html of the form
var innerHtml = new StringBuilder();
// Create and append hidden fields for the post values
foreach(var value in postValues)
{
var hidden = new TagBuilder("input");
hidden.Attributes.Add("type", "hidden");
hidden.Attributes.Add("name", value.Key);
hidden.Attributes.Add("value", value.Value);
innerHtml.Append(hidden.ToString(TagRenderMode.SelfClosing));
}
// Create the submit button
var submit = new TagBuilder("input");
submit.Attributes.Add("type", "submit");
submit.Attributes.Add("value", submitText);
// Append it to the stringbuilder
innerHtml.Append(submit.ToString(TagRenderMode.SelfClosing));
// Set the InnerHtml property of the form, and return it
form.InnerHtml = innerHtml.ToString();
return form.ToString(TagRenderMode.Normal);
}
This seems like a prime case for some JQuery and Ajax functionality. But if javascript isn't an option then i think wrapping each one in a form is probably the most simple solution.
When I need to do something like this I do it pretty much the same way as you do here. It is no problem to have multiple forms in one view. Its like having multiple links in one view (but they use POST instead of GET). I guess many .net developers think its wrong because of webforms only having one form tag. In your case I would even create more forms for each submit button as they seem to do different things. I consider having to check the name of the button in the controller to be a code smell. If they do something like activate/deactivate you should probably post that as a bool or something and have both forms post to the same action.
I think technically this is the best way to go. From a usability point of view you can question if this is the best way to do it. More than 20 items in a list can get quite confusing. But ofcourse, I don't really know what you are programming :)
I agree with Mattias about creating different actions for the activate and deactivate-button. This way you avoid having to use a switch-statement.
Consider the following code:
public ActionResult Edit(int id)
{
return View(db.Foos.Single(x => x.Id == id));
}
When user submits the changes, I would like to receive both original and current object values, such that the Update code can be:
Foo foo = db.Foos.Attach(current, original);
db.SubmitChanges();
I see two options:
1) Render a number of hidden inputs containing original values
<input type="hidden" name="original.A" value="<%= Model.A %> />
<input type="hidden" name="original.B" value="<%= Model.B %> />
<input type="text" name="current.A" value="<%= Model.A %>
<input type="text" name="current.B" value="<%= Model.B %>
and submit to:
public ActionResult Update(Foo current, Foo original)
{
Foo foo = db.Foos.Attach(current, original);
db.SubmitChanges();
}
2) Use some serialization/deserialization into one hidden field
<input type="hidden" name="original" value="<%= Serialize(original) %> />
and sumbmit to:
public ActionResult Update(Foo current, string original)
{
Foo original = DeserializeFrom<Foo>(original);
Foo foo = db.Foos.Attach(current, original);
db.SubmitChanges();
}
Are there any other options? Or tools that make writing such code easier?
EDIT:
To be more clear... the idea of keeping original value is to eliminate extra select that happens if code written this way:
public ActionResult Update(Foo changed)
{
Foo original = db.Foos.Single(x => x.Id == changed.Id);
MyUtils.CopyProps(original, current);
db.SubmitChanges();
}
make some custom HtmlHelper extension methods that simply write out both the hidden and textbox element. That way, your view markup stays simple, but you still get the pre/post state tracking in your post info.
I would stray away from the serialization option :-/
While I wouldn't know how to solve your problem, I can tell you that what you're thinking of would be extremely unsafe. In fact nothing would stop the client from altering the data sent through the request and in the best case have invalid data entered in your database. You should not trust the client with hidden fields, querystrings or cookies containing data you have to insert (unless you sign the data sent to the client in the first place and check the signature later).
Quick question regarding updating a list of items in asp.net mvc.
Basically I have an edit action method that returns a collection of objects (incidentally, the table structure of which looks as follows 'testID, assetID, Result' - a link table).
I basically want this items to be displayed one after another in a form and to be able to edit them. The form should post back and the modelbinder do its magic. But, its not that easy.
I have scoured the net and it seems the majority of the information about this stuff seems to be a little out of date. I've come across this post, which has not been updated in a long time, and this one which seems to suggest that you shouldn't bind to a already existing list for updating, and that there are problems when working with EF or Linq to Sql (which I am).
Is there an easy way to achieve what I want? Has the state of list model binding changed in the release version?
UPDATE - A little closer...
Here's my Edit method:
public ActionResult EditSurveyResults(Guid id)
{
var results = surveyRepository.GetSurveyResults(id);
return PartialView("EditSurveyResults", results);
}
And my form:
<div id="editSurveyResults">
<h2>
EditSurveryResults</h2>
<%= Html.ValidationSummary("Edit was unsuccessful. Please correct the errors and try again.") %>
<% using (Html.BeginForm())
{%>
<fieldset>
<legend>Results</legend>
<% int i = 0; foreach (var result in Model)
{ %>
<input type="hidden" name='results[<%= i %>].TestID' value='<%= result.TestID %>' />
<input type="hidden" name='results[<%= i %>].AssetID' value='<%= result.AssetID %>' />
<p>
<%= result.Task.TaskName%>
</p>
<p>
<label for="Result">
Result:</label>
<input type="text" name='results[<%= i %>].Result' value='<%= result.Result %>' />
<%= Html.ValidationMessage("Result", "*")%>
</p>
<% i++; } %>
<p>
<input type="submit" value="Save" />
</p>
</fieldset>
<% } %>
And my Edit POST method:
[AcceptVerbs(HttpVerbs.Post)]
public ActionResult EditSurveyResults(Guid id, IList<SurveyTestResult> results)
{
var oldValues = surveyRepository.GetSurveyResults(id);
if (ModelState.IsValid)
{
UpdateModel(oldValues);
surveyRepository.Save();
return Content("Done");
}
else
return PartialView("EditSurveyResults");
}
It's not complete of course, but it doesn't update anything in its current state. Am I missing a trick here? results is populated with the the updated entities so I'm not sure why its not updating...
UPDATE 2:
So, Im starting to think that the model binder cant do stuff like this. So, I've resorted to doing things in a more hacky way. If anyone can spot a problem with this then please let me know. FYI - this form will be grabbed with AJAX so I dont return a view, rather a simple message.
Here's the new code:
IList<SurveyTestResult> oldValues = surveyRepository.GetSurveyResults(id).ToList();
foreach (var result in SurveyTestResult)
{
//SurveyTestResult is the IList that comes down from the form.
SurveyTestResult thisone = oldValues.Single(p => p.AssetID == result.AssetID &&
p.TestID == result.TestID);
//update the old entity with the result from the new one
thisone.Result = result.Result;
}
And then I call Save on my repository.
Thanks in advance
One thing i noticed is that your not rendering <input type="hidden" name='results.Index' value='<%= i %>' /> as phil Haacks article mentions is mandatory.
Switching to a different Modelbinder might do the trick too. I use the DataAnnotations model binder and with that i dont have to generate .Index fields when binding to List's.