I am developing an edit form. The form i need to create needs to be dynamic so i went down the road of using EditorForModel and have many different but similar models in behind. To customize each field, i use EditorTemplates for control like dropdowns, checkboxes and radiobuttons. The project needs to be delivered soon and i have done almost everything but stuck with few things.
Here is my Sex.cshmt as EditorTemplate
#Html.RadioButton("", "M") Male
#Html.RadioButton("", "F") Female
The Edit view doesn't have much as well.
#using (Html.BeginForm("Edit", "Editor", FormMethod.Post))
{
#Html.AntiForgeryToken()
#Html.ValidationSummary(true)
#Html.HiddenFor(m => m.Id)
#Html.EditorForModel()
<input type="submit" class="btn btn-default" value="Save" />
}
And i got a model with few properties. Very simple. Field for Sex has UIHint annotation so that it can pick up Sex editor template. And there are some Required annotation for some properties as well. These work fine.
And i got my controller action to return right type of person and return a view using a Factory. Just a standard factory with a switch statement that return right object.
public ActionResult Edit(int p)
{
var person= _service.personService.Get(p);
var canFac = new CandidateFactory(type);
var res = canFac.CreateObject(person);
return PartialView("_Edit", res);
}
In the above code, res is an dynamically created object with properties. It has a property for Sex and it's a string with value of 'M' or 'F'. I know naturally radio buttons are bools but i have to map M or F to the radio buttons.
The form was generated fine. All the normal text-boxes are populated with data. But others like Radio buttons and check-boxes aren't populated. So, my question is, In my scenario, how do i pre-populate or pre-select radio-buttons with data?
You should make use of the html attributes:
#Html.RadioButton("M","M", Model == "M" ? new { Checked = "checked" } : null) Male
#Html.RadioButton("F","F,", Model == "F" ? new { Checked = "checked" } : null) Female
Related
I have 2 controller fields say Type and Data.
Depending on value selected for Type (Date or Text), I want to display Data field dynamically as either a text input or a custom timepicker input.
Since only one will be rendered at any time, I need to bind with the same property name (Data).
This is what I am trying:
#if (Model.Type == "Date")
{
// custom timepicker control goes here
<input asp-for="Data" class="form-control timepicker"/>
}
else
{
<input asp-for="Data" class="form-control text-input" type="text"/>
}
On page load only text input is rendered, and it shows/hides based on Type selected. The timepicker input is never displayed (the html is not generated at all).
Is there a way to achieve this in MVC?
You can not have two <input> elements with the same name. If a <form> containing multiple inputs with the same name is posted, the MVC model binder will only bind one value from the last input.
To achieve what you want, you have two options:
Either have only one input with name="Data" of type="text" in the View, and let the timepicker write the time as a string to this input. Then in the controller, parse this input value depending on the selected Type.
Or have two inputs with name="TextData" and name="TimeData", and disable and hide one of these inputs using JS depending on the selected Type. In the controller, read the value from the right input depending on the selected Type. This is arguably the cleaner solution.
In MVC5 the second solution would look like this (I am not familiar with MVC-Core):
#model MyViewModel
#using (Html.BeginForm("Submit", "MyController", FormMethod.Post)) {
#Html.EditorFor(m => m.Type)
#Html.EditorFor(m => m.TextData, new { #class = "text-input"})
#Html.EditorFor(m => m.TimeData, new { #class = "timepicker"})
}
<script type="text/javascript">
function toggleInput_() {
if ($('##Html.IdFor(m => m.Type)').val() === 'Text') {
$('##Html.IdFor(m => m.TextData)').prop('disabled', false).show();
$('##Html.IdFor(m => m.TimeData)').prop('disabled', true).hide();
}
else {
$('##Html.IdFor(m => m.TextData)').prop('disabled', true).hide();
$('##Html.IdFor(m => m.TimeData)').prop('disabled', false).show();
}
}
$(document).ready(function() {
$('##Html.IdFor(m => m.Type)').on('change', function() {
toggleInput_(); // toggle when drop down changes
});
toggleInput_(); // toggle initially on page load
});
</script>
Controller:
[HttPost]
public ActionResult Submit(MyViewModel postData) {
string textValue = null;
DateTime? timeValue = null;
if (postData.Type == "Text") {
textValue = postData.TextData;
}
else {
timeValue = postData.TimeData;
}
// ...
}
ASP MVC already has this functionality built in with Editor Templates. By following the convention, you can specify a template to be used for any type (including user-defined complex types) which will be rendered with #Html.EditorFor().
In a nutshell, just place two partial views in your ~/Views/Shared/EditorTemplatesfolder, one with model type DateTime and the other string. The correct partial view will be rendered when using #Html.EditorFor(m => m.Property) based on the type of Property.
Note: the default editor for a string property will already be an input with type="text", so you don't necessarily need to specify that template.
See this link for a tutorial on Editor templates (and Display templates):
https://exceptionnotfound.net/asp-net-mvc-demystified-display-and-editor-templates/
I try to pass some hidden data to my controller by using the hiddenFor, I know the value I want gets to the view, but after submiting the form the value stays null when it arrives in the controller. The data in EditorFor is passed correctly to the controller.
// View
#using (Html.BeginForm())
{
#Html.AntiForgeryToken()
// Some working editorFor fields. Data from these gets successfully received
// The name is correctly displayed in the paragraph
<p>#Model.name</p>
// This data is not received in the controller
#Html.HiddenFor(x => x.name)
<div class="form-group">
<div class="col-md-offset-2 col-md-10">
<input type="submit" value="Create" class="btn btn-default" />
</div>
</div>
}
// Controller
[HttpPost]
[ValidateAntiForgeryToken]
public ActionResult Create(Product product, HttpPostedFileBase image)
{
product.name = "a name";
return View(product);
}
I also tried using a normal named hidden, but this also didn't return a value.
Someone an idea what I missed?
You can pass the hidden fields automatically, if you have a form, using for example the razor helper
#using (Html.BeginForm("CreateTable", "Home", FormMethod.Post, null){ #HiddenFor(i => i.PropertyName) }
and the hidden fields must be inside of form, otherwise you will "lost" them.
Update following your updated question: Try remove the HiddenField and change <p>#Model.name</p>
to
#Html.LabelFor(i => i.Name)
I did focus on the incorrect thing, the problem was that I changed the model in the controller after the postback. But this only changes the model en does not changes the ModelState, which the form data uses.
//This is updated after model changes.
<p>#Model.name</p>
//For this you need to update the ModelState
#Html.HiddenFor(x => x.name)
In the controller you need to use ModelState.Remove(property name). (Or clear the complete ModelState)
//After removal of the property the ModelState will update to the new model value.
product.name = "a name";
ModelState.Remove("name");
return View(product);
In this article it's explained, https://weblog.west-wind.com/posts/2012/Apr/20/ASPNET-MVC-Postbacks-and-HtmlHelper-Controls-ignoring-Model-Changes.
I'm very new in MVC :(
I created a dynamic form by cloning a principal DIV element and its elements. The elements are combobox, textbox and a date textbox. When I create a new "clone", the DIV every member of itself has an incremental ID like tab_Container, tab_Container_1, text, text1, combo, combo1, etc... Now, I'm trying to get the values of each member in the Divs into the controller.
Googling I find something like this:
[HttpPost]
public ActionResult NewEntry(Model Entry)
{
Control myControl = new Control();
myControl.FindControl("Text0");
if (myControl != null)
{
/// apparently, find the control,here i wanna to get the value of each field !! ¿?
/// do i have to create a list[] ... exist something like Entry.Text0 = myControl.value?
}
else
{
Response.Write("Control not found");
}
return View(Entry);
}
Any suggestion? Is Control the best option? Do I have to do something else in Javascript code?
While it's normally better to have some sort of Model / ViewModel this situation is a bit different. MVC binds on the "Name" property of your form inputs.
So say for instance your razor syntax generates something like this:
<form>
<input type="text" name="input1" />
<input type="text" name="input2" />
<!-- etc etc etc -->
<input type="submit" value="submit" />
</form>
since this is dynamically generated and you don't have a model that would cleanly bind to this. You can use the FormCollection type as the parameter of your action. This gives you a collection of all items posted to the server that you could then loop through or peer into to get the properties that you want.
public ActionResult myAction(FormCollection collection)
{
var value = collection["input1"];
var value2 = collection["input2"];
return View();
}
I've following code in razor view. How do I use Kendo Radio button to render the same? Mainly, I'm struggling to assign enum value to radio button value.
#if (Model == declaredEnum.Val1)
{
#Html.RadioButtonFor(l => l, (int)declaredEnum.Val1, new { #checked = "checked" });
}
else
{
#Html.RadioButtonFor(l => l, (int)declaredEnum.Val1);
}
#Html.LabelFor(l => l, "Label")
Edit
Definition of Enum
Public Enum declaredEnum
{
Val1,
Val2
}
There is another radio button with the same code, but it checks for val2. Current logic is working fine. I just need to convert to Kendo control instead of razor.
I realize that this post is very old, but I am working with Kendo UI for the first time and needed to figure this out for myself. The following code snippet creates a radio button group called "name-of-radio-group" from an enum with two values. It defaults the first radio button to checked:
<div class="form-group row">
#Html.Label(Enum.GetNames(typeof(declaredEnum)).ToList()[0], new { #class = "col-sm-2" })
#(Html.Kendo().RadioButtonFor(m => m.declaredEnum).Checked(true).Name(Enum.GetNames(typeof(declaredEnum)).ToList()[0])
.HtmlAttributes(new { #class = "col-sm-1", name = "name-of-radio-group"}))
#Html.Label(Enum.GetNames(typeof(declaredEnum)).ToList()[1], new { #class = "col-sm-2"})
#(Html.Kendo().RadioButtonFor(m => m.declaredEnum).Name(Enum.GetNames(typeof(declaredEnum)).ToList()[1])
.HtmlAttributes(new {#class = "col-sm-1", name = "name-of-radio-group"})
)
</div>
In my example I only had two enum values so I did not feel the need to use a loop, instead I just indexed the enums directly. In case it is not clear to someone the m.declaredEnum represents a property on a strongly typed model where the property name is the same as the name of the enum.
I have 3 tables:
RateProfile
RateProfileID
ProfileName
Rate
RateID
RateProfileID
PanelID
Other stuff to update
Panel
PanelID
PanelName
I have models for each of these. I have an edit page using the RateProfile model. I display the information for RateProfile and also all of the Rates associated with it. This works fine and I can update it fine. However, I also added a dropdown so that I can filter Rates by PanelID. I need it to post back on change so that it can display the filtered rates.
I'm using
#Html.DropDownList("PanelID", (SelectList)ViewData["PanelDropDown"], new { onchange = "$('#RateForm').submit()" })
for my dropdownlist. Whenever it posts back to my HttpPost Edit method though, it seems to be missing all information about the Rates navigation property. It's weird because I thought it would do exactly what the input/submit button that I have in the form does (which actually passes the entire model back to my HttpPost Edit action and does what I want it to do). The panelID is properly being passed to my HttpPost Edit method and on to the next view, but when I try to query the Model.Rates navigation property is null (only when the post comes from the dropdown. Everything works fine when the post comes from my submit input).
Get Edit:
public ActionResult Edit(int id, int panelID = 1)
{
RateProfile rateprofile = db.RateProfiles.Single(r => r.RateProfileID == id);
var panels = db.Panels;
ViewData["PanelDropDown"] = new SelectList(panels, "PanelID", "PanelName", panelID);
ViewBag.PanelID = panelID;
return View(rateprofile);
}
HttpPost Edit:
[HttpPost]
public ActionResult Edit(RateProfile rateprofile, int panelID)
{
var panels = db.Panels;
ViewData["PanelDropDown"] = new SelectList(panels, "PanelID", "PanelName", panelID);
ViewBag.PanelID = panelID;
if (ModelState.IsValid)
{
db.Entry(rateprofile).State = EntityState.Modified;
foreach (Rate dimerate in rateprofile.Rates)
{
db.Entry(dimerate).State = EntityState.Modified;
}
db.SaveChanges();
return View(rateprofile);
}
return View(rateprofile);
}
View:
#model PDR.Models.RateProfile
#using (Html.BeginForm(null,null,FormMethod.Post, new {id="RateForm"}))
{
<div>
#Html.Label("Panel")
#Html.DropDownList("PanelID", (SelectList)ViewData["PanelDropDown"], new { onchange = "$('#RateForm').submit()" })
</div>
#{var rates= Model.Rates.Where(a => a.PanelID == ViewBag.PanelID).OrderBy(a => a.minCount).ToList();}
#for (int i = 0; i < rates.Count; i++)
{
<tr>
<td>
#Html.HiddenFor(modelItem => rates[i].RateProfileID)
#Html.HiddenFor(modelItem => rates[i].RateID)
#Html.HiddenFor(modelItem => rates[i].PanelID)
#Html.EditorFor(modelItem => rates[i].minCount)
#Html.ValidationMessageFor(model => rates[i].minCount)
</td>
<td>
#Html.EditorFor(modelItem => rates[i].maxCount)
#Html.ValidationMessageFor(model => rates[i].maxCount)
</td>
<td>
#Html.EditorFor(modelItem => rates[i].Amount)
#Html.ValidationMessageFor(model => rates[i].Amount)
</td>
</tr>
}
<input type="submit" value="Save" />
}
To summarize my problem, the below query in my view only works when the post comes from the submit button and not when it comes from my dropdownlist.
#{var rates= Model.Rates.Where(a => a.PanelID == ViewBag.PanelID).OrderBy(a => a.minCount).ToList();}
How does it look in the rendered page? You may have two elements being rendered with the same name/id:
#Html.DropDownList("PanelID", (SelectList)ViewData["PanelDropDown"], new { onchange = "$('#RateForm').submit()"
and
#Html.HiddenFor(modelItem => rates[i].PanelID)
I suggest you use Firefox/Firebug to examine the actual request that it is being made. I can't imagine that the form will post differently if triggered via the submit vs. the button, but I suppose it's possible.
On a larger note, I would make a couple of comments about your design.
I would use ViewBag properties for your selections rather than key-based access to ViewData. This will be much more readable in both your controller and your view.
Consider having a separate action that populates the information filtered by the dropdown list. Render that action in your view based on the PanelID and call that action via AJAX to get the new HTML rather than doing a full form post.
Avoid applying your handlers in your markup. Rather, add an optional Scripts section to your master page and, when needed, add scripts to your page that apply your behaviors through that section. This allows you to control via the master where view-specific scripts are placed in the document and keeps your behaviors separate from your document structure, a best practice for readability.
I found the problem. It looks like because I was only looping through a subset of my navigation property (Rates filtered by panelID), the model that was returned from the view only had that subset of navigation properties available to it. After saving the changes, I just redefined my model (called the record from the database again) and it looks good now.
Ie. There were supposed to be 140 records in my navigation property, filtering by panelID == 1 narrowed it down to 28 records. For some reason the entity framework decided that it didn't feel like maintaining the relationship to the other 112 records, so when I changed the dropdown to filter by panelID == 2, the only records available all had panelID == 1 and returned null.