How to format date in lambda expression in Razor View? - asp.net-mvc

I have a View on which I need to display a date formatted in "dd/MM/yyyy".
Actually it is displayed as: #Html.LabelFor(model => model.DtNews) and I don't know where and how I can put the Format() function.
Data is retrieved from the database using EF. How can I do it?

#Html.LabelFor(model => model.DtNews)
#Html.EditorFor(model => model.DtNews)
and on your view model you could use the [DisplayFormat] attribute
[DisplayFormat(ApplyFormatInEditMode = true, DataFormatString = "{0:dd/MM/yyyy}")]
public DateTime DtNews { get; set; }
Now you're gonna tell me that this class is generated by the EF framework to which I would respond to you: YOU SHOULD NOT USE YOUR EF AUTOGENERATED MODELS IN YOUR VIEWS. You should define and use view models. The view models are classes specifically tailored to the requirements of your views. For example in this particular view you have a requirement to format the dates in a particular way: perfect fit for view models:
public class MyViewModel
{
[DisplayFormat(ApplyFormatInEditMode = true, DataFormatString = "{0:dd/MM/yyyy}")]
public DateTime DtNews { get; set; }
}
then your controller action could query your repository and fetch the domain model (EF autogenerated entity) and map it to a view model. Then it will pass this view model to the view.

I'd just throw a buddy class on your model.DtNews
A buddy class will decorate your existing model
[MetadataType(NewsMetadata)]
public partial class News // this is the same name as the News model from EF
{ /* ... */ }
/* Metadata type */
public class NewsMetadata
{
[DisplayFormat(ApplyFormatInEditMode = true, DataFormatString = "{0:dd/MM/yyyy}")]
public DateTime DtNews { get; set; }
}

Try this. it work for me.
#Model.DtNews.Value.ToString("dd-MM-yyyy")

#Html.LabelFor(model => model.DtNews.ToString("dd/MM/yyyy"))
^should do the trick.
you could also use editor/display templates as discussed here.

If DtNews is a DateTime, then try this:
#Html.LabelFor(model => model.DtNews.ToString("dd/MM/yyyy"));

Use This
#Html.TextBoxFor(m => m.MktEnquiryDetail.CallbackDate, "{0:dd/MM/yyyy}")

Related

ASP.NET MVC Webgrid display multiple columns in a single row

