breaking datetime into constituent parts ASP.NET MVC form - asp.net-mvc

i have searched the web relentlessly for this and have not found anything - which is surprising because i would think it is such a common scenario!
Basically, on my model i have a DateTime field which i wish the user to populate through a form. I am using the Html helper to render all other parts of the form (along with validation)
So this question is in two parts...
Html Helper
Firstly, is there any way to use the Html helper to split the DateTime field to be rendered as the three constituent parts of a date: day, month, year (since i do not care about the time part). This could be rendered as text boxes, drop down lists or a combination of both.
Model Binding
And then when the form is posted, what is the best approach for binding back up to the model? I have seen Scott Hanselmann's solution to this, but it seems a little bloated for what i need - i was hoping for a slightly more elegant solution. Is it recommended to extend DefaultModelBinder and set that as default binder (since all dates would be handled in this way) or write a class that implements IModelBionder and set it as the default binder for the DateTime type?
Thanks for all the help in advance :-) i'm loving MVC but it's infuriating me that something so trivial is causing so much headaches!

think i've worked out a decent solution to this, so i will provide an answer for any who stumble across this in future!
With regards to the Html helper, i somehow completely overlooked creating an extension method! So eventually when it occurred to me, i wrote an extension method which basically makes calls to other methods of the Html helper to provide three fields (whether you use drop down of text inputs is your choice)
public static string DateTime(this HtmlHelper helper, string name)
{
StringBuilder builder = new StringBuilder();
string dayName = name + ".Day";
string monthName = name + ".Month";
string yearName = name + ".Year";
builder.Append(helper.TextBox(dayName));
builder.Append(helper.DropDownList(monthName, typeof (Months)));
builder.Append(helper.TextBox(yearName));
return builder.ToString();
}
As for the binding after a form post, i simply created a class which implemented IModelBinder and set it as default for DateTime type on application start. This can then be overridden at a controller action level should a different method of binding be required. This was essentially a simpler version of Scott's binder which i linked to in my question.
Works like a charm at the moment (if a little simplistic), but i'm sure it'll be enough of a foundation for anyone else who is mystified by this problem!

Related

Avoiding foreach for #html.checkboxfor

I am developing an application using MVC. I had a requirement where I have to display checkbox for a list.
I was going through different posts for doing this, one of them is the use of avoiding foreach for looping and making use of #html.editorfor() as described in the answer by Darwin dimitrov here:
This answer works fabulously fine, but I have a clarification , it is:
In the same view I have 2 requirements , the one with checkboxfor and the other one with radiobuttonfor
So, If I am using
<div>#Html.EditorFor(x => x.RoleAccess)</div>
How do I write the (~/Views/Shared/EditorTemplates/RoleAccessViewModel.cshtml) to serve for checkboxfor for one requirement , and the other one for #radiobuttonfor .
Wont this approach be hardcoded which will always render the RoleAccessViewModel.cshtml whenever EditorFor(x => x.RoleAccess) is used?Please execuse me If I have used any technical terms wrong way,as I still a novice in mvc.
The EditorFor method has an overload that accepts a template name as argument. I think that solves your problem if I understand it correctly. See http://msdn.microsoft.com/en-us/library/ee407414%28v=vs.118%29.aspx
You can also solve this by using the UIHint attribute on your property instead (or in addition to) relying on naming the template after your view model. Then you can create an alternate template to render the radio buttons and specify that:
[UIHint("RadioList")]
public List<Something> MyRadioButtonList { get; set; }
EditorFor will then look for the template: Views\Shared\EditorTemplates\RadioList.cshtml
You could do the same for your checkbox list, as well, instead of relying on the view model. For example, [UIHint("CheckboxList")] and CheckboxList.cshtml. Then, you'd be able to apply these templates more broadly.

Where is the best place to format Model properties in ASP.NET MVC(3)?

