Setting bool in ViewModel using RadioButtonFor? - c#

I have a fairly complex form, but the problem I'm having is with a very basic part of it. I have a radio button group with two radio buttons to represent a true or false value for a bool within my view model. So I have a model like this:
public class MyViewModel {
public bool IsPOBox { get; set; }
}
Then within my view I have the following code to produce the radio buttons:
#Html.RadioButtonFor(m => m.IsPOBox, true, new { id = "togglePOBoxOn" }) #Html.Label("togglePOBoxOn", "PO Box")
#Html.RadioButtonFor(m => m.IsPOBox, false, new { id = "togglePOBoxOff" }) #Html.Label("togglePOBoxOff", "Street Address")
The problem is, when I submit this with "PO Box" selected, the IsPOBox property is being set to false instead of true. In fact, the property is set to false regardless of which radio button is selected.
Have I messed something up here?

It turns out ASP.NET MVC doesn't include disabled fields' values when you submit a form. The field was being disabled by a checkbox further up in the form. Annoying!
Edit:
After seeing #JohnSaunders' comment below I did a quick search for how I'm supposed to have an uneditable field, but still have the information submitted. It turns out you should use the readonly attribute. I learned something today!
i.e.
<input type="text" readonly />

Related

Text Box values chabged but the binding see it as null [duplicate]

I am working on some data collection forms in WinForms/C#. When the form loads, I am looping through a configuration and adding a new Binding to each of the TextBox controls; mapping the Text property of each TextBox control to specific string property on my POCO object.
public void BindTextBoxControls(dynamic entity, List<TextBoxConfig> textBoxConfig)
{
foreach (var config in textBoxConfig)
config.Control.DataBindings.Add(new Binding("Text", entity, config.PropertyName));
}
Everything has been working as expected, new records properly saving new values entered into the corresponding TextBox controls, TextBoxes populating with the correct values when reopened a previously entered records with the form, and updates to values in TextBoxes of previously entered records are getting the updated values set on the underlying POCO.
However, I started to layer in some business rules onto the form specifically to gray out/disable and clear out previously entered values in the TextBox based on other user input/activity on the form - things are not working as expected.
In a contrived example; a rule like if a Checkbox_1 is checked then TextBox #5 should not be valued (clear out any previously entered value and disable it from input). On my Checkbox_1 event handler for CheckedChanged, I specifically check if the Checkbox_1 is checked and if so, set TextBox_1.Text == null and TextBox_1.Enabled = false. This works as expected and on the form, I see any previously entered value cleared from the TextBox_1 and it becomes enabled.
private void chkCheckBox1_CheckedChanged(object sender, EventArgs e)
{
if(!chkCheckBox1.Checked)
{
txtBox5.Text = string.Empty;
}
}
However, when I debug and break on the save and inspect the underlying POCO's property that the underlying control is bound to after the method is called; the old value still persists on the object's property which the text box is bound to, despite the textbox having not value appearing on the form. When I reopen the form for that record, the old cleared out value is re-populated in the disabled TextBox. However, manually clearing out the value in the same TextBox or updating a value and inspecting the object shows the updated value after those operations are performed.
It seems like changing the Text value of a TextBox control (e.g. the Text property of a TextBox) in code maybe somehow be "bypassing" the DataBinding? I'm actually seeing the same/similar behavior when applying similar rules to "uncheck" TextBoxes programmatically within event handler methods - the CheckBox controls are also using DataBinding to boolean properties on the POCO.
When you setup databinding by this overload: Binding(String, Object, String), then the value of DataSourceUpdateMode will be OnValidation, which means when you modify the value of control's property using code or through UI, the binding will push the new value to data source only after Validating event happens for the control.
To fix the problem, use either of the following options:
Use another overload and set the DataSourceUpdateMode to OnProperetyChanged
OR, after setting the Value of the TextBox.Text call ValidateChildren method of the form.
Example - Set the DataSourceUpdateMode to OnProperetyChanged
public class Person
{
public string Name { get; set; }
public string LegalCode { get; set; }
public bool IsRealPerson { get; set; }
}
Person person;
private void Form1_Load(object sender, EventArgs e)
{
person = new Person() {
Name = "My Company", LegalCode = "1234567890", IsRealPerson = false };
NameTextBox.DataBindings.Add(nameof(TextBox.Text), person,
nameof(Person.Name), true, DataSourceUpdateMode.OnPropertyChanged);
LegalCodeTextBox.DataBindings.Add(nameof(TextBox.Text), person,
nameof(Person.LegalCode), true, DataSourceUpdateMode.OnPropertyChanged);
IsRealPersonCheckBox.DataBindings.Add(nameof(CheckBox.Checked), person,
nameof(Person.IsRealPerson), true, DataSourceUpdateMode.OnPropertyChanged);
IsRealPersonCheckBox.CheckedChanged += (obj, args) =>
{
if (IsRealPersonCheckBox.Checked)
{
LegalCodeTextBox.Text = null;
LegalCodeTextBox.Enabled = false;
}
};
}
Note - You can put the logic inside the model
Another solution (Which needs more effort and more changes in your code) is implementing INotifyPropertyChanged in your model class. Then when PropertyChanged event raises for your boolean property, you can check if it's false then you can set the string property to null.
In this approach you don't need to handle UI events. Also right after updating the model property, the UI will be updated; in fact implementing INotifyPropertyChanged enables two-way databinding for your model class.