I have a Asp.net MVC grid.
My problem is I need to display multiple columns in a single row.
For example:
Name Date Compensation
Id USD - 99999
Grade INR - 99999
The above layout is a single row in the grid.
All the columns (Name, Id, Grade, Curency1, Amount1, Currency2, Amount2 ) are available in a single record as separate columns. Here Currency1 means USD and Currency2 means INR.
Any ideas how to do this. I am using a strongly typed model and EF6.
I think the best way to do this would be to create a separate 'type' and model for each multi-faceted column, then try to display this type in the webgrid (which I show is possible in the latter part of my example).
For example:
Create a new 'type' (or 'column') class called CompensationColumn:
...
using System.Web.Mvc;
namespace yourproject.Columns // I put this in its own namespace/folder - you don't have to
{
public class CompensationColumn
{
public string Currency1 { get; set; }
public int Amount1 { get; set; }
public string Currency2 { get; set; }
public int Amount2 { get; set; }
public CompensationColumn(string currency_1, int amount_1, string currency_2, int amount_2)
{
Currency1 = currency_1;
Amount1 = amount_1;
Currency2 = currency_2;
Amount2 = amount_2;
}
}
}
Then create a file called CompensationColumn.cshtml in the yourproject/Shared/EditorTemplates folder (if the Shared folder doesn't exist you can also create a your view/DisplayTemplates folder). Define how this column will look, as if it was a custom 'type' (modify this to your liking):
#model yourproject.Columns.CompensationColumn
#if (Model != null)
{
#Model.Currency1<text> - </text>#Model.Amount1<text><p/></text>
#Model.Currency2<text> - </text>#Model.Amount2
}
else
{
}
Then in your Models folder, create a partial class to extend your current EF table model (file name shouldn't matter). I am going to assume your table is 'employee_table'. I am also adding Metadata for the model in this class as it is a good place to put it if you are using a database-first design:
using System.Web.Mvc;
using yourproject.Columns;
namespace yourproject.Models
{
[MetadataType(typeof(EmployeeModelMetaData))] // This links the metadata class below
public partial class employee_table // This should be the EF class name
{
[DisplayName("Compensation")]
public CompensationColumn Compensation { get; set; } // Here we add a new field for your row
}
public class EmployeeModelMetaData
{
// copy your EF class fields here and decorate them with dataannotations. This is helpful
// if you are using a database-first design as it won't get overwritten when db changes.
[DisplayName("Id")]
public int emp_id { get; set; }
[DisplayName("Amount")]
[DisplayFormat(DataFormatString = "{0:c}", ApplyFormatInEditMode = true)]
public int emp_amount1 { get; set; }
// etc . . .
}
}
I make a few assumptions here about a database-first design, but you should be able to figure out how to adapt it to a code-first design if needed.
If you also need to edit elements this column type together, then you would need to create a model binder, but I'm not going there since you only mentioned displaying it.
To get the display template to display in the webgrid, you will need to format: the columns of the webgrid. In your view with an IEnumerable model (e.g. your Index view):
#{
var grid = new WebGrid(Model);
List<WebGridColumn> columns = new List<WebGridColumn>();
WebGridColumn col = grid.Column(columnName: "Col3", header: "Compensation", format: (item) =>
{
yourproject.Columns.CompensationColumn c = item.Compensation; return Html.DisplayFor(model => c);
} );
columns.Add(col);
}
#grid.GetHtml(columns: columns)
This last snippet I adapted from Frédéric Blondel's code here

Custom Data Annotations for Complex Models

I have a view model like this:
public class Event
{
public string Name { get; set; }
[DateRangeValidator]
public DateTimeSpan DateRange { get; set; }
}
And this contains another class called DateTimeSpan that looks like this:
public class DateTimeSpan
{
public DateTime Start { get; set; }
public DateTime End { get; set; }
}
I want to enable jQuery unobtrusive validation on the client side so I have written a custom data annotation validator (it inherits from ValidationAttribute and implements IClientValidatable) but it only seems to work if I apply the annotation to the Start and End properties of the DateTimeSpan class, and not if I apply it to the DateRange property of the Event class.
This is in my view:
#Html.LabelFor(x => x.DateRange.Start, "Start Date:") #Html.ValidationMessageFor(x => x.DateRange.Start)
#Html.TextBoxFor(x => x.DateRange.Start)
#Html.LabelFor(x => x.DateRange.End, "End Date:") #Html.ValidationMessageFor(x => x.DateRange.End)
#Html.TextBoxFor(x => x.DateRange.End)
ASP.NET MVC 3 will only inject the unobtrusive JavaScript data-* attributes into the HTML if the annotation is added to the Start and End properties, is there a way to make it work if the property is applied to the DateRange property instead?
I don't want my domain model class (DateRange, i.e. non view-model classes) to have to implement IClientValidatable because then I have to reference System.Web.Mvc in the domain model project.
Edit: Not sure if it's relevant but the DateRangeValidator attribute does checks to make sure the end date occurs after the start date etc.

DisplayFormat.DataFormatString for a phone number or social security number

Is there a way I can use the DisplayFormat attribute on a view model property to apply a DataFormatString format for a social security number or a phone number? I know I could do this with javascript, but would prefer to have the model handle it, if possible.
[DisplayFormat(ApplyFormatInEditMode = true, DataFormatString = "???????")]
public string Ssn { get; set; }
The following should work, however notice the type difference for the Ssn property.
[DisplayFormat(DataFormatString = "{0:###-###-####}")]
public long Phone { get; set; }
[DisplayFormat(ApplyFormatInEditMode = true, DataFormatString = "{0:###-##-####}")]
public long Ssn { get; set; }
Note, that in order for the formatting to be applied you would need to use the following html helper in your view:
#Html.DisplayFor(m => m.Property)
The accepted answer is great if the type is an integer, but a lot of ids wind up being typed as a string to prevent losing leading zeros. You can format a string by breaking it up into pieces with Substring and make it reusable by sticking it in a DisplayTemplate.
Inside /Shared/DisplayTemplates/, add a template named Phone.vbhtml:
#ModelType String
#If Not IsNothing(Model) AndAlso Model.Length = 10 Then
#<span>#String.Format("({0}) {1}-{2}",
Model.Substring(0, 3),
Model.Substring(3, 3),
Model.Substring(6, 4))</span>
Else
#Model
End If
You can invoke this in a couple ways:
Just annotate the property on your model with a data type of the same name:
<DataType("Phone")> _
Public Property Phone As String
And then call using a simple DisplayFor:
#Html.DisplayFor(Function(model) model.Phone)
Alternatively, you can specify the DisplayTemplate you'd like to use by name:
#Html.DisplayFor(Function(model) model.VimsOrg.Phone, "Phone")

