How can I validate a currency field? - asp.net-mvc-4

I have an ASP.NET MVC-4 application with this currency field:
[DisplayFormat(ApplyFormatInEditMode = true, DataFormatString = "{0:c}", ConvertEmptyStringToNull = true)]
[DataType(DataType.Currency)]
public decimal? Price { get; set; }
This is the corresponding part in my view:
#Html.EditorFor(model => model.Price)
#Html.ValidationMessageFor(model => model.Price)
If the price is 100 Euro the text field in the view shows:
100,00 €
This is nice.
But I am having problems as soon as I try to do a Postback. The validator pops up and says that the price field needs to be a number.
I can only fix this if (1) I delete the € symbol and (2) replace the decimal separator (replace comma with a dot).
If there is no better solution, I guess I could change the DataFormatString = "{0:F2}" in order to avoid the currency symbol.
But how do I make the validator accept the comma as decimal separator instead of the (American) dot?
Thanks for your help, guys!

So, I was able to solve my problem with jQuery's Globalization plugin from http://github.com/jquery/globalize.
I added the following files to my /scripts folder:
/scripts/globalize.js
/scripts/cultures/globalize.cultures.js
/sctipts/cultures/globalize.culture.de-DE.js
In BundleConfig.cs:
bundles.Add(new ScriptBundle("~/bundles/scripts/globalization").Include(
"~/Scripts/globalize*",
"~/Scripts/cultures/globalize*"));
In _Layout.cshtml:
#Scripts.Render("~/bundles/scripts/globalization")
and in the script section of _Layout.cshtml:
$.validator.methods.number = function (value, element) {
return this.optional(element) || !isNaN(Globalize.parseFloat(value));
}
$.validator.methods.range = function (value, element, param) {
return this.optional(element) || (Globalize.parseFloat(value) >= param[0] && Globalize.parseFloat(value) <= param[1]);
}
However, I couldn't make the currency symbol to work with the client validation, so I also changed the data annotation as follows:
[DisplayFormat(ApplyFormatInEditMode = false, DataFormatString = "{0:c}", ConvertEmptyStringToNull = true)]
But that was it. No other changes necessary. I can now enter values like "1,49" or "18,77" and everything gets stored properly in my database.

Related

Assigning Value to Bootstrap-datetimepicker with Format MM/YYYY Displays Incorrect Year

I am using bootstrap-datetimepicker version 4.17.47 in ASP MVC 4 app.
I have this model property:
public DateTime MonthYear { get; set; }
I assign default value in controller (arbitrary value is just an example actual value is determined through some logic):
model.MonthYear = new DateTime(2017, 3, 1);
I display this in view using datepicker so users can update the value as needed:
#Html.TextBoxFor(model => model.MonthYear, new { #class = "form-control input month-picker" })
Script:
$('.month-picker').datetimepicker({
format: 'MM/YYYY',
useCurrent: false,
toolbarPlacement: 'bottom',
viewMode: 'months'
});
The problem is the control displays "03/0001" instead of "03/2017".
What am I missing here?
Apparently the problem is that the input is being filled with a DD/MM/YYYY date from server when datetimepicker is expecting only MM/YYYY. Try formating the textbox, like:
#Html.TextBoxFor(model => model.MonthYear, "{0:MM/yyyy}", new { #class = "form-control input month-picker" })
That should work.

FluentValidation allow null OR specify length?

I have a rule like this:
RuleFor(m => m.Title).Length(1, 75);
However, if the Title is null, I still get the validation stating the Title length must be between 1 and 75 characters, you entered 0.
How can I change the rule so it allows for null title, but if one is specified it must be between 1 and 75 characters? Thanks.
I'm working on a bit of an assumption here, but I'm guessing your title isn't set to null but to string.Empty. You can add particular clauses to any rule by doing the following:
public class Thing
{
public string Title { get; set; }
}
public class ThingValidator : AbstractValidator<Thing>
{
public ThingValidator()
{
this.RuleFor(s => s.Title).Length(1, 75).When(s => !string.IsNullOrEmpty(s.Title));
}
}
As suggested by Yannick Meeus in above post, we need to add 'When' condition to check for not null.
It resolved the issue.
Here I wanted to allow Phone number to be null, but if specified then it should contain ONLY digits.
RuleFor(x => x.PhoneNumber).Must(IsAllDigits).When(x => !string.IsNullOrEmpty(x.AlternateNumber)).WithMessage("PhoneNumber should contain only digits");

How to assign null/string to an int type in MVC Razor using Entity Framework?

