Pass other value when checkbox is clicked - asp.net-mvc-4

I want to pass other value when checkBox is clicked.
my code is as below
foreach (var item in Model) {
<td>
#Html.CheckBox("Project", false, new { item.ProjectId})
</td>
}
Controller is
[HttpPost]
public ActionResult Test(FormCollection collection)
{
var test = collection["Project"]; }
in Var test i am getting True or False
Is there any way to get ProjectId??

The CheckBox and CheckBoxFor helpers are meant for bool and bool? properties (In fact, if you look at the source code you will see they force a casting of the property value to bool?. Check the method InputHelper here in the source code).
In the case of the CheckBox helper, the output will be something like this:
<input id="Foo" name="Foo" type="checkbox" value="true">
<input name="Foo" type="hidden" value="false">
As you can see, the values are hardcoded to true/false boolean values. You could override the value attribute but that would only apply to the checkbox and not to the hidden field with the value for when the checkbox is unchecked. Using the helper with non-boolean fields would also lead you to trouble because of the casting to bool? I mentioned above.
One possible solution would be to manually create the desired html. (You need to be carefull with the name attribute here as it is what the MVC model binder will use to populate your model). Writing something like this in your view:
<input id="Foo" name="Foo" type="checkbox" value="#item.ProjectId">
#* you could add another hidden input for when the checkbox is not checked *#
If you are using the model metadata or if you have complex bindings, this may be a problem as you would need to manually set the html attributes and be careful with the name attribute.
Another option is to create your own html helper. This could allow you to use other types not just booleans and should expect a value for when the checkbox has been checked. The idea is to create something like this:
public static class CustomHelpers
{
public static MvcHtmlString CheckBoxNonBooleanFor<TModel, TValue>(this HtmlHelper<TModel> html, Expression<Func<TModel, TValue>> expression, object checkedValue)
{
var fieldName = ExpressionHelper.GetExpressionText(expression);
var fullBindingName = html.ViewContext.ViewData.TemplateInfo.GetFullHtmlFieldName(fieldName);
var fieldId = TagBuilder.CreateSanitizedId(fullBindingName);
var metadata = ModelMetadata.FromLambdaExpression(expression, html.ViewData);
var value = metadata.Model;
TagBuilder tag = new TagBuilder("input");
tag.Attributes.Add("name", fullBindingName);
tag.Attributes.Add("id", fieldId);
tag.Attributes.Add("type", "checkbox");
tag.Attributes.Add("value", (checkedValue ?? true).ToString());
if ((checkedValue != null && checkedValue.Equals(value))
||(checkedValue == null && value == null ))
{
tag.Attributes.Add("checked", "checked");
}
var validationAttributes = html.GetUnobtrusiveValidationAttributes(fullBindingName, metadata);
foreach (var key in validationAttributes.Keys)
{
tag.Attributes.Add(key, validationAttributes[key].ToString());
}
return new MvcHtmlString(tag.ToString());
}
}
This helper can be used in your view as in the following example (Don´t forget to include your helper namespace into the views namespaces settings of the web.config in the views folder):
#Html.CheckBoxNonBooleanFor(m => m.Foo, 132)
And it will generate the following html, taking care of things like the metadata attributes, the name, the checked attribute, etc for you.
<input id="Foo" name="Foo" type="checkbox" value="132" data-val="true" data-val-number="The field Foo must be a number.">
If needed, you could easily extend it adding another parameter uncheckedValue that will add a hidden input field with the desired value for when the checkbox remains unchecked. You could also add the htmlAttributes parameter to allow you passing additional random html attributes.
As a final comment, just double check that radio buttons won´t be a fit for your requirements, as you could easily have multiple radio buttons for the same field with different int values.