How to update properties from object using input fields with onchange methods to write the data?

I'm trying to make a "Edit" section to edit objects and update them in the database:
First of all I obtain the current object and load input fields depending on PropertyType(string = textfield, int = number picker, etc):
Object:
Place input fields:
<EditForm #ref="EditForm" Model="ObjectType" novalidate>
<DataAnnotationsValidator />
#foreach (var property in EditObject.GetType().GetProperties())
{
#if (property.PropertyType.Name.Equals("String"))
{
<SfTextBox Placeholder="#property.Name"
Type="InputType.Text"
#onchange="#((Microsoft.AspNetCore.Components.ChangeEventArgs __e) => property.SetValue(EditObject, __e.Value))"
Value="#property.GetValue(EditObject, null)"
CssClass="create-model-item">
</SfTextBox>
}
else if (property.PropertyType.Name.Contains("Int"))
{
<SfNumericTextBox TValue="int?" Placeholder="Choose a Number Value="#property.GetValue(EditObject, null)" CssClass="create-model-item">
<NumericTextBoxEvents TValue="int?"
ValueChange="#((Syncfusion.Blazor.Inputs.ChangeEventArgs<int?> __e) => property.SetValue(EditObject, __e.Value))">
</NumericTextBoxEvents>
</SfNumericTextBox>
}
}
<SfButton OnClick="SendForm" IsPrimary="true" CssClass="mb-3">Create</SfButton>
</EditForm>
Now I want to change the data of the properties in "EditObject" to the new values that the user has filled in, this is done by the "OnChange" method:
#onchange="#((Microsoft.AspNetCore.Components.ChangeEventArgs __e) => property.SetValue(EditObject, __e.Value))"
However this is giving me this error:
Does anyone know what is going wrong?
Thanks in advance!:)
You don't have to specify the type of the event.
Your #onchange code needs to change to #onchange="#((__e) => { property.SetValue(EditObject, __e.Value); })"
That should stop the error.
There is a two-way binding support for Textbox component. Therefore, no additional method is needed to update new values to Model property. If you used two-way binding it will be updated by default. So, kindly try with #bind-Value attribute.
Also have a look at the below UG link.
UG link: https://blazor.syncfusion.com/documentation/textbox/data-binding/#two-way-data-binding

ASP.NET MVC checkbox always false

