How to set default value of TextBox empty string instead of null - asp.net-mvc

I may be out of date, but one principle I adhere to is avoid nulls as much as possible.
However what I have found is that for a strongly typed view in which the user inputs the properties of an object I want to save, if some fields are not entered they are assigned as null.
Then when you try to save the changes, the validation fails.
So rather than set each property to an empty string, how can I automatically set each TextBox on a form to default to an empty string rather than a null?

You could put the following attribute on your string-properties in your model:
[DisplayFormat(ConvertEmptyStringToNull=false)]
So whenever someone posts a form with empty text-fields, these will be an empty string instead of null...

To be honest, I'd say your coding methodology is out of date and flawed. You should handle all possibilities, it's not hard. That's exactly what string.IsNullOrEmpty(value); is for.
I'm guessing your validation logic is something like:
if (value == string.Empty) { isValid = false; }
So it doesn't handle the null values. You should replace that check so it also checks for nulls.
string value1 = null;
string value2 = string.Empty;
string.IsNullOrEmpty(value1); // true
string.IsNullOrEmpty(value2); // true

An alternative solution to using attributes on each model property, as described in the accepted answer, is using a custom model binder, see string.empty converted to null when passing JSON object to MVC Controller

I ran across this problem when dealing with an old service that requires empty strings. I created an extension method:
public static string GetValueOrDefault(this string str)
{
return str ?? String.Empty;
}
So you can use this when you want to make sure any strings that are null become empty
yourString1.GetValueOrDefault();
yourString2.GetValueOrDefault();

Related

ModelState binding custom array of checkboxes

