ASP MVC Model Binder - asp.net-mvc

I can't explain Model Binder behavior in this example:
enum Gender
{
Male,
Female
}
#model Gender
#Html.ActionLink("Test", "Index", new { gender = Model });
public ActionResult(string gender)
{
ViewBag.Gender = gender;
return View()
}
why it works — string gender argument in action correct get Enum type?

It works because absolutely everything can be converted to a string by way of object.ToString(). Further, it happens on Enum that the conversion produces something useful; the text of the enumeration value's name.
The default binder can convert back and forth between these values, plus the numeric ones, in most cases.

Related

Display only date and no time in mvc [duplicate]

I have the following razor code that I want to have mm/dd/yyyy date format:
Audit Date: #Html.DisplayFor(Model => Model.AuditDate)
I have tried number of different approaches but none of that approaches works in my situation
my AuditDate is a DateTime? type
I have tried something like this and got this error:
#Html.DisplayFor(Model => Model.AuditDate.Value.ToShortDateString())
Additional information: Templates can be used only with field access, property access, single-dimension array index, or single-parameter custom indexer expressions.
Tried this:
#Html.DisplayFor(Model => Model.AuditDate.ToString("mm/dd/yyyy"))
No overload for method 'ToString' takes 1 arguments
If you use DisplayFor, then you have to either define the format via the DisplayFormat attribute or use a custom display template. (A full list of preset DisplayFormatString's can be found here.)
[DisplayFormat(DataFormatString = "{0:d}")]
public DateTime? AuditDate { get; set; }
Or create the view Views\Shared\DisplayTemplates\DateTime.cshtml:
#model DateTime?
#if (Model.HasValue)
{
#Model.Value.ToString("MM/dd/yyyy")
}
That will apply to all DateTimes, though, even ones where you're encoding the time as well. If you want it to apply only to date-only properties, then use Views\Shared\DisplayTemplates\Date.cshtml and the DataType attribute on your property:
[DataType(DataType.Date)]
public DateTime? AuditDate { get; set; }
The final option is to not use DisplayFor and instead render the property directly:
#if (Model.AuditDate.HasValue)
{
#Model.AuditDate.Value.ToString("MM/dd/yyyy")
}
I have been using this change in my code :
old code :
<td>
#Html.DisplayFor(modelItem => item.dataakt)
</td>
new :
<td>
#Convert.ToDateTime(item.dataakt).ToString("dd/MM/yyyy")
</td>
If you are simply outputting the value of that model property, you don't need the DisplayFor html helper, just call it directly with the proper string formatting.
Audit Date: #Model.AuditDate.Value.ToString("d")
Should output
Audit Date: 1/21/2015
Lastly, your audit date could be null, so you should do the conditional check before you attempt to format a nullable value.
#if (item.AuditDate!= null) { #Model.AuditDate.Value.ToString("d")}
Googling the error that you are getting provides this answer, which shows that the error is from using the word Model in your Html helpers. For instance, using #Html.DisplayFor(Model=>Model.someProperty). Change these to use something else other than Model, for instance: #Html.DisplayFor(x=>x.someProperty) or change the capital M to a lowercase m in these helpers.
You can use the [DisplayFormat] attribute on your view model as you want to apply this format for the whole project.
[DisplayFormat(ApplyFormatInEditMode = true, DataFormatString = "{0:dd/MM/yyyy}")]
public Nullable<System.DateTime> Date { get; set; }
#ChrisPratt's answer about the use of Display Template is wrong. The correct code to make it work is:
#model DateTime?
#if (Model.HasValue)
{
#Convert.ToDateTime(Model).ToString("MM/dd/yyyy")
}
That's because .ToString() for Nullable<DateTime> doesn't accept Format parameter.
For me it was enough to use
[DataType(DataType.Date)]
[DisplayFormat(DataFormatString = "{0:dd/MM/yyyy}")]
public DateTime StartDate { set; get; }
I implemented the similar thing this way:
Use TextBoxFor to display date in required format and make the field readonly.
#Html.TextBoxFor(Model => Model.AuditDate, "{0:dd-MMM-yyyy}", new{#class="my-style", #readonly=true})
2. Give zero outline and zero border to TextBox in css.
.my-style {
outline: none;
border: none;
}
And......Its done :)
You could use Convert
<td>#Convert.ToString(string.Format("{0:dd/MM/yyyy}", o.frm_dt))</td>
In View Replace this:
#Html.DisplayFor(Model => Model.AuditDate.Value.ToShortDateString())
With:
#if(#Model.AuditDate.Value != null){#Model.AuditDate.Value.ToString("dd/MM/yyyy")}
else {#Html.DisplayFor(Model => Model.AuditDate)}
Explanation: If the AuditDate value is not null then it will format the date to dd/MM/yyyy, otherwise leave it as it is because it has no value.
After some digging and I ended up setting Thread's CurrentCulture value to have CultureInfo("en-US") in the controller’s action method:
Thread.CurrentThread.CurrentCulture = new System.Globalization.CultureInfo("en-US");
Here are some other options if you want have this setting on every view.
About CurrentCulture property value:
The CultureInfo object that is returned by this property, together
with its associated objects, determine the default format for dates,
times, numbers, currency values, the sorting order of text, casing
conventions, and string comparisons.
Source: MSDN CurrentCulture
Note: The previous CurrentCulture property setting is probably optional if the controller is already running with CultureInfo("en-US") or similar where the date format is "MM/dd/yyyy".
After setting the CurrentCulture property, add code block to convert the date to "M/d/yyyy" format in the view:
#{ //code block
var shortDateLocalFormat = "";
if (Model.AuditDate.HasValue) {
shortDateLocalFormat = ((DateTime)Model.AuditDate).ToString("M/d/yyyy");
//alternative way below
//shortDateLocalFormat = ((DateTime)Model.AuditDate).ToString("d");
}
}
#shortDateLocalFormat
Above the #shortDateLocalFormat variable is formatted with ToString("M/d/yyyy") works. If ToString("MM/dd/yyyy") is used, like I did first then you end up having leading zero issue. Also like recommended by Tommy ToString("d") works as well. Actually "d" stands for “Short date pattern” and can be used with different culture/language formats too.
I guess the code block from above can also be substituted with some cool helper method or similar.
For example
#helper DateFormatter(object date)
{
var shortDateLocalFormat = "";
if (date != null) {
shortDateLocalFormat = ((DateTime)date).ToString("M/d/yyyy");
}
#shortDateLocalFormat
}
can be used with this helper call
#DateFormatter(Model.AuditDate)
Update, I found out that there’s alternative way of doing the same thing when DateTime.ToString(String, IFormatProvider) method is used. When this method is used then there’s no need to use Thread’s CurrentCulture property. The CultureInfo("en-US") is passed as second argument --> IFormatProvider to DateTime.ToString(String, IFormatProvider) method.
Modified helper method:
#helper DateFormatter(object date)
{
var shortDateLocalFormat = "";
if (date != null) {
shortDateLocalFormat = ((DateTime)date).ToString("d", new System.Globalization.CultureInfo("en-US"));
}
#shortDateLocalFormat
}
.NET Fiddle
Maybe try simply
#(Model.AuditDate.HasValue ? Model.AuditDate.ToString("mm/dd/yyyy") : String.Empty)
also you can use many type of string format like
.ToString("dd MMM, yyyy")
.ToString("d") etc
This is the best way to get a simple date string :
#DateTime.Parse(Html.DisplayFor(Model => Model.AuditDate).ToString()).ToShortDateString()
Instead of
#Html.DisplayFor(Model => Model.AuditDate)
Use
#Model.AuditDate.ToString("MM/dd/yyyy")
This style renders the date as: 06/02/2022.
You can style your string accordingly to how you need it.
I had a similar issue on my controller and here is what worked for me:
model.DateSigned.HasValue ? model.DateSigned.Value.ToString("MM/dd/yyyy") : ""
"DateSigned" is the value from my model
The line reads, if the model value has a value then format the value, otherwise show nothing.
Hope that helps
You can use this instead of using #html.DisplayFor().
#Convert.ToString(string.Format("{0:dd/MM/yyyy}", Model.AuditDate))
You just need To set Data Annotation in your Model.
[DisplayFormat(ApplyFormatInEditMode = true,DataFormatString = "{0:MM/dd/yyyy}")]
public DateTime AuditDate {get; set;}
On view(cshtml page)
#Html.DisplayFor(model => model.AuditDate)
Nothing else you need to do.
Hope its useful.
See this answer about the No overload for method 'ToString' takes 1 arguments error.
You cannot format a nullable DateTime - you have to use the DateTime.Value property.
#Model.AuditDate.HasValue ? Model.AuditDate.Value.ToString("mm/dd/yyyy") : string.Empty
Tip: It is always helpful to work this stuff out in a standard class with intellisense before putting it into a view. In this case, you would get a compile error which would be easy to spot in a class.