I'm assuming you want the projectid given a checkbox is checked?
<td>
#Html.Hidden("projectId", item.ProjectId)
#Html.CheckBox("selected", false)
</td>
[HttpPost]
public ActionResult Test(int projectId, bool selected)
{
if (selected)
{
///use your projectId

Related

NetCore InputTagHelper using reflection

I have a model with lots of properties. Creating or updating theirs views is a troublesome work.
So i trying to use Reflection to create view during runtime:
(PS: yes, i know the front-end is very import, i should use Vue or other frame to create views at designtime. But I just want to try buildering forms at runtime, yes i am a freak. LOL)
There is my code:
#{
var dic = Model.GetAttributePropsByCategory();// get PropertyInfo by CategoryAttribute and custom DisplayIndexAttribute
}
#foreach (var items in dic)
{
var category = items.Key; // InfoCategoryAttribute
foreach (var tuple in items.Value)
{
var displayIndex = tuple.Item1; // Custom: DisplayIndexAttribute
var prop = tuple.Item2; // PropertyInfo
<input asp-for="#prop.Name" class="form-control"/>
}
}
My target is:
<input class="form-control valid" type="text" data-val="true" data-val-required="The VIN field is required." id="VIN" name="VIN" value="VIN001" aria-describedby="VIN-error" aria-invalid="false">
But the result like this:
<input class="form-control valid" type="text" data-val="true" data-val-required="The Name field is required." id="prop_Name" name="prop.Name" aria-describedby="prop_Name-error" aria-invalid="false">
So, I read the source code in the aspnetcore.mvc.taghelpers. I found the key is InputTagHelper.For:
public class InputTagHelper : TagHelper
{
...
[HtmlAttributeName(ForAttributeName)]
public ModelExpression For { get; set; }
...
}
But I can't understand how the InputTagHelper.For was created. Therefor, i dont know how to override it to achieve my target.
Is there any Suggestions? Thx.
In source code of InputTagHelper, we can find following code snippet will help generate textbox input and the third parameter For.Name would be used to set value(s) of textbox id and name attribute.
return Generator.GenerateTextBox(
ViewContext,
modelExplorer,
For.Name,
modelExplorer.Model,
format,
htmlAttributes);
And if we debug the source code, we will find the value of For look like below.
To achieve your expected result, you can customize it and pass For.Model.ToString() to GenerateTextBox method rather than For.Name, like below.
return Generator.GenerateTextBox(
ViewContext,
modelExplorer,
//For.Name,
For.Model.ToString(),
modelExplorer.Model,
format,
htmlAttributes);
Test Result

SelectList dropdown list showing multiple = "multiple" for current viewmodel

This kind of a bizarre issue and I can't figure out a solution how I want.
I'm using .net core 2.1. I have a orders view model like this:
public class OrdersFilterViewModel
{
[Display(Name = "Account Numbers:")]
public IEnumerable<SelectListItem> AccountNumbers { get; set; }
}
My viewmodel and SelectList in my orders controller is called like this:
var vm = new OrdersFilterViewModel
{
AccountNumbers = new SelectList(_context.Account.Where(m => m.UserID == userId), "AccountNumber", "AccountNumber", account)
};
return PartialView("_FilterOrders", vm);
The problem lies when trying to get a dropdown list in the view which looks like this:
<form asp-action="FilterOrders" asp-controller="Order" id="ordersFilterForm" method="post">
<div class="form-group">
<label asp-for="AccountNumbers" class="control-label"></label>
<select asp-for="AccountNumbers" class="form-control" asp-items="#Model.AccountNumbers">
</select>
</div>
<div class="form-group">
<input type="submit" value="Submit" class="btn btn-default" />
</div>
</form>
This somewhat works but gives me a textarea type display where multiple = "multiple" is always tacked on in the browser. I've discovered that if I add something like the following to my viewmodel:
public int? AccountId { get; set; }
Then change my view to:
<select asp-for="AccountId" class="form-control" asp-items="#Model.AccountNumbers">
I can then have my dropdown list. However, I don't need that property for anything as far as I know. I tried a million things so it's possible I made some other slight changes I'm forgetting to get that to work, but that's the gist of it.
Is there any way around adding that extra property? Or do I need it for something I'm not aware of? Or is there any way to set multiple = "false" or something to that effect so I can get my dropdown list with my original viewmodel and such?
I haven't dealt with the post back to the controller yet, so maybe that will reveal the gotchas. I'm basically trying to create a modal type query filter that doesn't really do much other than modify some parameters and send them back to my query to update it. Thanks.
Is there any way around adding that extra property? Or do I need it
for something I'm not aware of?
Yes, you need this extra property, because in your select there are many items, and the user will select one or multiple items, and on the server side you'll need to know what the user selected, this is the purpose of the select tag.
And the multiple = "multiple" depends on what you put in the asp-for in the case of asp-for="AccountId" it is a single int value, so it won't use multiple, is you have an array in the asp-for then it will use the multiple.
Here is a pretty detailed description about the select tag helper:
Select Tag Helper in ASP.NET Core MVC

Dropdownlist from EditorTemplates does not return value

I created a simple SingleSelectDropdownlist view model to avoid writing html and classes over and over again. This class has a property called Options (where options are stored for the dropdown) and SelectedValue (a string property to store selected value) and a Placeholder property.
#model SingleSelectDdl
#{
var options = new SelectList(Model.Options, "Value", "Text", string.Empty);
}
<select asp-for="SelectedValue" asp-items="options" class="form-control" placeholder="#Model.Placeholder"></select>
<span asp-validation-for="SelectedValue"></span>
After upgrading to ASP.NET Core 1.1.0 I noticed that the SelectedValue property doesn't return any value anymore, it just returns an empty string no matter what I do.
Any idea what is causing this?

