Kendo UI Core and DropDownListFor

Kendo UI Core supports data attribute initialization of its widgets. This initialization approach is largely created for it’s MVVM capabilities, but also can be used for server-side approaches too, which in turn can be used with ASP.NET MVC HTML helpers. The setup may be a little different than you are used to.

Please note: Although Telerik has MVC wrappers already created and ready for you to develop with, these come with the licensed version of the product and are not available through the Kendo UI Core license.

To begin, let’s look at how we’d setup the dropdownlist as an HTML widget without and use of the server.

<select id="reasonID" data-role="dropdownlist" data-option-label="-Select Reason-" data-value="">
  <option selected="selected" value="">-Select Reason-
  <option value="1">Entered in Error
  <option value="2">Removed
</select>

In our example, a select element with a pre-defined list of options is wrapped with the kendo dropdownlist widget. It’s initialized via the data-role attribute, indicating it’s a dropdownlist widget. Next, it defines options that the widget supports. Most of the attributes on the widget that you would configure through the JavaScript API can be defined here, but the syntax differs sometimes (for instance, the optionLabel property you would use in JS initialization is data-option-label in data attribute initialization). If you run into an issue, I was able to find what I needed online so far.

Telerik has some documentation on data attribute initialization here.

The dropdownlist widget can use a textbox as the source, and can also wire data up using JSON via local JavaScript API of via AJAX if you like. I’ve stuck with a select because I’m using the MVC DropDownListFor widget, and to get this widget to render the correct content that I needed, I had to use the following configuration:

@Html.DropDownListFor(
      i => i.ReasonTypeID,      
      Model.ReasonTypes, 
      new
      {
           data_role="dropdownlist", 
           data_option_label="-Select Reason-", 
           data_value=(Model.Receiving.ShrinkTransactionTypeID.HasValue ? Model.Receiving.ShrinkTransactionTypeID.Value.ToString() : ""), 
           style="width:100%;" })

Most of this is standard. The third parameter of DropDownListFor provides HTML attributes to the rendered select option. Here we define the role data attribute used for initialization. The next attribute defines the option label, and the data_value attribute defines the selected value, which is interesting. We already define what’s selected via the lambda expression pointing to our model. However, that’s used to render the selected attribute. What I found is that to cover all selection scenarios (including the default “Select Something” item) is to add the value attribute too, which the widget uses to perform the selection. The code above manually checks for null and converts to an empty string if no value present.

In case you didn’t notice, note how the HTML attribute use data_role, particularly the underscore. The convention that MVC uses is underscore (supported by the language), which is converted to a dash during the rendering process.

Advertisements

Dynamic Script Registering Component in ASP.NET MVC

One of the performance enhancements recommended in tools like YSlow and others is to put your JavaScript script files at the bottom of the page. The great difficulty in a web forms or MVC web application with doing that is scripts at the bottom of the page are typically added in the master page. Any pages or views can make use of a special section just for scripts, but partial views or user controls can’t take advantage of that.

I’m about to illustrate a solution that will do just that. It’s setup for MVC, but could be easily adapted for web forms by making the helpers as controls. You have to understand that MVC is a top down structure. So as views or partials render, they render their contents in place. The common approach is to have a section for scripts:

site.master:


  // master page common scripts


@* for any custom scripts in the view *@
@RenderSection("scripts", false)

The view can add a @section scripts { } to take advantage of this, but the partial view cannot. Additionally, everything is rendered in top down format; if a partial view at the top of your page has a script like:


$(function() { alert("In the partial view"); });

The problem with this script is that the final rendered view (when scripts are defined at the bottom) will look like:


$(function() { alert("In the partial view"); });


.
.
.
view content
. . // master page common scripts // view scripts

Since it’s top down, it’s also dependent on order, so scripts defined have no dependency (lacking a dependency feature like angular or script JS).

The following solution was meant to give more control over placement of defined scripts. Scripts are defined in a wrapper, and queued up for later rendering. The solution I’m providing is simple, but easily enhanced to add dependencies in scripts, custom ordering, and more features.

To give an idea of what we need to do, the system will achieve something like the following:

view.cshtml:

@Html.AddScript("ViewInit", () => @$(function() { .. }));

partial view:

@Helper Script()
{
   
     ..
   
}

@Html.AddScript("PartialInit", (m) => Script());

master:

@Html.PlaceScripts()