class cast exception when data binding with list

The domain looks like
class EventDonation implements Serializable{
String title
String body
Integer customDonationMin
Integer customDonationMax
EntityStatus status
List denominations
static hasMany = [denominations: Integer]
}
Please note that the denominations is a list of Integers. Now during binding
List<String> whiteList = ['title', 'body', 'customDonationMin', 'customDonationMax', 'denominations']
bindData(don, params, ['include': whiteList])
if(params.status){
don.status = EntityStatus.ACTIVE
}
else{
don.status = EntityStatus.INACTIVE
}
don.validate()
if(don.hasErrors()){
render(view: "editdonation", model: [id:id, donation:don])
return
}
The following error is displayed
Message: java.lang.String cannot be cast to java.lang.Integer
In the view page there are 4 textboxes for entering denominations. I was thinking that the strings i.e '10', '20', '30', '40' will automatically be converted to integers during binding. I think the error is associated with binding this list. I appreciate any help. Thanks!
Interesting thing is when i change the type of the list to String as follows:
class EventDonation implements Serializable{
String title
String body
Integer customDonationMin
Integer customDonationMax
EntityStatus status
List denominations
static hasMany = [denominations: String]
}
The following binding works fine i.e no error is thrown.
List<String> whiteList = ['title', 'body', 'customDonationMin', 'customDonationMax', 'denominations']
bindData(don, params, ['include': whiteList])
if(params.status){
don.status = EntityStatus.ACTIVE
}
else{
don.status = EntityStatus.INACTIVE
}
don.validate()
if(don.hasErrors()){
render(view: "editdonation", model: [id:id, donation:don])
return
}
So, i am confident that the type Integer for the list is the source of the problem. Since denominations are integers i cannot think of any other type. I appreciate any help on this. Thank you!

