I am using MVC and have an Index View. One of the fields that is displayed is type Bool. Currently, it will display a True/False for its value however is there a way to display a check mark or a checked checkbox when True and nothing if False?
My Field -
public bool PrimaryContact { get; set; }
My View -
#foreach (var item in Model.Contacts)
{
<tr>
<td>#item.PrimaryContact</td>
<td>#item.ContactType</td>
<td>#item.Contact1</td>
</tr>
}
If you absolutely want to show a checkbox, you can do this.
<td> <input type="checkbox" checked="#item.PrimaryContact" /> </td>
But a checkbox is a form input control. So if this is a view only screen, why not simply print "Yes" or "No" instead of the input control.
<td>#(item.PrimaryContact?"Yes":"No")</td>
Related
I'm trying to implement a button to adding and removing a row using static HTML. I saw this question that seems to be what I want, but I found that the example doesn't work for drop-down lists. None of the drop-down lists' options ever are marked as selected. If I don't clear the model state, all of the old values are kept. How can I keep my changes to the view-model?
// Controller action
[HttpPost]
public virtual ActionResult DoSomething(DoSomethingViewModel viewModel)
{
if (viewModel != null)
{
if (viewModel.ButtonPressed != null)
{
if (viewModel.ButtonPressed.Trim() == "Cancel")
{
return Redirect(ApplicationUtilities.CancelRequestUrl);
}
else if (viewModel.ButtonPressed.Trim() == "AddRow")
{
ModelState.Clear();
// This only covers non-JavaScript users.
// One for the newest one.
viewModel.FieldOneValues.Add(String.Empty);
viewModel.FieldTwoValues.Add(String.Empty);
viewModel.FieldThreeValues.Add(null);
return View(viewModel);
}
else if (viewModel.ButtonPressed.Trim().StartsWith("Remove"))
{
ModelState.Clear();
String[] split = viewModel.ButtonPressed.Split('-');
if (split.Length == 2)
{
Int32 indexToRemove;
Regex regex = new Regex(#"\[([0-9]+)\]");
Match match = regex.Match(split[1]);
if (match.Success && Int32.TryParse(match.Groups[1].Value, out indexToRemove))
{
viewModel.FieldOneValues.RemoveAt(indexToRemove);
viewModel.FieldTwoValues.RemoveAt(indexToRemove);
viewModel.FieldThreeValues.RemoveAt(indexToRemove);
}
}
return View(viewModel);
}
}
}
if (ModelState.IsValid)
{
return WhateverIsDoneOnSuccess(viewModel);
}
else
{
return View(viewModel);
}
}
// View Model
public class DoSomethingViewModel
{
public DoSomethingViewModel()
{
this.FieldOneValues = new List<String>();
this.FieldTwoValues = new List<String>();
this.FieldThreeValues = new List<Int32?>();
}
public virtual IList<String> FieldOneValues { get; set; }
public virtual IList<String> FieldTwoValues { get; set; }
public virtual IList<Int32?> FieldThreeValues { get; set; }
public virtual String ButtonPressed { get; set; }
}
<!-- Spark View -->
<tr each="var fieldOneValue in Model.FieldOneValues">
<td headers="FieldOneTh">${Html.TextAreaFor(m => m.FieldOneValues[fieldOneValueIndex])}</td>
<td headers="FieldTwoTh">${Html.TextAreaFor(m => m.FieldTwoValues[fieldOneValueIndex])}</td>
<td headers="FieldThreeTh">
${Html.TextBoxFor(m => m.fieldOneValueIndex], new { disabled="disabled", #readonly="readonly" })}
${Html.DropDownListFor(
m => m.FieldThreeValues[fieldOneValueIndex]
, ApplicationUtilities.FieldThreeSelectListItems
, " "
)}
</td>
<td headers="AddRemoveTh">
<button name="${Html.NameFor(m => m.ButtonPressed)}" class="Remove" type="submit" value="Remove-[${fieldOneValueIndex}]">Remove</button>
<button if="fieldOneValueIsLast" name="${Html.NameFor(m => m.ButtonPressed)}" class="Add" type="submit" value="AddRow">Add</button>
</td>
</tr>
<!-- HTML Output -->
<tr>
<td headers="FieldOneTh"><textarea cols="20" id="FieldOneValues_0_" name="FieldOneValues[0]" rows="2">
</textarea></td>
<td headers="FieldTwoTh"><textarea cols="20" id="FieldTwoValues_0_" name="FieldTwoValues[0]" rows="2">
</textarea></td>
<td headers="FieldThreeTh">
<input data-val="true" data-val-number="The field Nullable`1 must be a number." disabled="disabled" id="FieldThreeValues_0_" name="FieldThreeValues[0]" readonly="readonly" type="text" value="0" />
<select id="FieldThreeValues_0_" name="FieldThreeValues[0]"><option value=""> </option>
<option value="0">Option 1</option>
<option value="1">Option 2</option>
<option value="2">Option 3option>
</select>
</td>
<td headers="AddRemoveTh">
<button name="ButtonPressed" class="Remove" type="submit" value="Remove-[0]">Remove</button>
<button name="ButtonPressed" class="Add" type="submit" value="AddRow">Add</button>
</td>
</tr>
Plus I'm curious; I think there should be a way to do this.
There is, but you have to handle the post correctly. It's situations like these why PRG (Post-Redirect-Get) is recommended. When you click something like a remove button for a particular item, it's not appropriate to save all the other fields and do whatever else would happen when the whole form is actually submitted. All the user indicated was that they wanted to remove this one item.
Therefore, when you get the post, you remove that item from the database or wherever it's persisted and then you redirect back to the original form if that's what you want. The redirect process updates the page state so that the item is now gone and the rest of the form can then be edited without carrying around stale data. What you're trying to do is remove the item, but then just return the view directly which still has the posted item in the data backing it. That's where your problem is.
I think you went down this path because you're trying to maintain any edits the user made to other areas of the form, but that's simply not going to be possible. However, you do have some options:
Don't actually have a button that removes the item right this minute. Instead, provide a checkbox or something that indicates the item should be deleted when the user posts the entire form. Then you can save the all the form data, remove the indicated items, and redirect afterwards like you should.
Use local storage to save the user's edits on the client-side, and then read them back from local storage after the page loads again, following the redirect. However, this requires JS.
Use AJAX to submit the request to remove the item, and then remove the row from the DOM. However, this requires JS.
Also, remember that it's entirely possible to progressively enhance your form. So, you can implement #1 and #3, and then if JS isn't available, #1 still serves as a fallback.
Here's my code:
#using (Html.BeginForm("AddMCondition", "Admin"))
{
<td class="admin-textbox">
#Html.TextBox("txtMCondition")
</td>
<td>
#foreach (var exerType in Model.AllExerciseTypes)
{
<label>
#Html.CheckBox("RestrictedType")
#exerType.Name
</label>
}
<input type="submit" value="Add Medical Condition" />
</td>
}
and this is how I'm retrieving values in the controllers
public ActionResult AddMCondition(string txtMCondition, string[] RestrictedType)
{
//Code here...
}
There are only 3 item in the AllExerciseTypes collections. I've notice that each checkbox is sending at least a false value to the controller whether it's selected or not. If I don't check any checkbox, I get 3 false in the collection. If I check 1 checkbox, I get 4 values, i.e. 1 true and 3 falses, and so on. When I check all of them, I get 3 values, i.e. True, False, True, False, True, and False.
Is there any reason, checkboxes are sending at least false in each situation?
This is because the Html Helper for a checkbox adds an extra hidden field with a value of false to ensure that something is posted back to the server even if the checkbox is not checked. If you add <input type="checkbox" name="cbSomething" value="true"/> to your form and post it back without checking the checkbox then you won't get a value sent back to the server. If you use the strongly typed helper extension (Html.CheckBoxFor(m => m.RestrictedType) for example) and bind a checkbox to a property on your model, the model binder will correctly bind the value to the model property. Here's the code snippet from the input extensions within MVC:
if (inputType != InputType.CheckBox)
return TagBuilderExtensions.ToMvcHtmlString(tagBuilder1, TagRenderMode.SelfClosing);
StringBuilder stringBuilder = new StringBuilder();
stringBuilder.Append(tagBuilder1.ToString(TagRenderMode.SelfClosing));
TagBuilder tagBuilder2 = new TagBuilder("input");
tagBuilder2.MergeAttribute("type", HtmlHelper.GetInputTypeString(InputType.Hidden));
tagBuilder2.MergeAttribute("name", fullHtmlFieldName);
tagBuilder2.MergeAttribute("value", "false");
stringBuilder.Append(tagBuilder2.ToString(TagRenderMode.SelfClosing));
return MvcHtmlString.Create(((object) stringBuilder).ToString());
I am creating checkbox inside iterator. Below are my code,
<s:iterator value="contacts" var="contact">
<tr>
<td>
<s:checkbox name="selectContactsCheckBox" fieldValue="%{#contact.contactid}" value="%{defaultContacts.contains(contact.contactid)}" theme="simple"/>
</td>
<td>${contact.fullname}</td>
<td>${contact.mobile}</td>
<td>${contact.organization}</td>
<td>${contact.department}</td>
</tr>
</s:iterator>
this code creates checkbox and works fine. But i want to preselect this checkboxes using a collection from action.
Below is a method from my action calss,
public List<String> getDefaultContacts() {
return Arrays.asList(this.selectedContacts);
}
suppose if i have 100 contact in list and if getDefaultConatacs() return just 5 string then i want to select those 5 checkbox u
If i use below line(value attributes check whether the contactid is available in list)
<s:checkbox name="selectContactsCheckBox"
fieldValue="%{contactid}"
value="%{contactid in defaultContacts}"
theme="simple">
</s:checkbox>
We have a form and depending upon the circumstances we want to switch between an add operation and an update operation from the same form. Below is a cut down version of our form.
Effectively the "Order number" textbox is disabled and can never be edited in this form. Now, the scenario is a bit like this:
The first time the user lands on this form, the "Order number" text box is blank.
The user enters a customer name and submits the form.
At this point in the controller action, we get the max value of order number in the database and increment the order number by 1 . We then add that new record in the database.
If that operation is successful, we update the current form and the "Order Number" textbox should now be updated with the order number created in the previous step AND also what should happen is that we are now in Edit mode.
Say the user then updates the "Customer name" and submits the form, the record in the database should be updated in this instance.
Now for some code:
The View:
<%: Html.TextBox("OrderNumber", Model.OrderNumber == 0 ? "" : Model.OrderNumber.ToString(), new { #disabled = "true" })%>
The controller:
public ActionResult Index()
{
var customerOrderModel = new CustomerOrderModel();
return View(customerOrderModel);
}
public ActionResult Add(CustomerOrderModel customerOrderModel, FormCollection values)
{
// We write the logic for either the add or update.
return this.View("Index", customerOrderModel);
}
I have removed the code from the Add action because from putting breakpoints we know that the "// We write the logic for either the add or update." is not the problem.
Now where we are having trouble is this. We can add the new entry in the table following which the "Order Number" field gets updated and is displayed correctly. However, after we change the customer name and try to update, the customerOrderModel passed into the "Add" action shows that the order number being passed is 0(which is our default in the system and which is used to determine if we are performing an add or update operation).
So the question is why is our textbox getting updated, which would seem to indicate that our model is getting updated, but then when we try to submit, the correct model doesn't get passed in? Moreover, why is it that the Index action doesn't get hit after the "Add" action is completed? What do we have to do to get things to work the way we want them to?
Model
namespace Demo.Models
{
public class Order
{
public int OrderId { get; set; }
public string CustomerName { get; set; }
}
public class OrderDb:DbContext
{
public DbSet<Order> Orders { get; set; }
}
}
View
#model Demo.Models.Order
<form action="/" method="post">
<table>
<tr>
<td>#Html.LabelFor(m=>m.OrderId)</td>
<td>
#Html.EditorFor(m => m.OrderId)
</td>
</tr>
<tr>
<td>
#Html.LabelFor(m=>m.CustomerName)
</td>
<td>
#Html.EditorFor(m=>m.CustomerName)
</td>
</tr>
<tr>
<td></td>
<td>
<input type="submit" name="submit" value="submit" />
</td>
</tr>
</table>
</form>
Action
public ActionResult Index(Order o)
{
if (o.CustomerName != null)
{
using (OrderDb db = new OrderDb())
{
db.Entry(o).State = o.OrderId == 0 ? EntityState.Added : EntityState.Modified;
db.SaveChanges();
ModelState.Clear();
}
}
return View(o);
}
This is because HtmlHelpers look to ModelState for values first and then uses the values you explicitly use.
So when you add the entity you get ["Id"]=0 inside your model state.
So solve you have to clear your ModelState with .Clear() after a successful add.
I noticed what seems to me a bug in asp.net MVC or simply I am doing something wrong. I am currently using 1.0 so maybe this is something that will be addressed in the 2.0 release. But either way, here we go.
When I my view model has a property which is the same name as the declared id for a drop down list, the selected item is ignored and the rendered html has nothing selected.
Not sure if I did something wrong, but changing the name of the id fixes the problem. I simplified the example, hope it is clear, otherwise please let me know.
Here is my view where the declared ID is the same name as my list in the model:
<table border="0" cellpadding="0" cellspacing="0">
<tr>
<td>
<%= Html.DropDownList("IsMultipleServicers", Model.IsMultipleServicers) %>
</td>
</tr>
</table>
And the rendered Html
<table border="0" cellpadding="0" cellspacing="0">
<tr>
<td>
<select id="IsMultipleServicers" name="IsMultipleServicers">
<option value="false">No</option>
<option value="true">Yes</option>
</select>
</td>
</tr>
</table>
Now lets make a small change. I will change the declared id to be something different.
Here is my View:
<table border="0" cellpadding="0" cellspacing="0">
<tr>
<td>
<%= Html.DropDownList("MultipleServicers", Model.IsMultipleServicers) %>
</td>
</tr>
</table>
And now the rendered html:
<table border="0" cellpadding="0" cellspacing="0">
<tr>
<td>
<select id="IsMultipleServicers" name="IsMultipleServicers">
<option value="false">No</option>
<option selected="selected" value="true">Yes</option>
</select>
</td>
</tr>
</table>
Notice that now I get a selected option which would be the second element in the List.
Here is my ViewModel just to tie everything together:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.Mvc;
namespace MVCProject.Models.ViewModels.Service
{
public class ServiceViewModel : ViewModel
{
public List<SelectListItem> IsMultipleServicers { get; set; }
}
}
Here is my action:
[AcceptVerbs(HttpVerbs.Get)]
public virtual ActionResult Service()
{
return View(new ServiceViewModel()
{
IsMultipleServicers = BuildBooleanSelectList(true)
};
}
private List<SelectListItem> BuildBooleanSelectList(bool isTrue)
{
List<SelectListItem> list = new List<SelectListItem>();
if (isTrue)
{
list.Add(new SelectListItem() { Selected = false, Text = "No", Value = "false" });
list.Add(new SelectListItem() { Selected = true, Text = "Yes", Value = "true" });
}
else
{
list.Add(new SelectListItem() { Selected = true, Text = "No", Value = "false" });
list.Add(new SelectListItem() { Selected = false, Text = "Yes", Value = "true" });
}
return list;
}
I think the problem is a confusion regarding the DropDownList overloads:
Html.DropDownList(string name) looks for a view model property of name and type IEnumerable<SelectListItem>. It will use the selected item (SelectListItem.Selected == true) from the list, unless there is a form post value of the same name.
Html.DropDownList(string name, IEnumerable<SelectListItem> selectList) uses the items from selectList, but not their selected values. The selected is found by resolving name in the view model (or post data) and matching it against the SelectListItem.Value. Even if the value cannot be found (or is null), it still won't use the selected value from the list of SelectListItems.
Your code uses the second overload, but specifies a "value" property that doesn't exist ("MultipleServicers").
To fix your problem, either use the first overload:
<%= Html.DropDownList("IsMultipleServicers") %>
Or, add a string MultipleServicers property to your view model and populate it in your controller. I'd recommend this solution as it gets around several problems with initial display, post display and mapping the post data to a view/post model:
public class ServiceViewModel : ViewModel
{
public string MultipleServicers { get; set; }
public List<SelectListItem> IsMultipleServicers { get; set; }
}
Then for your HTML:
<%= Html.DropDownList(Model.MultipleServicers, Model.IsMultipleServicers) %>
This technique maps into MVC2, as well:
<%= Html.DropDownListFor(x => x.MultipleServicers, Model.IsMultipleServicers) %>
I encountered this same problem using the Html.DropDownList(string name, IEnumerable selectList) overload. It appears that my model has a property of the same name as the name of the drop down list. This being the case, MVC favored the property value of my Model over the Selected property of each entry in the IEnumerable.
The solution was to use a name for the dropdown list that does not match up to a property name. Another solution would be to write my own extension method that ignores model and view state and instead always honor the selected property.
The DropDownList helper pulls the default value from the model. In the first case, the value in the model corresponding to the name is a SelectList -- this doesn't match any of the items in the list, it is the list, so no value is chosen. In the second example, your model does not include a property with that name so the value from the model can't be used and it defaults to the state indicated in the SelectList itself. Typically, I will have a property on the model for the selected value -- this becomes the default -- and another property representing the potential values for the list.