The idea is to add scripts via a method, which queues them up to be rendered, and writes them out at the point the PlaceScripts method is called. Note that for partial views, I’m using a little trick I noted earlier, whereby using a helper is the only way to circumvent exceptions that occur when using the action-based approach.

AddScript essentially adds the definitions to the HttpContext.Items collection, a common trick for storing singleton-like data elements that are available across the application. Since HttpContext.Items is available to add and then render scripts, it’s a great place to share content. The AddScript method shoves in data:

public void AddScript(HtmlHelper html, string key, Action fn)
{
    var http = html.ViewContext.HttpContext;
	var list = http.Items["_Scripts"] as Dictionary;
	
	if (list == null)
		list = new Dictionary();
	
	list[key] = fn;
	
	http.Items["_Scripts"] = list;
}

This method checks to see if we have a dictionary in the items collection; if not, it creates it, pushes in our item, and updates the collection. Similar, our other method, which takes a helper result, uses another overload to store the string result for easy rendering:

public void AddScript(this HtmlHelper html, string key, HelperResult fn)
{
	this.AddScript(html, key, fn(html.ViewData.Model).ToHtmlString());
}

public void AddScript(this HtmlHelper html, string key, string script)
{
    var http = html.ViewContext.HttpContext;
	var list = http.Items["_Scripts"] as Dictionary;
	
	if (list == null)
		list = new Dictionary();
	
	list[key] = script;
	
	http.Items["_Scripts"] = list;
}

We could easily add dependencies and ordering requirements to this solution if we needed; for now, let’s finish this out by showing how to grab the contents and render them:

public void PlaceScripts(this HtmlHelper html)
{
var http = html.ViewContext.HttpContext;
var list = http.Items[“_Scripts”] as Dictionary;

if (list == null)
return;

using writer = new HtmlTextWriter(html.ViewContext.Writer)
{
foreach (var entry in list)
{
if (typeof(entry.Value) is string) {
writer.WriteLine(entry.Value);
}
else {
//This is an action; the action will be
//rendered in the current output stream, and
//do not need to be directly written
entry.Value(html.ViewData.Model);
}
}
}

}

This would need greatly enhanced for dependencies and sorting; right now, we take the list in order and render it out. For HTML results or just plain string definitions, it gets rendered to the current response stream. Actions get processed the same way, but MVC handles that internally, rather than your code.

And voila, we are done; now we can define scripts in the view and partial views and expect them to be rendered where we want them placed, and have a component that can be easily enhanced to do much more.

Rendering Markup Anywhere in MVC