ASP MVC3 ActionLink not rendering variables

I am trying to use an ASP MVC3 action link to navigate to another view (same controller). The view is attached to a model that uses a compound key for its primary key. Below is the action link as it's written on the view
#Html.ActionLink("Edit Agent", "AgentEdit", "BankListMasterController",
new { #agentId = int.Parse(item.AgentId), #id = item.ID})
However when this is rendered it renders as the following
http://localhost:2574/BankListMaster/AgentEdit?Length=24
Which obviously throws an error:
The parameters dictionary contains a null entry for parameter 'agentId' of non-nullable type 'System.Int32' for method 'System.Web.Mvc.ViewResult AgentEdit(Int32, Int32)' in 'Monet.Controllers.BankListMasterController'. An optional parameter must be a reference type, a nullable type, or be declared as an optional parameter.
Parameter name: parameters
Here is the controller method for good measure:
public ViewResult AgentEdit(int agentId, int id)
{
string compare = agentId.ToString();
BankListAgentId agent = (from c in db.BankListAgentId
where c.ID == id &&
c.AgentId.Equals(compare)
select c).Single();
return View("AgentEdit", agent);
}
#Html.ActionLink("Edit Agent", "AgentEdit", "BankListMasterController",
new { agentId = int.Parse(item.AgentId), id = item.ID}, null)
That should do the trick
And rationale is: as per http://msdn.microsoft.com/en-us/library/system.web.mvc.html.linkextensions.actionlink(v=vs.108).aspx
You won't find there method with (HtmlHelper, string, string, string, object) there's however (HtmlHelper, string, string, string, object, object) where the second last object is route values and the last are html attributes.
Based on the parameters you have provided, the wrong ActionLink is being called.
The problem is that it is "attempting to serialize a string object"
Here is the canonical answer to the "'Length' parameter in link" question:
Why does Html.ActionLink render "?Length=4"

