Storing string versions of database status values - asp.net-mvc

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?

Related

Type cast an generic enum T to its type of T

I am building an generic SelectListItem to be used with a dropdown. Trying to cast an Enum of type T to its type , but Iam missing something. LocalizedStringKeyFor complains that its getting two parameters but as I sees it ((typeof(T))status) is just one parameter????
public static SelectListItem BuildSelectListItem<T>(T status, T? selected) where T : struct, Enum
{
var pretext = LocalizedString.Get(LocalizedStringKeyHelper.LocalizedStringKeyFor((typeof(T))status));
return new SelectListItem()
{ Text = pretext, Value = ((int)Convert.ChangeType(status, typeof(T))).ToString(), Selected = selected.HasValue && EqualityComparer<T>.Default.Equals(status, selected.Value) };
}
Actually you do give "two" different parameters to LocalizedStringKeyFor
You pass typeof(T) "and" status (missing some comma's but ok).
I think you're kind of looking for a helper function like below; where you pass your "T" (as enum) as the enumerator and the "selected" as parameter.
public static string LocalizedStringKeyFor<T>(T? selected) where T : struct, Enum
{
return "Not a clue";
}
public static IEnumerable<SelectListItem> BuildSelectList<T>(T? selected) where T : struct, Enum
{
var values = new List<SelectListItem>();
foreach (T type in (T[])Enum.GetValues(typeof(T)))
{
values.Add(new SelectListItem()
{
Text = LocalizedStringKeyFor<T>(type),
Value = Convert.ToInt32(type).ToString(),
Selected = selected == null ? false : selected.GetValueOrDefault().Equals(type)
});
}
return values;
}
In the method above I don't have a clue what your LocalizedStringKeyFor does, so that's the answer "Not a clue" - but it uses your enum as T for listing all options and marking the "selected?" as selected option. If there is no selected present your first option will default to selected.
You want to pass your enum value as an int, therefor the Int32 conversion and beware that this method does not work with flagged enums (or at least combined flagged enums).
UPDATE: How to use;
You can use this function for instance like so (where Model.Role is of course your enum)
#Html.DropDownListFor(m => m.Role, _dropdownLists.BuildSelectListItem<Contracts.Roles>(Model.Role), new { #class = "form-control" })

#Html.DisplayFor for Empty

I am trying to display empty string when model.EndDate is 0
#Html.DisplayFor(modelItem => model.EndDate)
I tried
#Html.DisplayFor(modelItem => model.EndDate == 0 ? "" : model.EndDate.ToString())
and
#Html.Display("End Date",model.EndDate == 0 ? "" : model.EndDate.ToString())
both did not worked for me. Both of the displaying empty when data is available.
Do a conditional outside the DisplayFor:
#if (Model.EndDate != 0)
{
Html.DisplayFor(modelItem => model.EndDate)
}
You can't compare a datetime to 0 in asp.net.
error CS0019: Operator `==' cannot be applied to operands of type `System.DateTime' and `int'
A DateTime is a value type so it get a default value of DateTime.MinValue when it isn't set.
using System;
public class Test {
public static DateTime badDate;
public static DateTime goodDate = DateTime.Now;
public static void Main(string[] args) {
Console.WriteLine(goodDate == DateTime.MinValue ? "" : goodDate.ToString());
Console.WriteLine(badDate == DateTime.MinValue ? "" : badDate.ToString());
}
}
How about this one ?
DateTime dt;
if (DateTime.TryParse("Your Date", out dt))
{
//Now you have validated the date
//Your code goes here
}
I think you should make your model property be a nullable type. So for example if its type is DateTime then declare the property as a nullable DateTime:
public DateTime? EndDate { get; set; }
this way instead of a 0 date (whatever that means) you will have no value. This also makes sense especially if this model property is coming from the database and is nullable.
You can read more about the nullable types here: http://msdn.microsoft.com/en-us/library/2cf62fcy(v=vs.80).aspx
I didn't like the answers given above because adding IF Statements to everything just slows your system down, in my case i needed to display a default string value several times in each row of a list view when the class property was a null value.
So have a look at the MS Docs here:
DisplayFormatAttribute.NullDisplayText Property
The following example shows how to use the NullDisplayText to define a caption to display when the data field is null.
// Display the text [Null] when the data field is empty.
// Also, convert empty string to null for storing.
[DisplayFormat(ConvertEmptyStringToNull = true, NullDisplayText = "[Null]")]
public object Size;

Dart: How to bind to variables annotated with int via Web UI?

What is the best practice in Dart when dealing with classes as data records?
To Elaborate: When writing an app, it is likely that a class for a table row will be created. As in
class Item { int itemid, String itemName, double score }
Item item = new Item();
This allows compile time catching of any typos etc. in Dart. (Unlike using a class that relies on NoSuchMethod.)
It will also need a corresponding string structure to bind to the HTML such as
<input id="itemname" type="text" bind-value="itemEdit.itemName">
So the Dart would be:
class ItemEdit { String itemId, String itemName, String score }
ItemEdit itemEdit = new ItemEdit();
Next we need a way to get from one to the other, so we add a method to Item
fromStrings(ItemEdit ie) {
itemid = ie.itemId == null ? null : int.parse(ie.itemId);
itemName = ie.ItemName;
score = ie.score == null ? null : double.parse(ie.score);
}
And the other way around:
toStrings(ItemEdit ie) {
ie.itemid = itemId == null ? '' : ie.itemId.toString();
ie. itemName = itemName == null ? '' : itemname; // Web_ui objects to nulls
ie.score = score == null ? null : score.toStringAsFixed(2);
}
Also, we get jason data from a database, so we need to add another method to Item:
fromJson(final String j) {
Map v = JSON.parse(j);
itemid = v['itemid'];
itemname = v['itemname'];
score = v['score'];
}
And we need to be able to revert to default values:
setDefaults() { itemId = 0; itemName = "New item"; score = 0; }
This verbosity gets me feeling like I am writing COBOL again!
There is something fundamental missing here - either in my understanding, or in the Dart/WebUI libraries.
What I would like to write is something like
class Item extends DataRecord {
int itemid = 0,
String itemName = 'New item',
double score = 0.0;
}
Then, without further coding, to be able to write code such as
item.toStrings();
...
item.fromStrings();
...
item.fromJson(json);
...
item.setDefaults(); // results in {0,'New item',0.0}
And to be able to write in the HTML:
value="{{item.strings.score}}"
If this was possible, it would be quicker, simpler, clearer, and less error prone than the code I am writing at the moment.
(Full disclosure, this answer is written with the assumption that at least one bug will be fixed. See below)
Three suggestions that might help.
Use named constructors to parse and create objects.
Take advantage of toJson() when encoding to JSON.
Use bind-value-as-number from Web UI
1) Named constructors
import 'dart:json' as json;
class Item {
int itemid;
String itemName;
double score;
Item.fromJson(String json) {
Map data = json.parse(json);
itemid = data['itemid'];
itemName = data['itemName'];
score = data['score'];
}
}
2) Encoding to JSON
The dart:json library has a stringify function to turn an object into a JSON string. If the algorithm encounters an object that is not a string, null, number, boolean, or collection of those, it will call toJson() on that object and expect something that is JSON-encodable.
class Item {
int itemid;
String itemName;
double score;
Map toJson() {
return {'itemid':itemid, 'itemName':itemName, 'score':score};
}
}
3) Now, having said that, sounds like you want to easily bind to HTML fields and get primitive values back, not just strings. Try this:
<input type="number" min="1" bind-value-as-number="myInt" />
(Note, there seems to be a bug with this functionality. See https://github.com/dart-lang/web-ui/issues/317)
(from https://groups.google.com/a/dartlang.org/forum/#!topic/web-ui/8JEAA5OxJOc)
Just found a way to perhaps help a little in the this situation:
class obj {
int gapX;
String get gapXStr => gapX.toString();
set gapXStr(String s) => gapX = int.Parse(s);
...
Now, from the HTML you can use, for example
bind-value="gapXStr"
and in code you can use
x += ob.gapX;

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.

ASP.NET MVC 2 - HTML.EditorFor() and Custom EditorTemplates

With MVC 2's addition of the HtmlHelper EditorFor() it is not possible to create strongly typed Display and Editor templates for a given Model object and after fiddling with it I am a bit stumped as to how to pass additional Model data to the editor without losing the strong-typing of the editor control.
Classic Example: Product has Category. ProductEditor has a DropDownList for Category containing the names of all Categories. The ProductEditor is strongly typed to Product and we need to pass in the SelectList of Categories as well as the Product.
With a standard view we would wrap the Model data in a new type and pass that along. With the EditorTemplate we lose some of the standard functionality if we pass in a mixed Model containing more than one object (first thing I noticed was all of the LabelFor/TextBoxFor methods were producing entity names like "Model.Object" rather than just "Object").
Am I doing it wrong or should Html.EditorFor() have an additional ViewDataDictionary/Model parameter?
You can either create a custom ViewModel which has both properties OR you'll need to use ViewData to pass that information in.
I am still learning, but I had a similar problem for which I worked out a solution.
My Category is an enum and I use a template control which examines the enum to determine the contents for the Select tag.
It is used in the view as:
<%= Html.DropDownList
(
"CategoryCode",
MvcApplication1.Utility.EditorTemplates.SelectListForEnum(typeof(WebSite.ViewData.Episode.Procedure.Category), selectedItem)
) %>
The enum for Category is decorated with Description attributes to be used as the text values in the Select items:
public enum Category
{
[Description("Operative")]
Operative=1,
[Description("Non Operative")]
NonOperative=2,
[Description("Therapeutic")]
Therapeutic=3
}
private Category _CategoryCode;
public Category CategoryCode
{
get { return _CategoryCode; }
set { _CategoryCode = value; }
}
The SelectListForEnum constructs the list of select items using the enum definition and the index for the currently selected item, as follows:
public static SelectListItem[] SelectListForEnum(System.Type typeOfEnum, int selectedItem)
{
var enumValues = typeOfEnum.GetEnumValues();
var enumNames = typeOfEnum.GetEnumNames();
var count = enumNames.Length;
var enumDescriptions = new string[count];
int i = 0;
foreach (var item in enumValues)
{
var name = enumNames[i].Trim();
var fieldInfo = item.GetType().GetField(name);
var attributes = (DescriptionAttribute[])fieldInfo.GetCustomAttributes(typeof(DescriptionAttribute), false);
enumDescriptions[i] = (attributes.Length > 0) ? attributes[0].Description : name;
i++;
}
var list = new SelectListItem[count];
for (int index = 0; index < list.Length; index++)
{
list[index] = new SelectListItem { Value = enumNames[index], Text = enumDescriptions[index], Selected = (index == (selectedItem - 1)) };
}
return list;
}
The end result is a nicely presented DDL.
Hope this helps. Any comments about better ways to do this will be greatly appreciated.
Try using ViewData.ModelMetadata this contains all of your class Annotations.
Excellent article http://bradwilson.typepad.com/blog/2009/10/aspnet-mvc-2-templates-part-4-custom-object-templates.html

Resources