I had a hard time coming up with the title, because as you know, markup is pretty dynamic in MVC. However, I came across an interesting limitation when it came to rendering markup. I’m not talking about rendering partial view content using Html.Partial, or using a helper method. I’m actually talking about rendering markup segments, which I’ll demonstrate with a VB.NET example (sorry, I’ve been stuck in the VB world for some time, it’s become more natural than C#):

@* SomeView.vbhtml *@
@Code
Html.ScriptDefine(
   @
      alert("ME");
   )
End Code

Html.ScriptDefine is not something Microsoft created, but a custom extension I created. This was a helper method to register script segments. It is not a method defined globally or in a view, but a helper that’s code, which can be easily reused across projects, which is why I tried to utilize this technique. Somewhere in the master page, a process read all of these scripts and rendered them in the master page. This was excellent; I could define these script blocks anywhere in the view, and they would all get rendered in one place.

My helper looked like the following:

Public Sub ScriptDefine(Of TModel)(html As HtmlHelper, key as String, fn as Action(Of TModel))
    Dim script = fn(html.ViewData.Model)
    'Store reference to model and script somewhere, 
    'which the master page retrieves all of the scripts and renders
End Sub

It worked, except in one scenario: Partial Views, which is a key reason why I wanted it. See, I often found myself using scripts in a partial view. I tried using an optimization technique where scripts run at the end of the page; the only problem was a partial view that used a script had it’s <script /&rt; block defined wherever the partial was, which was usually above the end of the view. The issue with partial views has to do with the rendering process, and although I wasn’t quite sure how to figure out why, I found a better solution anyway: HelperResult.

By defining the script in a helper (a small caveat) and then storing the helper result, this solved the problem much more easily. I was able to define an extension like the following:

Public Sub ScriptDefineHelper(Of TModel)(html As HtmlHelper, key As String, fn As Func(Of TModel, HelperResult))
   Dim helperResult = fn(html.ViewData.Model) 'Returns the content as IHtmlString
   Dim list = CType(html.ViewContext.HttpContext.Items("_Scripts_"), List(Of String))

   if (list Is Nothing) Then
      list = new List(Of String)
   End If

   list.Add(helperResult.ToHtmlString()) 'Store the scripts as a string, which is easy to render later

   html.ViewContext.HttpContext.Items("_Scripts_") = list
End Sub

Now wherever we use our helper, we can use it like:

@Code
  'Use in view or partial view
  Html.ScriptDefineHelper(Function(i) Scripts())
End Code

@Helper Scripts()
   
      alert("Hello");
   
End Helper

And we can render out all the scripts with the following code (we can also use a helper method for this):

Dim items = CType(html.ViewContext.HttpContext.Items("_Scripts_"), List(Of String))
For Each item in items
  @Html.Raw(item)
Next

The real question is why do all of this, when all of the scripts could be in the page? Well, there are good reasons for doing this. First and foremost, keeping the scripts used in a partial view are best defined in the partial view. Out of sight is out of mind, especially for JavaScript. By using this technique, scripts can be defined, and rendered at the designed area, more effectively. That is the primary benefit; outside of that, there aren’t a lot of benefits.

Adding ASP.NET MVC Anti-Forgery Tokens To All Post Requests Easily

One of the newer attacks against web applications is the cross-site request forgery attack. It’s an attack against modern applications that store a cookie to represent the currently logged in user. The problem has been explained in other web sites. I’d highly recommend checking out Phil Haack’s blog post on the subject.

One of the techniques to prevent this attack is to add an anti-forgery token using the @Html.AntiForgeryToken extension method. On the controller side, the action method defines the [ValidateAntiForgeryToken] attribute. Behind the scenes, the hidden input field for the anti-forgery token is validated by the MVC framework to ensure it’s correct. This has also been explained well; see Steve Sanderson’s post on the subject. While there is discussion as to whether this approach is needed just for the logging in an anonymous posts, or all posts in general, as been up for debate. But the point of CSRF is to attack authenticated users.

I’m not real fond of repetitive coding, especially when the framework is flexible enough to avoid it. Below is my solution to to create a flexible solution to validate all post operations. The first task is to create an attribute for validating the token. After using .NET Reflector by Red Gate to examine the existing ValidateAntiForgeryTokenAttribute class, the token is simply an authorization attribute that validates the request using a helper utility to validate it. See the example below.

public class GlobalAntiForgeryTokenAttribute
  : FilterAttribute, IAuthorizationFilter
{
  public sub OnAuthorization(filterContext As AuthorizationContext)
  {
	if (filterContext.HttpContext.Request.HttpMethod.ToUpper() == "POST")
	{
	  AntiForgery.Validate();
    }	
  }
}

Make sure to add this to the GlobalFilters.Filters collection:

GlobalFilters.Filters.add(new GlobalAntiForgeryTokenAttribute);

On authorization of the request, if the operation is a POST request, we call the Validate() method on the AntiForgery helper to actually perform the validation. All of our post operations are now checked for forgery; however, this will fail because we haven’t added our token globally. To do that, we have to create a custom form extension method like the following:

public static void FormExtensions
{
   public static MvcForm BeginDataForm(this HtmlHelper html, string action, string controller, ...)
   {
     var form = html.BeginForm(action, controller, ...);
	 //At this point, the form markup is rendered in BeginForm
	 // we can render the token
	
	 //With every form, we render a token, since this
	 //assumes all forms are posts
	 html.ViewContext.Writer.Write(html.AntiForgeryToken().ToHtmlString());
	
	return form;
   }
}

If we use our custom helper for all of our forms, then all of our custom forms will have rendered an anti-forgery token. Therefore we don’t have to worry about creating it ourselves, saving time and reducing code.

VB.NET Inline Functions in ASP.NET MVC and Razor

I’ve seen various debate about whether inline markup expressions work in VB, and I want to tell you that they do. Here is how I did it. Define a method like the following, which will be an HTML helper extension. Please see my introductory post on how to do that:


<Extension>
Public Sub Inline(Of TModel)(html As HtmlHelper(Of TModel), template As Action(OF TModel))

In a view, you can define an implementation of that method like the following:


@Code

Html.Inline(Sub(m)
@<span>This is my template</span>
End Sub)

End Code

This approach has worked for me, as long as any helper using the template is inside an @Code block, and the Sub(m) declaration is a multi-line sub, not an inline statement.