I made an asp.net website, but the checkbox is always false. Why is this so?
Model:
public string Username { get; set; }
public string Password { get; set; }
public bool Remember { get; set; }
CSHTML:
<div class="form-group">
#Html.Label("Remember me?")
#Html.CheckBoxFor(m => m.Remember)
</div>
The Remember property is always false, if the checkbox is checked then Rememberis still false.
I got the same issue, I fixed it by writing html checkbox tag, giving it the name same as property name, and value = true, if the checkbox is not checked no need to worry as it won't be submitted anyway,
in your case this will be it
<input type="checkbox" name="Remember" value="true" />
With Razor, I had the same problem. What worked for me was taking off the value="xxx" tag. Then it functioned normally.
Does not work:
<input class="form-check-input" value="true" asp-for="Answer.aIsCorrect" />
Works:
<input class="form-check-input" asp-for="Answer.aIsCorrect" />
The first parameter is not checkbox value but rather view model binding for the checkbox hence:
#Html.CheckBoxFor(m => m.SomeBooleanProperty, new { #checked = "checked" });
The first parameter must identify a boolean property within your model (it's an Expression not an anonymous method returning a value) and second property defines any additional HTML element attributes. I'm not 100% sure that the above attribute will initially check your checkbox, but you can try. But beware. Even though it may work you may have issues later on, when loading a valid model data and that particular property is set to false.
Source and additional info:
https://stackoverflow.com/a/12674731/3397630
Hope it will helpful for you ,kindly let me know your thoughts or feedbacks
Thanks
Karthik

Generating a list of RadioButtons with the same name in a for-loop in an ASP.NET MVC3 view

I'm working on an ASP.NET MVC3 project, and I have a problem with RadioButtons name generated by a for loop in a PartialView.
First, here is my code, I'll explain the exact problem just after :
Model FileUploadModel
public class FileUploadModel
{
public HttpPostedFileBase File { get; set; }
public bool IsMainFile { get; set; }
}
Model MyModel
public class MyModel
{
// Some properties
public List<FileUploadModel> Files { get; set; }
}
How the PartialView _UploadFiles is called
#Html.Partial("_UploadFiles", Model) // Model is a MyModel model
PartialView _UploadFiles - HERE IS THE PROBLEM
#model MyModel
#{
var nbFiles = (Model.Files != null ? Model.Files.Count : 0);
const int NB_DEFAULT_UPLOAD_FIELDS = 3;
}
#for (int i = 0; i < NB_DEFAULT_UPLOAD_FIELDS; i++)
{
#Html.TextBoxFor(m => m.Files[nbFiles].File, new { type = "file", name = "Files", id = "Files" })
#Html.RadioButtonFor(m => m.Files[nbFiles].IsMainFile, "Main ?", new { id = "rb" + i + "_File_IsMain" })
}
So this is the last snippet the problem. I'd like to create 3 upload fields with an associated RadioButton indicating if the file is the "main file". However, with the above snippet, the displayed view is OK, but when I validate my form, my posted model only have the first picture uploaded (and the corresponding boolean IsMainFile , the others are just ignored). That means the List Files contains only the first TextBox and first RadioButton data
So I tried with #Html.RadioButtonFor(m => m.Files[i].IsMainFile, but the RadioButtons have a different name so user can check all RadioButtons, which is NOT the desired behavior. MVC doesn't authorize me to override the name of the RadioButtons, so I can't give them my own name.
How can I generate these fields is order to only ONE Radiobutton can be checked AND my MyModel.Files property contains all chosen files ?
Thank you
I think your best bet here is to use editor templates.
First, you create a view named FileUploadModel, and place it a folder named EditorTemplates which should exist under your controller's Views folder. Note that the name of the view must match the name of your view model's class.
Its contents would look something like:
#model FileUploadModel
#Html.TextBoxFor(m => m.File, new { type = "file" })
#Html.RadioButtonFor(m => m.IsMainFile, true)
Then, back in your "_UploadFiles" view, your markup for the input field and radio button would change to:
#model MyModel
using (Html.BeginForm(...
#Html.EditorFor(m => m.Files)
...
Note that this will automatically iterate through and apply that editor template for each FileUploadModel object, and automatically name them as required. This of course assumes that the Files property in your model is populated.
Your action that accepts the post should accept type MyModel as its sole parameter, and everything should bind up automatically at post.
There are ways to avoid editor templates and build and name those fields programmatically, but this is really the preferred way, and much cleaner.
More specific to your issue... (In other words, I forgot that the code above won't group radio buttons.)
For the grouping of radio buttons to work, you should set the attribute name as follows:
#Html.RadioButtonFor(m => m.IsMainFile, true, new { Name = "IsMainFile" })
Case matters here. Note that Name must be capitalized for this to work. I'm not sure why this is the case.
The problem is that once you change that attribute, this field will no longer automatically bind in the post. This is unfortunate but understandably is as designed since, if you look at the request, I believe you will see that only the selected radio button value has been posted.
So, you could change the value of the radio button to something recognizable since just that one instance of IsMainFile will show up in the request, if I'm correct.
Perhaps you can use this specific knowledge to tweak your existing code.

Best way to display a group/selection of radiobuttons from the choice of a single radiobutton?

I have a form which for some questions would ask if something was included and if it isn't to supply a reason.
So I need a radio button which records to the database it's value like normal which I have setup with a RadioButtonFor and if "No"(false) is selected then a group/list of other radiobuttons will display.
Ofc this is just the ideal solution if this method isn't feasible then another solution would be to maybe a if statement in the controller so that if that main radiobutton has a value of "Yes"(true) then it would set the values of x, y and z radiobuttons to "No"(false) when it records the form to the database.
These are the 2 ideas I have on how to get the same end result but for the 1st idea I think the easiest way to perform it's function would be in jquery which I'm fairly new at so would struggle to come up with how to do it
For the 2nd idea it's 1 not ideal and 2 I'm not sure how I would then reference those radiobuttons/code the if statement to do said task.
Any other ideas would also be welcome but hopefully with help on how to implement them.
Well, this may sound overkill, but I would go with both solutions. You need the javascript script side code to do it right from a presentation standpoint - and you need the server-side code to do the validation right too.
If you implement only the client-side validation, how the system will behave if a browser has no support to javascript, or if it is just disabled? You cannot take javascript support for granted...
OTOH, you would offer a best user experience if you added that client-side functionality you're talking about...
And about your doubt of how to do the server-side validation: that's easy with ASP.NET MVC - on load, just set the same ViewData entry/ViewModel property that you have read during post.
Edit So let's talk about a complete solution.
Again, I'm not sure I understood what you need here. You're talking about radiobuttons, but you also seem to think you'll be able to control them individually (many radios binding to many fields). That's not usually the case - a group of radiobuttons is normally bound to the same field, with each radiobutton meaning a different value (so exactly like a single dropdown list). Of course, that does not mean your database must behave in the same way...
See this example:
<% using(Html.BeginForm("HandleForm", "Home")) { %>
Select your favorite color:<br />
<%= Html.RadioButton("favColor", "Blue", true, new { id = "rbColorBlue", class = "favColor" }) %> Blue <br />
<%= Html.RadioButton("favColor", "Purple", false, new { id = "rbColorPurple", class = "favColor" })%> Purple <br />
<%= Html.RadioButton("favColor", "Red", false, new { id = "rbColorRed", class = "favColor" })%> Red <br />
<%= Html.RadioButton("favColor", "Orange", false, new { id = "rbColorOrange", class = "favColor" })%> Orange <br />
<%= Html.RadioButton("favColor", "Yellow", false, new { id = "rbColorYellow", class = "favColor" })%> Yellow <br />
<%= Html.RadioButton("favColor", "Brown", false, new { id = "rbColorBrown", class = "favColor" })%> Brown <br />
<%= Html.RadioButton("favColor", "Green", false, new { id = "rbColorGreen", class = "favColor" })%> Green
<%= Html.RadioButton("favColor", "Other", false, new { id = "rbColorOther", class = "favColor" })%> Other
<div id="divOtherColorText" style="display: block">
Describe the color you want here:<br />
<%=Html.TextArea("otherColorText", new { id = "taOtherColor" }) %><br />
</div>
<% } %>
This will bound to a single controller parameter, favColor, with a default value of "Blue". See that, for convenience, we're assigning a distinct client-side id for each radiobutton (rbColorBlue, rbColorGreen and so forth). That means that you'll be able to treat each radiobutton individually in your jQuery code, even if they represent a single value to the server-side controller.
Talking about the server-side code, that's how the action will look like:
public class HomeController : Controller
{
public ActionResult HandleForm(string favColor, string otherColorText)
{
// Add action logic here
// If you want to have a separated field for your database,
// just do something like that:
MyDbFacade.BlueColorField = (favColor == "Blue");
MyDbFacade.GreenColorField = (favColor == "Green");
...
return View();
}
}
(Of course, you could also work with a ViewModel, but I'll not talk about that option here.)
Back to client-side. Let's suppose you don't want to show taOtherColor, unless the user selects the rbColorOther radiobutton. The jQuery code would be something like this:
$(document).ready(function() {
// If user selects any radiobutton:
if ( $('#rbColorOther:checked').length > 0) {
$("#divOtherColorText").show();
} else {
$("#divOtherColorText").hide();
}
});
I guess that would be it. Let me know if I have missed something... :-)
I would go with your first solution. Render both groups of radio buttons on the page but hide the second group with css. Then set some onclick events on the radio buttons in your first group to show/hide the second group depending on which one is clicked.
I think it is easier to just write the html for the radio buttons by hand because of the onclick handlers you are writing, but you could probably manage it using RadioButtonFor as well.
So your yes/no would look like this:
<input type="radio" name="yesNo" id="yes" onlclick="$('#other-options').hide();' value='true' <%: Model.YesNo ? "checked='checked'" : "" %>/><label for='yes'>Yes</label>
<input type="radio" name="yesNo" id="no" onlclick="$('#other-options').show();' value='false'<%: !Model.YesNo ? "checked='checked'" : "" %>/><label for='no'>No</label>
Your other options would look like this:
<div id='otherOptions' <%: Model.YesNo ? "style='display: none;'" : "" %>>
<input type="radio" name="XYesNo" id="xyes" value='true'/><label for='xyes'>X Yes</label>
<input type="radio" name="XYesNo" id="xno" value='false'/><label for='xno'>X No</label>
<input type="radio" name="YYesNo" id="yyes" value='true'/><label for='yyes'>Y Yes</label>
<input type="radio" name="YYesNo" id="yno" value='false'/><label for='yno'>Y No</label>
<input type="radio" name="ZYesNo" id="zyes" value='true'/><label for='zyes'>Z Yes</label>
<input type="radio" name="ZYesNo" id="zno" value='false'/><label for='zno'>Z No</label>
</div>
In your action method ignore any x,y,z values if the first YesNo was true :
if(model.YesNo){
//persist false values for x,y,z
} else {
//check model for values of x,y,z
}
Well I figured out the simpliest way to get the controller to sort out the validation which was
if (wd.AppTher == true)
{ wd.AppTherRea = 0; } this is the example for a drop down and for another radiobutton it would be instead of "0" or whatever value u want for the drop down for it to be "false" or if you want/have multiple options with int at the db field type then you just set your N/A or w/e value as the = too, for multiple RB or DDL then just add more in between { and }, wd was the value assigned to represent the table and otherwise a viewmodel value could be used.

Categories