asp.net mvc how to get validation attributes when manually constructing html dropdownlist

I am building a dropdownlist using the answer here: SelectListItem with data-attributes. This will allow me to add a different class and data annotations to different <option>'s
However I am struggling with building the <select> initially. I have
<select name="#Html.NameFor(a=>a.ValueType)"
id="#Html.IdFor(a=>a.ValueType)">
</select>
My ViewModel looks like
[Required(ErrorMessage = "Value Type is required")]
public string ValueType { get; set; }
How do I get the Required annotation validation into the view?
The final html in the browser should look like:
<select id="ValueType" name="ValueType" data-val-required="Value Type is required" data-val="true"></select>
but I currently have:
<select id="ValueType" name="ValueType"></select>
Is there a #Html method to get these data-val from the ViewModel attributes or how do i do this? Do I have to hard-code it?

MVC view displaying time when date only required

I have the following model property
[DataType(DataType.Date)]
[DisplayFormat(DataFormatString = "{0:d/M/yyyy}", ApplyFormatInEditMode = true)]
[Display(Name = "Date of Screening")]
public DateTime? ScreeningDate { get; set; }
and the following view:
#Html.TextBoxFor(model => model.ScreeningDate, new { #class="date maintainState" })
and yet I get this markup for the textbox:
<input value="18/06/2013 12:00:00 AM" class="date maintainState" ... type="text" />
I want the value to be 18/06/2013
It works when I apply #Html.EditorFor, but I need control over the class attribute. What am I doing wrong? Thank you very much.
It works when I apply #Html.EditorFor, but I need control over the
class attribute. What am I doing wrong?
Nothing, it's how things work. Only the EditorFor and DisplayFor helpers respect the [DisplayFormat] attribute.
I understand that you are using TextBoxFor because you want to apply a custom class to the field which is what the EditorFor doesn't allow you to. But that's not true. All that the EditorFor helper does is to render the corresponding editor template. And if you do not have a custom template it renders the default one.
So what?
Well, you write a custom editor template for the DateTime type that will behave as you want. Just like that in ~/Views/Shared/EditorTemplates/DateTime.cshtml:
#Html.TextBox("", ViewData.TemplateInfo.FormattedModelValue, ViewData)
And then simply:
#Html.EditorFor(model => model.ScreeningDate, new { #class="date maintainState" })
Now killed 2 rabbits with one bullet:
We applied the desired format
We applied the desired class to the input field
And if you wanna kill a third rabbit and be able to validate this custom format when the form is submitted back (because remember that the default model binder doesn't care much about this [DisplayFormat] attribute) you could write a custom model binder that will respect the [DisplayFormat] attribute, just as I explained here: https://stackoverflow.com/a/7836093/29407