I am using a simple MVC 4 application using Entity Framework.
In my View I am displaying data from of a table using webgrid.
View Also has Textboxes(EditorFor) for saving any new record in the table.
I am using partial view for the Textboxes, as in the beginning when the page is launched, the textboxes should remain empty.
Out of 5, two columns are of integer types.
In order to make the textboxes empty initially I am using a new object as -
#if (!dataGrid.HasSelection)
{
Datamodel = new EntityFrDemo.Models.FacultyDetails { DepartmentID = 0, Name = "", Subject = "", YrsExp = 0, Email = "" };
Html.RenderPartial("~/Views/Shared/_FacultyDetails.cshtml", Datamodel);
}
//------------------------------------------------------------------------
#Html.LabelFor(model => model.DepartmentID)
#Html.EditorFor(model => model.DepartmentID)
#Html.ValidationMessageFor(model => model.DepartmentID)
//-----------------------------------------------------------------------------
So I am able to make my boxes empty, however for the Integer type boxes '0' is coming, as I can only assign zero.
So How can I override/superimpose the integer value type boxes to empty string type so that boxes remains empty only in case when no row is selected i.e. in initial stage...?
When you use #Html.EditorFor() with a int value, Razor generate a html tag like this
<input type="number" name="propertyName" id="propertyName" value="propertyValue" />
If you didn't set a value for the int property, the default int value is zero. To set another value in the html tag, you can write it without Razor or you can set the value like the code below.
#Html.EditorFor(model => model.DepartmentID, new { htmlAttributes = new { #Value = "" } })
Note: It is capital "V", not a lower case "v".

Sorting on date field with Sitecore 7 ContentSearch

I'm trying to add a field sort to a date field in a ContentSearch query. I'm able to filter on the index field properly so I'm assuming the field is getting populated with values properly, however, results are not being sorted properly. Any thoughts? Here's the code I'm using to do query:
public static IEnumerable<Episode> GetPastEpisodes(Show show, bool includeMostRecent = false, int count = 0)
{
IEnumerable<Episode> pastEpisodes;
using (var context = _index.CreateSearchContext())
{
// querying against lucene index
pastEpisodes = context.GetQueryable<Episode>().Where(GetPastAirDatePredicate(show));
if(!includeMostRecent)
{
pastEpisodes = pastEpisodes.Where(item => item.Id != GetMostRecentEpisode(show).Id);
}
pastEpisodes = pastEpisodes.OrderByDescending(ep => ep.Latest_Air_Date);
if (count > 0)
{
pastEpisodes = pastEpisodes.Take(count);
}
pastEpisodes = pastEpisodes.ToList();
// map the lucene documents to Sitecore items using the database
foreach (var episode in pastEpisodes)
{
_database.Map(episode);
}
}
return pastEpisodes;
}
private static Expression<Func<Episode,bool>> GetPastAirDatePredicate(Show show)
{
var templatePredicate = PredicateBuilder.Create<Episode>(item => item.TemplateId == IEpisodeConstants.TemplateId);
var showPathPredicate = PredicateBuilder.Create<Episode>(item => item.FullPath.StartsWith(show.FullPath));
var airDatePredicate = PredicateBuilder.Create<Episode>(item => item.Latest_Air_Date < DateTime.Now.Date.AddDays(1));
var fullPredicate = PredicateBuilder.Create<Episode>(templatePredicate).And(showPathPredicate).And(airDatePredicate);
return fullPredicate;
}
The field is stored and untokenized and using the LowerCaseKeywordAnalyzer.
<field fieldName="latest_air_date" storageType="YES" indexType="UN_TOKENIZED" vectorType="NO" boost="1f" type="System.DateTime" settingType="Sitecore.ContentSearch.LuceneProvider.LuceneSearchFieldConfiguration, Sitecore.ContentSearch.LuceneProvider">
<analyzer type="Sitecore.ContentSearch.LuceneProvider.Analyzers.LowerCaseKeywordAnalyzer, Sitecore.ContentSearch.LuceneProvider"/>
</field>
Episode class has IndexField attribute set:
[IndexField("latest_air_date")]
public virtual DateTime Latest_Air_Date {get; set; }
Kyle,
As far as I can tell everything looks correct with your configuration and code. I mocked out something very similar in a vanilla Sitecore 7.2 instance and Dates sorted without issue.
One thing to note though, and this might be what is giving you some issues, is that Sitecore's FieldReader for DateTime only store the date portion of the DateTime. If you are expecting to be able to sort by true DateTime you will need to add a custom FieldReader or some computed fields.
See this blog that discusses the issue and explains the process to swap out a custom field reader: http://reasoncodeexample.com/2014/01/30/indexing-datetime-fields-sitecore-7-content-search/
I would also suggest looking at the index with Luke to verify what data is actually in the index. https://code.google.com/p/luke/
And finally you may want to enable Search debugging in Sitecore to see exactly how Sitecore is executing the query in Lucene.
Sitecore.ContentSearch.config:
<setting name="ContentSearch.EnableSearchDebug" value="true" />

Remove default 0 from numeric textbox

My model has an EditorFor that binds to a not null numeric field in a database. I wish to keep this field blank so that users can enter or scan numbers into the field. Unfortunately, it defaults to a 0. Is there an easy way to remove the 0 while keeping the field not null?
#Html.EditorFor(model => model.RackNumber, new { id = "RackNumber"})
Change model property type to nullable: public int? RackNumber {get;set;}
You can provide the Value attribute like this:
#Html.EditorFor(model => model.RackNumber, new { Value = Model.RackNumber == 0 ? "" : Model.RackNumber.ToString(), id = "RackNumber"})