I have a date that gets displayed on my View that shows the last date a user logged in. They may have never done so. So it's passed as a nullable DateTime.
On the UI, I display it like this:
<td>#(u.LastVisit != null ? u.LastVisit.Value.ToString("dd-MMM-yyyy") : "Not yet")</td>
But I have a few issues with this and think it might be bad practice.
Firstly, the view now has logic. (If null, show "Not Yet", else show the date). The View also dictates the date format. That format is already stored as a constant in a Constants file, accessible from my controller. And the text "Not Yet" should probably be a constant as well.
The only way I can see around this, is to return a String to the UI, and move that logic to the controller. But is that the right way to do this?
You can apply a [DisplayFormat] attribute to the property and set the DataFormatString and NullDisplayText properties, for example
[DisplayFormat(DataFormatString = "{0:dd-MMM-yyyy}", NullDisplayText = "Not yet")]
public DateTime? LastVisit { get; set; }
If you have already defined some constants for the format then you can use (for example) DataFormatString = yourAssembly.Constants.DateFormat where DateFormat is defined as
public const string DateFormat = "{0:dd-MMM-yyyy}";
and in the view use DisplayFor()
#foreach(var u in Model)
{
#Html.DisplayFor(m => u.LastVisit)
}
Related
Is there a way to better control the user experience of Html.TextBoxFor?
I currently have:
#Html.TextBoxFor(m => m.TotalDue,
new { #id = "TotalDue", #class = "decimal_input", #dir = "rtl" })
And a model of:
public decimal TotalDue { get; set; }
When a user comes to that field, if a 0 is displayed and the user begins to type his/her dollar amount, the leading zero stays there. When the user leaves the field, the amount is not formatted.
Ideally, I would like the user to come to the field and if they typed 123456.78 and then pressed TAB to the next field, it would display 123,456.78. If they type 123456, it would display 123,456.00. It would be best if it formatted as they typed, although I could live with just making the field pretty when they leave the field.
I have experimented with Html.EditorFor, but haven't found the right combination. I am open to using that or a "helper" of some sort. I would like the user to have the same experience as found in PDF forms, or something close to it.
Try this: imask.js. I've done a jsfiddle to demonstrate here, and a .NET fiddle here.
Include <script src="https://unpkg.com/imask"></script> on your page and then this script:
var totalDueMask = IMask(
document.getElementById('TotalDue'),
{
mask: '$num',
blocks: {
num: {
mask: Number,
radix: '.',
thousandsSeparator: ','
}
}
}
);
I've formatted it for USD but you can adjust the settings to suit - all the options are documented on the IMask site.
How can I properly validate a price field when doing postback?
I have already checked: How can I validate a currency field? but that doesn't fulfil my expectations.
I've tried with (and some variations):
[Range(0, (double)decimal.MaxValue)]
[DataType(DataType.Currency, ErrorMessage = "...")]
public decimal? Price {get; set;}
Problems:
It never uses the [DataType...] attribute to perform validations.
When the value used is not of decimal type, the issued validation error will be a standard English message that I don't know how to customize and need to.
If I make it string instead of decimal?, then the range validation will be thrown for non-decimal or negative values, but it will still allow values like: 1.23456, which is not the currency format I'm expecting.
I'm looking for a built-in approach before I have to create my own custom validation or regular expression. Something like:
[EmailAddress()]
public string ContactEmail {get; set; }
This perfectly suits me!
You can use FluentValidation. You create your own validator class inheriting from : AbstractValidator and inside the constructor you can put all your logic.
MyCurrencyValidatorMyClass : AbstractValidator<MyClass>
{
MyCurrencyValidatorMyClass()
{
RuleFor(x => x.MyField).NotNull().GreatherThan(0);
//All your rules
}
}
var validator = new MyCurrencyValidatorMyClass();
var myClassVar = new MyClass();
validator.Validate(myClassVar);
Also you can integrate your validator with MVC, for reference please see
https://fluentvalidation.codeplex.com/wikipage?title=mvc
For decimal reference see
https://github.com/JeremySkinner/FluentValidation/blob/master/src/FluentValidation/Validators/ScalePrecisionValidator.cs
This is the nuget package https://www.nuget.org/packages/FluentValidation
You can try this RegEx on your Price model property. It will check for strings in the format of "XXXX.XX" where X is a numeric digit (0-9):
[RegularExpression(#"\d{1,20}(\.\d{1,2})?", ErrorMessage = "Invalid Price. Please use the format of XXXX.XX.")]
The first range d{1, 20} allows up to twenty digits before the decimal place. The second range d{1, 2} allows up to two decimal places.
Haven't been able to find a good answer to my situation yet. I want this textbox to only take numbers and still have the id "SearchString2" so I can use it in my controller. Any idea how?
if (Roles.IsUserInRole(WebSecurity.CurrentUserName, "Admin"))
{
#:<p><b>Customer ID:</b> #Html.TextBox("SearchString2")</p>
}
Thanks in advance.
You can do something like this:
#Html.TextBox('SearchString2', new { #type = "number" })
This should set the type to be a number, you could then use attributes on your model to help limit it to only ints like so:
[RegularExpression(#"^[A-Z]+[a-zA-Z''-'\s]*$")]
[Required]
public string SearchString2 { get; set; }
You'll need to replace the regex with an actual regex and put an validation message in.
Here's more info on validation: http://www.asp.net/mvc/overview/getting-started/introduction/adding-validation
Actually, I think the correction needed to the above answer is
#Html.TextBox('SearchString2',null, new {#type="number"})
otherwise type=number shows up in the value.
I know this is one of the most frequently asked questions.
I had the solution to this but my current project is driving me nuts.
I use the exact same code and still the result is different.
This is the code I normally use to format my dates:
#Html.TextBoxFor(m => m.DateOfAgenda, "{0:dd/MM/yyyy}")
Normally this should result in 07/10/2014 but for some reason it results in 07-10-2014
When I test it in another project the code above works as expected.
As a test I compared the results of the follow lines:
#Html.TextBoxFor(m => m.DateOfAgenda, "{0:dd/MM/yyyy}")
/* result: 07-10-2014*/
#Html.TextBoxFor(m => m.DateOfAgenda)
/* result: 7-10-2014 00:00:00*/
So my code seems to work partially only the '-' won't be replaced by '/'
Does somebody know how to handle this once and for all?
EDIT
I have custom code setting the Culture.
It uses the route to set it to a specific culture (default is 'nl')
When I put the code below in comments everything seems to work.
But I need this part to load my resources (label text, errors, ...).
var language = handler.RequestContext.RouteData.Values["language"];
if (language != null)
{
Thread.CurrentThread.CurrentUICulture = CultureInfo.CreateSpecificCulture(language.ToString());
Thread.CurrentThread.CurrentCulture = CultureInfo.CreateSpecificCulture(language.ToString());
}
To be more specific this line breaks the format
Thread.CurrentThread.CurrentCulture = CultureInfo.CreateSpecificCulture(language.ToString());
Does anybody know why? I would expect that {0:dd/MM/yyyy} has priority because it is explicitly set.
Funny part is that the / is just translated to -.
When I use {0:dd//MM//yyyy} it results in 19--10--2014.
EDIT
Sorry for my late response but I couldn't find the time to try this out.
So basically the problem was the culture and my format settings.
#Html.TextBoxFor(m => m.DateOfAgenda, "{0:dd/MM/yyyy}")
The '/' still gets replaced by the divider specified by your culture.
My culture was set to 'nl' but by default this results in 'nl-nl' where they use '-' as date separator. I live in Belgium 'nl-be' and here we use '/'.
The could have set my culture or change the format.
The code below did the trick.
#Html.TextBoxFor(m => m.DateOfAgenda, "{0:dd'/'MM'/'yyyy}")
I'm not that familiar with ASP.NET MVC, but if you don't specify a format, your current culture's DateTimeFormat is used. Also, / is a special format specifier in .NET which means "replace me with the current DateTimeFormats date-separator which seems to be -.
So you should provide CultureInfo.InvariantCulture or mask the format specifier by using \\/instead:
#Html.TextBoxFor(m => m.DateOfAgenda, "{0:dd\\/MM\\/yyyy}")
or by using the character literal '/':
#Html.TextBoxFor(m => m.DateOfAgenda, "{0:dd'/'MM'/'yyyy}")
Read: The "/" Custom Format Specifier
This appears to be by design. TextBoxFor sets the value as per the following code snippet
string attemptedValue = (string)htmlHelper.GetModelStateValue(fullName, typeof(string));
tagBuilder.MergeAttribute("value", attemptedValue ?? ((useViewData) ? htmlHelper.EvalString(fullName, format) : valueParameter), isExplicitValue);
which calls the EvalString method of HtmlHelper
internal string EvalString(string key, string format)
{
return Convert.ToString(ViewData.Eval(key, format), CultureInfo.CurrentCulture);
}
which calls the Eval method of ViewDataDictionary
public string Eval(string expression, string format)
{
object value = Eval(expression);
return FormatValueInternal(value, format);
}
internal static string FormatValueInternal(object value, string format)
{
....
return String.Format(CultureInfo.CurrentCulture, format, value);
}
So the value uses the format of the current culture. I presume this is because the DefaultModelBinder use the current culture info to parse the string representation of the date back to a DateTime (if you were able to override it, it would not bind unless you created a custom model binder.
View Page
#Html.TextBoxFor(m => m.DateOfAgenda, new { Value = Model.DateOfAgenda.ToString("dd/MM/yyyy")});
Model page
[DisplayFormat(ApplyFormatInEditMode = true, DataFormatString = "{0:dd/MM/yyyy}")]
public DateTime DateOfAgenda{ get; set;}
View Page
#Html.EditorFor(model => model.DateOfAgenda)
In my mvc application I have stored my date in string format. eg: 10212013
But I would like to display that sting as a date in my UI eg: 10/21/2013
How to archive this?? Is it possible to user DisplayFormat for this purpose.
EDIT
This is what I need to do.
#Html.DisplayFor(model => DailyTransaction.MyDate)
Datatype of MyDate is string so this will display as 10212013
But I need to display this as 10/21/2013
can I use string format inside the view of MVC project
I tried to use the following and that is also did not work for me.
[DisplayFormat(DataFormatString = "{0:MM/dd/yyyy}")]
public string MyDate{ get; set; }
Try creating a custom DisplayFormatAttribute that will format the string in the correct format. See this How to make configurable DisplayFormat attribute
EDIT: After looking at your question again you could look at using an editor template to display the data: See this: http://blogs.msdn.com/b/nunos/archive/2010/02/08/quick-tips-about-asp-net-mvc-editor-templates.aspx
Use DateTime.ToString() like;
YourDateTime.ToString("MM/dd/yyyy", CultureInfo.InvariantCulture);
For example;
DateTime d = DateTime.Today;
Console.WriteLine(d.ToString("MM/yy/yyyy", CultureInfo.InvariantCulture));
Output will be;
10/21/2013
Here a DEMO.
As an alternative, you can use DisplayFormatAttribute.DataFormatString like;
[DisplayFormat(DataFormatString="{0:MM/yy/yyyy}")]
yeah you just have to use insert function. here is the example
string dateValue = "10212013";
string dateFormat = x.Insert(2, "/");
dateFormat = dateFormat.Insert(5, "/");
thats all...
If you are sure about the saved format of the date you could use Substring() and Format() methods as below:
string.Format("{0}/{1}/{2}", myStringDate.Substring(0,2),
myStringDate.Substring(2,2),
myStringDate.Substring(4))
You can apply this to your Class and here is a demo;
public class myModelClass
{
private string _MyDate;
public string MyDate
{
get {
return string.Format("{0}/{1}/{2}", _MyDate.Substring(0,2),
_MyDate.Substring(2,2),
_MyDate.Substring(4)); }
set { _MyDate = value; }
}
}
You can create custom templates for various data types that are specific to your requirements.
To create a custom template create a new file in /Views/Shared/DisplayTemplates - Call it StringToDate.cshtml. The name is important and is used in the function call below.
Next, in the file add the code to convert the string to a date format as you require. The simplest solution is to insert the / characters.
#model string
if (Model != null && Model.Length == 8)
{
#(Model.Insert(2,"/").Insert(5,"/"))
} else {
#("Err")
}
Add the above to the file and save it. The else part simply outputs an error if the basic format checks fail.
Next in your view, where ever you need to display this value. Simply use
#Html.DisplayFor(model => model.StringDate, "StringToDate")