ViewModel Binding is working, the object passed back to the edit controller contains the correct values, which is a list of selected options. However, ModelState binding is not working, the model state AttemptedValues exist, but aren't being reloaded into the fields.
I have a model with the following properties
class Model
{
public List<string> AvailableValues { get; set; }
public List<string> SelectedValues { get; set; }
}
But in my view I have some categorization, so I can't do a direct foreach.
foreach (var category in CatgoryList.Categories)
{
foreach (var available in Model.AvailableValues.Where(x => category.AvailableValues.Contains(x))
{
var check = Model.SelectedValues!= null && Model.SelectedValues.Contains(available.Id);
check &= (ViewData.ModelState["SelectedValues"] != null) && ViewData.ModelState["SelectedValues"].Value.AttemptedValue.Contains(available.Id);
<input type="checkbox" name="SelectedValues" id="available.Id" value="available.Id" checked="#check"/>#available.FriendlyName<br>
}
}
The ModelState does contain SelectedValues from the previous post, but it doesn't auto-bind, because I have a custom field for the checkboxes.
This code is smelly
Is there a better way to get the data to load from the Attempted Value
EDIT:
Ok, so my question wasn't clear enough, let me clarify.
On a validate, I'm retuning the same view if there was an error.
The modelstate is holding the previously entered values in ModelState["field"].Value.AttemptedValue.
With fields created using the helpers, TextboxFor, CheckboxFor, etc, these values are automatically filled in.
However, when using the normal reflexes for checkbox binding, only the values of the checked checkboxes are returned in the data object passed back to the controller. This means I'm not using the logic that fills values in from the ModelState.
What I've done is dig through the modelstate myself for the attempted values, because they do exist under the field name "SelectedValues". But I have to manually apply them. The value there looks like this.
ModelState["SelectedValues"] = "Value1;Value2;Value4"
Is there a better way to get the data to load from the Attempted Value in the model state.
The primary "smell" (to use your term) I see here is that the code you have in the nested foreach is written directly in your view (*.cshtml), but code of that complexity should be in your Controller action.
You should calculate and generate all the data your view will need in the controller, and then pass that data through to the view using Model (looks like you are already doing that) and you can also use the ViewBag to pass additional data not contained in your Model. Then the view is just responsible to generate the HTML.
That's the other problem I see with your code - you are referencing the ViewData.ModelState which is highly unusual to see in a view. The ModelState should be examined in the controller before you even decide which view to render.
It looks like maybe you are just passing data through ViewData.ModelState that should actually be passed through ViewData/ViewBag.
You can read more about passing data to a view here.
Ok, so basically, I couldn't find anything that will do this for me. The default Html helper methods just don't cover this scenario.
So, I wrote an extension method.
Basically it pulls in the enumerator from the model using the expression you send to it, just like any other helper, but you also send the entry in the list you want to build a checkbox against.
It ends up looking like this.
#Html.CheckboxListEntryFor(x => x.SelectedEntries, AvailableEntries[i].Id)
The method does the following
Get the propertyInfo for the list and check if selected entries contains the values.
Check if the ModelState is invalid, if so, overwrite the checked value with the modelstate entry
build an html checkbox that uses the property name as the name and id of the checkbox, and sets checked based on the previous steps.
public static MvcHtmlString CheckboxListEntryFor<TModel, TProperty>(this HtmlHelper<TModel> htmlHelper,
Expression<Func<TModel, TProperty>> expression, string entryValue)
{
PropertyInfo info = GetPropertyInfo(typeof (TModel), expression);
var enumerator = info.GetValue(htmlHelper.ViewData.Model);
var check = enumerator != null && ((IList) enumerator).Contains(entryValue);
if (!htmlHelper.ViewData.ModelState.IsValid)
{
check = htmlHelper.ViewData.ModelState[info.Name] != null &&
htmlHelper.ViewData.ModelState[info.Name].Value.AttemptedValue.Contains(entryValue);
}
var fieldString = String.Format(
"<input type=\"checkbox\" name=\"{0}\" id =\"{1}\" value=\"{1}\"{2}/>",
info.Name, entryValue, check ? " checked=\"checked\"" : string.Empty);
return MvcHtmlString.Create(fieldString);
}

asp.net mvc 3 validation on data type

I am trying to realize valition on data type. I have used DataAnnotations, but for data type it's not showing customized message
for example when I' am trying enter string data into int typed field. How I can customize messages in this case?
If I had to guess, you sound like you want a custom message to display when validating one or more fields in your model. You can subclass the DataAnnotations.ValidationAttribute class and override the IsValid(object) method and finally setting a custom ErrorMessage value (where ErrorMessage already belongs to the ValidationAttribute class)
public class SuperDuperValidator : ValidationAttribute
{
public override bool IsValid(object value)
{
bool valid = false;
// do your validation logic here
return valid;
}
}
Finally, decorate your model property with the attribute
public class MyClass
{
[SuperDuperValidator(ErrorMessage="Something is wrong with MyInt")]
public int MyInt { get; set; }
}
If you're using out-of-the-box MVC3, this should be all you need to propertly validate a model (though your model will probably differ/have more properties, etc) So, in your [HttpPost] controller action, MVC will automagically bind MyClass and you will be able to use ModelState.IsValid to determine whether or not the posted data is, in fact, valid.
Pavel,
The DataAnnotations DataType attribute does not affect validation. It's used to decide how your input is rendered. In such a case, David's solution above works.
However, if you want to use only the built-in validation attributes, you probably need to use the Range attribute like this:
[Range(0, 10, ErrorMessage="Please enter a number between 0 and 10")]
public int MyInt { get ; set ;}
(Of course, you should really be using the ErrorMessageResourceName/Type parameters and extract out hard-coded error message strings into resx files.)
Make sure to let MVC know where to render your error message:
<%= Html.ValidationMessageFor(m => m.MyInt) %>
Or you can just use EditorForModel and it will set it up correctly.
I don't think this has been answered because I have the same issue.
If you have a Model with a property of type int and the user types in a string of "asd" then the MVC3 framework binding/validation steps in and results in your view displaying "The value 'asd' is not valid for <model property name or DisplayName here>".
To me the poster is asking can this message that the MVC3 framework is outputting be customized?
I'd like to know too. Whilst the message is not too bad if you label your field something that easily indicates an number is expected you might still want to include additional reasons so it says something like:
"The value 'asd' is not valid for <fieldname>; must be a positive whole number."
So that the user is not entering value after value and getting different error messages each time.

How to handle nulls in ASP.NET MVC

I have user registration form. It has field Date of birth. It is not compulsory in database also not on UI. But while create when i am goiing to assign it to objects property, i have to convert it in to date format. But if i have not selected it, it will become null in the FormCollection object. like
User.DOB=Convert.ToDateTime(collection["DOB"]);
Now issue is if it collection["DOB"]is null then it throws exception. I can not assign default value here. So how can i handle this situation?
You'll probably be better off using DateTime.TryParse for this.
This way you can check whether you're working with a valid date or not.
DateTime dateOfBirth;
bool isValidDateOfBirth = DateTime.TryParse(collection["DOB"], out dateOfBirth);
if(isValidDateOfBirth)
{
// do stuff
}
else
{
// do some other stuff
}

How to get model binder to leave null strings as null?

My Sql Server database has some nullable nvarchar fields, and no nvarchar fields containing empty strings. I want to keep it this way, but the default MVC model binder seems to turn null strings into empty strings.
When a controller retrieves a null nvarchar database field, the null field turns into null string inside the controller, and from there the view renders them, say as blank text boxes. When the page is posted, the default model binder uses these blank text boxes to update the model, and the formerly null strings are changed to empty strings. When the data is updated back to the database, nulls are overwritten with empty strings.
What is the easiest way to get model binding to leave these nulls unchanged?
I know you are probably looking for something more sophisticated, but the default behavior of the ModelBinder is to convert empty form field values into the default value for the datatype of your model object property. String properties become empty, int properties become 0, etc.
You can obviously create a validation scheme that will check for string.empty and convert to null prior to updating the DB. For int form fields you will need to check for 0, and then convert to null.
Here's a hack I used a few months ago before I found the eden of stackoverflow. :) It's a pain, and doesn't scale well, but it works:
Basically, you override the binding inside of a partial linq object. If there's a value you know should always be null (but never legitimately empty) you can do the following. I used this for a string-based user id (SID).
partial void OnSubProcess_Owner_UserChanged()
{
if (string.IsNullOrEmpty(this.SubProcess_Owner_User))
this._SubProcess_Owner_User = null;
}
James
The right answer might be to override the default Model binder to add this functionality yourself.
Maybe you could have a NullValueAttribute that you could apply to string properties to identify the null value. Then make empty string a null value.
I am experiencing the same problem at the moment and will probably resort to this
I'm posting this answer to follow through on this question. After working with it for a while I came to see this problem as part of the general concern of model integrity. For a while I had implemented a solution inside my update stored procedures to catch empty strings and turn them to nulls, along the lines of mikerennick's answer above. Later I wanted also to make sure fields were trimmed and I happened to move the application to NHibernate (and most of the stored procedures went away). In the end I embedded some POCO logic to trim and check for empty strings (from whatever source) in the setters as so:
public MyClass {
private string _name;
public string Name {
get { return _name; }
set { _name = value.TrimToNullIfEmpty(); }
}
}
public static class StringExtensions {
public static string TrimToNullIfEmpty(this string s) {
string temp = (s ?? "").Trim();
return temp.Length == 0 ? null : temp;
}
}

Default Values in LINQ Modelling

To put it in basic form, my database table doesn't allow nulls for varchars, it must have blanks. My model doesn't allow nulls so it won't insert a record if I leave form fields empty. If an empty form field appears I want a default value of blank to be used instead. I've tried, for example, the following without any luck:
[Column]
[DisplayName("WMD Company")]
[DefaultValue(" ")]
public string WMDCompany { get; set; }
So instead, in my controller action I have to do a check like the following:
if(myModel.WMDCompany == null) myModel.WMDCompany = " ";
Which is plain nasty to me. Is there any way of getting [DefaultValue(" ")] to work?
Cheers
What about something like this:
private string wmdCompany;
public string WMDCompany
{
get
{
if (this.wmdCompany == null)
{
return string.Empty;
}
return this.wmdCompany;
}
set
{
this.wmdCompany = value;
}
}
The DefaultValue attribute is not used. LINQ to SQL has not support for DB defaults unfortunately. That property is intended for use in API extension if I remember, but I don't know of any that use it.
Two approaches to get around this you could use.
First update your data layer, by appropriately controlling the property, and setting it to null. Use a partial class to extend your data class, and implement the OnCreated() partial method, and in this set the value to String.Empty.
partial void OnCreated()
{
MyProp = String.Empty;
}
Secondly, you could change your DBML representation to allow nulls, but in your database, use a trigger to convert NULLs to empty strings.
I'd go with the first approach myself - assuming you can't just use NULLs as suggested by Adrian
Inserting spaces as a placeholder for NULL seems like a very obscure method to me. Why don't you just change your table design to allow NULL values?

Resources