I've been looking a lot recently as best practices within the ASP.NET MVC framework, specifically around sparation of concern. My question here is, if I have a property on a model, where is the best place to do some formatting on that property?
I've thought about a few options but am not sure which is best. The example uses a DateTime. Sorry this got a bit long.
Option 1:
In the view: #Model.TheDate.String("{0:'blah'dd/MM/yyyy}")
I know this isn't right because the format string shouldn't be here (what if I want to change the format throughout the whole app)
Option 2:
Extension method used within the view: #Model.TheDate.DisplayItNicePlease()
This kind of makes sense because the view is chosing the extension method to use for formatting and the viewmodel just exposes the date property to the view. Not sure if the view or view model should be responsible for formatting though.
Option 3:
In an Html Helper method, i.e. something like Where should I place Declarative HTML helpers in ASP.NET MVC 3 so you could use:
#Html.DisplayDateNice(Model.TheDate)
Option 4:
Extra property on the view model to format it and return a string e.g.:
public string TheDateStr
{
get
{
return TheDate.DisplayItNicePlease();
}
}
And then in the view you just have
#Model.TheDateStr
Fairly similar to option 3 only it keeps the view really, really simple - the view literally just outputs what is in the model and doesn't care about the format of the data.
Option 5:
DisplayFormat attributes, e.g.:
On the view model:
[DisplayFormat(DataFormatString = "{0:'blah'dd/MM/yyyy}")]
public DateTime TheDate { get; set; }
And then on the view:
#Html.DisplayFor(m => m.TheDate)
I like this apart from again the format string is not reused, so bring on option 5...
Option 6:
Some kind of custom DisplayFormatAttribute e.g. NiceDateDisplayFormatAttribute.
This seems to me like it might be the nicest solution and works well if you just want a simple string formatting. I think this gets more complicated if you want something more complicated, e.g showing relative time (Calculate relative time in C#), in which case maybe this is best being in an extension method/html helper.
There are probably other ways I'm not even aware of...
Either use an extension helper to format the type if this type formatting will be used a lot or add an extra property in the Model to return a formatted version of the original property.
For a single view, I personally use option 5: putting it in the view model.
If date format's being used for multiple pages, you can create a html helper extension for formatting the date. So in the view, you'd have something like
#Model.TheDate.ToDateString()
But overall, i still prefer option5 because i create viewmodels for everypage, and just throw a DisplayFormat attribute on it.

How to alter the rendering of links in ASP.NET MVC?

Our designers have come up with button styles for an application which require the addition of <span> tags inside the <a> tags of our links.
In ASP.NET we implemented this by adding an App_Browsers entry for Link Buttons.
How would I go about doing this in ASP.NET MVC?
I've contemplated creating my own versions of all of the various HTML helper functions for creating ActionLinks and RouteLinks but this seems to be quite a 'brute force' way of doing things.
Is there a nice elegant way of doing it?
I know we could write some simple jQuery to do it, but we'd rather have the markup coming out of the server correctly in the first place.
Actually I think writing a new helper is exactly the way I would go. Seems to me that that's exactly what they are there for and it makes them very re-usable too.
You could always write one extension method, that takes another one (one of the built-in ones) as an argument, and wrappes the <span> around your link text before calling it. It should be quite easy to do with lambdas...
public static string SpanLink(this HtmlHelper helper,
string linkText, object args, Action<string> action)
where TController : IController
{
action("<span>" + linkText + "</span>", args);
}
And to call it:
<%= Html.SpanLink<HomeController>("link text", (s) => Html.ActionLink<HomeController>(c => c.Index(s));
(This code is typed directly into the answer field of SO - I haven't even checked it to make sure it compiles. So bear with me if it doesn't work on the first try...)

lessons learned or mistakes made when using asp.net mvc

what are your top lessons learned when starting asp.net mvc that you would highlight to someone starting out so they can avoid these mistakes?
Use Html.Encode() everywhere you print data, unless you have a very good reason to not do so, so you don't have to worry about XSS
Don't hardcode routes into your views or javascripts - they're going to change at some point, use Url.Action() instead
Don't be afraid of using partial views
MVC is no silver bullet, first evaluate if it's indeed the best tool of choice for solving your problem.
Don't forget the "Unit Tests" part of the pattern.
Try to always use a ViewModel to pass data between the Controller and the View.
You may think you don't need one, you can just pass your model around, but suddenly you need a list box with several options for editing a model, or displaying a message (not validation message) and you start adding items to the ViewData, with magic strings as keys, making the app harder to maintain.
There are also some security issues that you solve with a ViewModel.
For instance:
class user:
int id
string name
string email
string username
string password
Your view let's the user change his name and email and posts to the action
public ActionResult Edit(User user)
{
--persist data
}
Someone could tamper your form and post a new password and username and you will need to be very careful with the DefaultBinder behavior.
Now, if you use a ViewModel like:
class userEditViewModel:
int id
string name
string email
The problem is gone.
Whenever it is possible make your view typed
Avoid logic in your views
stay away from the HttpContext
Get Steve Sandersons Pro ASP.NET MVC Framework
Debug into the Sourcecode
If you make a Controller method with a different parameter name from id for a single parameter method, you have to make a new route. Just bite the bullet and use id (it doesn't care about the type) and explain it in the comments.
Makes sure you name your parameters with RedirectToAction :
return RedirectToAction("DonateToCharity", new { id = 1000 });
You lose your ViewData when you RedirectToAction.
Put javascript in seperate files, not into the view page
name of the controller :)
unit test Pattern
Don't use the Forms collection, use model binding.
Try not to use ViewData, create a ViewModel.
If you have a loop or an if in your View, write an HTML helper.
Kindness,
Dan
Don't let your controller become a fat one and do too much work. I've seen 1000+ line controllers in the past and it just becomes an absolute nightmare to understand what's going.
Utilise unit testing for your controllers to ensure that dependencies are kept under control and that your code is testable.
Don't get drawn into letting jQuery and fancy clientscript define the behaviour of your application, try and use it as sparingly as you can and let it enhance your application instead.
Use partial views and HTML helpers whenever possible to ensure that your Views do not become unwieldy and a maintenance nightmare.
Use a ViewModel whenever possible.
Use a dependency injection framework to handle your dependencies (MvcContrib has several controller factories, though it's simple enough to roll your own).
Use a different controller for every section of your site (e.g., Home, Account)
Learn how to use ViewData and TempData
Learn what's the use of RenderPartial

Converting classic ASP.NET custom control to MVC

Ok, this is for me a very tough challenge. We're taking our existing ASP.NET website and converting (redesigning the PL only) to MVC. Our site is very complex. But the hard part is to convert the existing custom controls to MVC equivilant. The custom controls (I am not talking about user controls) are just of course a class currently that inherits System.Web.UI.Control and uses that object throughout. For example, we have some properties at the top of this existing custom class like so:
Dictionary<int, Control> configControls;
DropDownList kControl;
CheckBox confirmBox;
These all are variables of type Web controls in classic ASP.NET.
So I figured maybe what I could do (without building entire new custom controls from scratch) is to use the HtmlHelper object. So I tried this:
(include first the using statement that includes System.Web.MVC.Html at the top of my new custom class in our new web project)
private HtmlHelper helper;
Dictionary configControls;
helper.DropDownList
but this is not working. I guess I can't use this object just like this ?? I figured I can use HtmlHelper in the Dictionary and then make variable types off of helper. but those are just extension methods, not objects
I don't know of an equivalent to something like the generic "Control" we had available to us to inherit from such as in classic ASP.NET. Surely it won't be the same in MVC obviusly (stateless and a completely diff way of doing things) but what can I use in MVC with the same concept sort of?
So I figured maybe what I could do (without building entire new custom controls from scratch) is to use the HtmlHelper object. So I tried this:
(include first the using statement that includes System.Web.MVC.Html at the top of my new custom class in our new web project)
private HtmlHelper helper;
Dictionary configControls;
helper.DropDownList
but this is not working. I don't even know if this approach will work in my custom control. And when I try to use my helper variable, I get no extension methods unless it's inside an existing extension method where the signature has an HtmlHelper param passed in. So when I create that private variable just in my custom class outside, I get nothing in intellisense to choose from when doing "helper.". So do I need to define that object like this: ?
private HtmlHelper htmlHelper = new HtmlHelper();
but it's asking for a ViewContext and an IViewDataContainer as params. If I'm building out a custom method that knows nothing yet about its view (it shouldn't need to) because I'm simply creating strings of HMTL in this custom class to be passed to the Extension method to ultimately spit out fields then maybe I can't use HtmlHelper this way in a custom class like this.
So can I use that object in a way instead of "Control"? Maybe I can even in my dictionary variable use type object in place of control ? I don't know and then cast object to type HtmlHelper when I need to use or reference that value from the dictionary? But for now, I figured I can use HtmlHelper object in the Dictionary and then make variable types off of helper. but those are just extension methods, not objects.
I hope I am making any sense here when you read this.
I just blogged about this last night, some of this might be helpful for you.
WebForms And MVC In Harmony — Almost…
Basically it discusses some options for emulating "WebControls" using MVC.
Additionally, you can still use WebControls like you could before (granted they may not work if they need things like the ViewState). The problem I've discovered with that is you have a disconnect from the inline render code and the WebControls themselves.
I did write this method last night which let you use WebControls with inline code.
using System.Reflection;
using System.IO;
using System.Web.UI;
using System.Web;
using System.Web.Mvc;
public static class MyExtensionMethods {
//example method - renders a webcontrol to the page
public static void RenderControl(this HtmlHelper helper, Control control) {
//perform databinding if needed
MethodInfo bind = control.GetType().GetMethod("DataBind");
if (bind is System.Reflection.MethodInfo) {
bind.Invoke(control, null);
}
//render the HTML for this control
StringWriter writer = new StringWriter();
HtmlTextWriter html = new HtmlTextWriter(writer);
control.RenderControl(html);
//write the output
HttpContext.Current.Response.Write(writer.ToString());
//and cleanup the writers
html.Dispose();
writer.Dispose();
}
}
//then used like...
<% int[] numbers = { 1, 2, 3, 4, 5 }; %>
<% this.Html.RenderControl(new DataGrid() { DataSource = numbers }); %>
Just an interesting concept you might be interested in.
Short of hacking webforms controls into your MVC application, servercontrols with many methods do not map to MVC.
They are replaced by partials and controllers(or subcontrollers if you like that sort of thing).
If all you want to do is render some HTML based on a few parameters, then a Helper is what you are after. Static Class, static methods. If however, you need to keep state, and do a bunch of stateful stuff, then a partial, JS, and controller(or subcontroller) are really what you are after.
Server Controls that manage their own state really are a thing of the past in MVC.
Remember that MVC is an attempt to use the web the way it was meant to work, particularly if you bring REST into the picture. Webforms is a fudge to make the web work like windows forms.
I would create needed business logic, shared partial view (probably, with quite a lot of well structured javascript lines attached) and seperated controller.
Then i would use this bunch of code through partial request technique.
Not sure how much this will be of help but, do have a look at this series of blog post
Custom controls everywhere
Also have a look at the Catharsis project
Web-Application Framework - Catharsis - Part I - New Solution
The codeplex URL for the same is
Catharsis
This project has some good examples of control creating for asp.net mvc.

Resources