Grails Scaffolding - define possible values for this property of a domain class

I am new to Grails. I have a Person domain class as :
class Person {
String firstName
String lastName
String gender
Date dateOfBirth
}
And wondering if I can define possible values for a property - say gender as {M, F, U} so that these three values will be listed in combo box when using dynamic scaffolding for Person controller.
Here I just wanted to know if there is such feature in Grails framework? If such feature exists , then how can I use it?
From the documentation http://grails.org/doc/latest/guide/scaffolding.html, you should be able to use an inList constraint:
class Person {
String firstName
String lastName
String gender
Date dateOfBirth
def constraints = {
gender( inList: ["M", "F", "U"])
}
}
This should scaffold to a select list for the gender field, depending on the version of Grails you're using. 2.0+ definitely does this.
Here is an alternative solution
class Person {
String firstName
String lastName
enum Gender {
M(1),
F(2),
U(3)
private Gender(int val) { this.id = val }
final int id
}
Gender gender = Gender.U
Date dateOfBirth
def constraints = {
gender()
}
}
This will store gender in the database as an integer (1,2,3) and default the gender to U. The benefit here is you can rename what F, M, and U mean without handling a data migration.

Storing string versions of database status values

I'm writing an ASP.NET MVC 3 site and I need to convert some integer values from the database that represent statuses with user readable strings, for example:
...
<td>
#item.Status
</td>
...
I've written an HTML Helper to do the conversion:
public static string MessageType(this HtmlHelper helper, int type)
{
string messageType = "Unknown";
switch((Types.MessageTypes)type)
{
case Types.MessageTypes.Join:
messageType = "Join App";
break;
case Types.MessageTypes.New:
messageType = "New App";
break;
}
return messageType;
}
And I'm storing the types as an enumeration in the Types class.
My question is whether there is a cleaner way to do this? I don't like these magic strings in my code and it creates numerous dependencies in different parts of the code.
Any thoughts?
Many thanks,
Sam
I usual decorate the enums with the DisplayAttribute like this.
enum MessageTypes
{
[Display(Name = "Join App")]
Join,
[Display(Name = "New App")]
New
}
Then I use a helper to extract them:
public static string EnumDisplay(this HtmlHelper helper, Enum model)
{
var enumType = model.GetType();
var modelValue = Convert.ToInt64(model);
var matches = from field in enumType.GetMembers()
where field.MemberType == MemberTypes.Field && Enum.IsDefined(enumType, field.Name)
let value = Convert.ToInt64(Enum.Parse(enumType, field.Name, false))
where modelValue == value
let attribute = field.GetCustomAttributes(typeof (DisplayAttribute), false).Cast<DisplayAttribute>().FirstOrDefault()
select attribute == null
? field.Name
: attribute.Name;
return matches.FirstOrDefault() ?? "Unknown";
}
Then you can do
#Html.EnumDisplay((MessageType)item.MessageType)
In my opinion, you should be storing the corresponding user readable strings in the database next to the integer. And return an object e.g. StatusViewModel to the view model. the View model may have >
class StatusViewModel
{
int type;
string displayMessage;
}
makes sense?

Resources