How do I add a Custom Query for a drop down and retain the View Model Pattern?

I've read many articles which they state that querying should not be placed in the Controller, but I can't seem to see where else I would place it.
My Current Code:
public class AddUserViewModel
{
public UserRoleType UserRoleType { get; set; }
public IEnumerable<SelectListItem> UserRoleTypes { get; set; }
}
public ActionResult AddUser()
{
AddUserViewModel model = new AddUserViewModel()
{
UserRoleTypes = db.UserRoleTypes.Select(userRoleType => new SelectListItem
{
Value = SqlFunctions.StringConvert((double)userRoleType.UserRoleTypeID).Trim(),
Text = userRoleType.UserRoleTypeName
})
};
return View(model);
}
The View:
<li>#Html.Label("User Role")#Html.DropDownListFor(x => Model.UserRoleType.UserRoleTypeID, Model.UserRoleTypes)</li>
How do I retain the View Model and Query and exclude the User Type that should not show up?
I think that you are doing it just fine.
Any way... all you can do to remove the querying logic from controller is having a ServiceLayer where you do the query and return the result.
The MVC pattern here is used correctly... what your are lacking is the other 2 layers (BusinessLayer and DataAccessLayer)... since ASP.NET MVC is the UI Layer.
UPDATE, due to comment:
Using var userroletypes = db.UserRoleTypes.Where(u=> u.UserRoleType != 1);
is OK, it will return a list of UserRoleType that satisfy the query.
Then, just create a new SelectList object using the userroletypes collection... and asign it to the corresponding viewmodel property. Then pass that ViewModel to the View.
BTW, I never used the db.XXXX.Select() method before, not really sure what it does... I always use Where clause.
SECOND UPDATE:
A DropDownList is loaded from a SelectList that is a collection of SelectItems.
So you need to convert the collection resulting of your query to a SelectList object.
var userroletypes = new SelectList(db.UserRoleTypes.Where(u=> u.UserRoleType != 1), "idRoleType", "Name");
then you create your ViewModel
var addUserVM = new AddUserViewModel();
addUserVM.UserRoleTypes = userroletypes;
and pass addUserVM to your view:
return View(addUserVM );
Note: I'm assuming your ViewModel has a property of type SelectList... but yours is public IEnumerable<SelectListItem> UserRoleTypes { get; set; } so you could change it or adapt my answer.
I don't see anything wrong with your code other than this db instance that I suppose is some concrete EF context that you have hardcoded in the controller making it impossible to unit test in isolation. Your controller action does exactly what a common GET controller action does:
query the DAL to fetch a domain model
map the domain model to a view model
pass the view model to the view
A further improvement would be to get rid of the UserRoleType domain model type from your view model making it a real view model:
public class AddUserViewModel
{
[DisplayName("User Role")]
public string UserRoleTypeId { get; set; }
public IEnumerable<SelectListItem> UserRoleTypes { get; set; }
}
and then:
public ActionResult AddUser()
{
var model = new AddUserViewModel()
{
UserRoleTypes = db.UserRoleTypes.Select(userRoleType => new SelectListItem
{
Value = SqlFunctions.StringConvert((double)userRoleType.UserRoleTypeID).Trim(),
Text = userRoleType.UserRoleTypeName
})
};
return View(model);
}
and in the view:
#model AddUserViewModel
<li>
#Html.LabelFor(x => x.UserRoleTypeId)
#Html.DropDownListFor(x => x.UserRoleTypeId, Model.UserRoleTypes)
</li>

DataAnnotations on public fields vs properties in MVC

Why don't DataAnnotations work on public fields? Example:
namespace Models
{
public class Product
{
[Display(Name = "Name")]
public string Title; // { get; set; }
}
}
public ActionResult Test()
{
return View(new Models.Product() { Title = "why no love?" });
}
#Html.LabelFor(m => m.Title) // will return 'Title' if field, or 'Name' if property
#Html.DisplayFor(m => m.Title)
If Title is a field, then the Display attribute seems to have no effect. If Title is changed to a property, it works as expected as displays "Name".
It would seem easy in this example to just change to a property, but I am trying to use the types from F# where they get compiled to a class with fields and not properties.
This was tested in ASP.NET 4 and MVC RC 3.
The reason why DataAnnotations do not work with fields is because the reflection-like mechanism that is used to retrieve the attributes (TypeDescriptor) only supports properties.
While it would not be easy, we could look into making this work with fields if there is enough demand.

Resources