Using ClientDependency with MVC 3 beta's Razor view engine

by James Diacono 28. October 2010 11:06

ClientDependency already has full support for MVC...but how do you get it work with the Razor view engine?

Razor and helpers: what’s the deal man?

@Html.HelperA()

@{
	Html.HelperB();
}

In the above code, the first example is equivalent to

<%:Html.HelperA() %>

(the HTML escaped version of <%=Html.HelperA() %>) and the second to

<% Html.HelperB() %>

Client “Deep”-endancy

As such, I assumed ClientDependency would look something like this in Razor world:

@{
    Html.RequiresCss("Site.css", "Styles");
    Html.RequiresJs("Site.js", "Scripts");
}

@Html.RenderCssHere(new List<IClientDependencyPath>() {
    new BasicPath("Styles", "/Content/Css")
})

@Html.RenderJsHere(new List<IClientDependencyPath>() { 
    new BasicPath("Scripts", "/Scripts")
})

Sadly not. I got a compilation error: CS1061: 'System.Web.Mvc.HtmlHelper<object>' does not contain a definition for 'RequiresCss' and no extension method 'RequiresCss' accepting a first argument of type 'System.Web.Mvc.HtmlHelper<object>' could be found (are you missing a using directive or an assembly reference?)

Okay, so I need to include the ClientDependency helpers in the page namespace config. My Web.config (the one in the "Views" folder) turned out looking like this:

<configuration>
    <system.web.webpages.razor>
        <host factorytype="System.Web.Mvc.MvcWebRazorHostFactory, System.Web.Mvc, Version=3.0.0.0, Culture=neutral, PublicKeyToken=31BF3856AD364E35" />
        <pages pagebasetype="System.Web.Mvc.WebViewPage">
            <namespaces>
                <add namespace="System.Web.Mvc" />
                <add namespace="System.Web.Mvc.Ajax" />
                <add namespace="System.Web.Mvc.Html" />
                <add namespace="System.Web.Routing" />
                <add namespace="ClientDependency.Core" />
                <add namespace="ClientDependency.Core.Mvc" />
                <add namespace="MySite.Web.Views.Helpers" />
            </namespaces>
        </pages>
    </system.web.webpages.razor>
</configuration>

So I tried running it again, but the @Html.RenderCssHere() and @Html.RenderJsHere() methods had resulted in this

<!--[Css:Name="StandardRenderer"]//-->

<!--[Javascript:Name="StandardRenderer"]//-->

Yuck.

Razor automatically HTML escapes anything returned by a helper when called in the @Html.HelperA() style, unless that helper returns an HtmlString type.  Hence the Html.RenderCssHere and Html.RenderJsHere need to be wrapped by an HTML string.  Shannon provided this solution in a comment below:

@MvcHtmlString.Create(Html.RenderCssHere(new List<IClientDependencyPath>() {
    new BasicPath("Styles", "/Content/Css")
}))

@MvcHtmlString.Create(Html.RenderJsHere(new List<IClientDependencyPath>() { 
    new BasicPath("Scripts", "/Scripts")
}))

and voila! The best, of the best, of the best.

Tags: ,
Categories: .Net | ClientDependency

Client Dependency available via NuPack

by Aaron Powell 7. October 2010 05:10

Today Scott Guthrie announced a new project from Microsoft called NuPack which is for easily adding third-party libraries into any .NET project that you’re working on.

As soon as I saw the announcement I set about working on getting some of the open source projects into NuPack.

And I’m excited to say that you can now get ClientDependency directly from NuPack!

clientdependency-in-nupack

You can install either the Web Forms version or the MVC version, and it’ll even set up the basic config for you in your web.config.

Happy installing.

Categories: ClientDependency | .Net

Searching Multi-Node Tree Picker data (or any collection) with Examine

by Aaron Powell 22. September 2010 11:10

With the release of uComponents recently a lot of people are starting to work with a new data type called the MultiNodeTreePicker, and with this I’ve seen a few questions around searching the data it generates using Examine.

The problem is there is a catch, if you’re using the CSV storage (which you must if you’re working with Examine) you’ll hit a problem, the Examine index will have something like this:

1011,1231,1232,1225

But how do you search on that? Searching for ‘1231’ will not return anything, because it’s prefixed with ‘,’ and postfixed with ‘,’. So this brings a problem, how do you search?

Bring on Events

As Shannon spoke about at CodeGarden 10 Examine has a number of different events you can hook into to do different things (slides and code) and this is what we’re going to need to work with.

I’ve touched on events before but this time we’re going to look at a different event, we’re going to look at the GatheringNodeData event.

GatheringNodeData event

So this event in Examine is fired while Examine is scraping the data out of an XML element which it has received. This XML could be from Umbraco (in the scenario we’re looking at here) or it could be from your own data source, and the event is raised once Examine as turned the XML into a Key/ Value representation of it.

The event that raises has custom event arguments, which has a property called Fields. This Fields property is a dictionary which contains the full Key/ Value representation of the data which will end up in Examine!

Now this dictionary is able to be manipulated, so you can add/ remove data as you see if (but that’s a topic for another blog), it also means you can change the data!

Changing the data for our needs

As I mentioned at the start of this we end up with comma-separated string from the datatype which isn’t useful for searching, so we can use an event handler to change what we’ve got. First we need to tie an event handler

public class ExamineEvents : ApplicationBase 
{
	public ExamineEvents() 
	{
		var indexer = ExamineManager.Instance.IndexProviderCollection["MyIndexer"];
		indexer.GatheringNodeData += new EventHandler(GatheringNodeDataHandler);
	}

	void GatheringNodeDataHandler(object sender, IndexingNodeDataEventArgs e)
	{
		//do stuff here
	}
}

So this is just a simple wire-up, using the ApplicationBase class in Umbraco so that it’ll be created on application start-up. Next we need to implement the event handler:

void GatheringNodeDataHandler(object sender, IndexingNodeDataEventArgs e)
{
	//grab the current data from the Fields collection
	var mntp = e.Fields["TreePicker"];
	//let's get rid of those commas!
	mntp = mntp.Replace(",", " ");
	//now put it back into the Fields so we can pretend nothing happened!
	e.Fields["TreePicker"] = mntp;
}

And you’re done! Now the data will be written into the index with spaces rather than commas meaning that you can search on each ID without the need for wildcards or any other “hacks” to get it to work.

Note: This will work in the majority of cases, the only reason it’ll fail is if you’re using an analyzer that strips out numbers before indexing. For more information about Lucene analyzers take a look at this article: http://www.aaron-powell.com/lucene-analyzer

Adding embedded resource with ClientDependency

by Shannon Deminick 3. August 2010 05:32

The uComponents project for Umbraco is coming along rather nicely! So far there are 13 new data types created and a few extensions, hopefully soon we’ll have a package ready to go. In the meantime, I thought I’d share a nice little code snippet that we’re using this throughout uComponents that allows you to add embedded resources to ClientDependency. It’s pretty easy and works perfectly with Umbraco 4.5 or any other site you have running ClientDependency. This will ensure that embedded resources (JavaScript or CSS) are added to the ClientDependency combined scripts/styles and also compressed and cached.

First, I’ll show you how to register your embedded resources using our extension method class (for a Control object):

this.AddResourceToClientDependency(
    "DataTypesForUmbraco.Controls.Shared.Resources.PrevalueEditor.css", 
    ClientDependency.Core.ClientDependencyType.Css);

The above code assumes:

  • The class that you are consuming the code inherits from System.Web.UI.Control
  • That your embedded resource’s path is: DataTypesForUmbraco.Controls.Shared.Resources.PrevalueEditor.css
  • That your embedded resource is a CSS type

Pretty easy right!! Well, the only thing missing is that you’ll need to add our extension method class to your project which looks like this:

/// 
/// Extension methods for embedded resources
/// 
public static class ResourceExtensions
{

    /// 
    /// Adds an embedded resource to the ClientDependency output by name
    /// 
    /// 
    /// 
    /// 
    public static void AddResourceToClientDependency(this Control ctl, string resourceName, ClientDependencyType type)
    {
        //get the urls for the embedded resources           
        var resourceUrl = ctl.Page.ClientScript.GetWebResourceUrl(ctl.GetType(), resourceName);

        //add the resources to client dependency
        ClientDependencyLoader.Instance.RegisterDependency(resourceUrl, type);
    }

    /// 
    /// Adds an embedded resource to the ClientDependency output by name and priority
    /// 
    /// 
    /// 
    /// 
    public static void AddResourceToClientDependency(this Control ctl, string resourceName, ClientDependencyType type, int priority)
    {
        //get the urls for the embedded resources           
        var resourceUrl = ctl.Page.ClientScript.GetWebResourceUrl(ctl.GetType(), resourceName);

        //add the resources to client dependency
        ClientDependencyLoader.Instance.RegisterDependency(priority, resourceUrl, type);
    }

}

So basically, if you have a Control, you can register your embedded resources in ClientDependency with one line of code… sweeeeet.

Categories: .Net | ClientDependency

Umbraco 4.1 Benchmarks Part 1

by Shannon Deminick 15. April 2010 18:42

This is the first installment of what will hopefully be many Umbraco benchmark reports created by various members of the core team in the lead up to the launch of Umbraco 4.1. This benchmark report is about the request/response performance of the Umbraco back-office. This compares 4 different configurations: 4.0.3 with browser cache disabled (first run), 4.0.3 with browser cached files, 4.1 with browser cache disabled and 4.1 with browser cached files. These comparisons have been done by using newly installed Umbraco instances with ONLY the CWS package installed. The benchmark results were prepared by using Charles Proxy.

Test Stats 4.0.3 4.0.3
client cached
4.1 4.1
client cached
Content app Completed Requests 68 7 46 6
Response (KB) 687.05 72.48 431.41 32.54
Edit content
home page
Completed Requests 50 2 34 1
Response (KB) 385.10 47.28 343.36 12.07
Expand all
content nodes
Completed Requests 17 17 16 16
Response (KB) 18.47 18.47 13.96 10.85
TOTALS Completed Requests 135 26 96 23
Response (KB) 1063.62 138.23 788.73 55.46

Note: the above is based on <compilation debug=”false”> being set in the web.config. If it is set to true, the compression, combination and minification for both the ClientDependency framework and ScriptManager is not enabled. Also, this is not based on having IIS 7’s dynamic/static compression turned on, these benchmarks are based on Umbraco performing ‘as is ‘ out of the box which will be the same for IIS 6.

Though there’s only 3 tests listed above, these results will be consistent throughout all applications in the Umbraco back office in version 4.1.

The 4.1 difference:

  • In 4.0.3, all ScriptResource calls generated by ScriptManager were not being compressed or minified. This was due to a browser compatibility flag that was set in the base page (this was probably very old code from pre v3!).
  • Script managers in the back-office have the ScriptMode=”release” explicitly set (for minification of ScriptResource.axd)
  • The ClientDependency framework is shipped with 4.1 and all of the back office registers it’s JavaScript and CSS files with this framework. This allows for:
    • Combination, compression, minification of dependencies
    • Rogue script/style detection (for those scripts/styles that weren’t registered with the framework will still get compressed/minified)
    • Compression/minification of specified Mime types, in this case all JSON requests in the back office (namely the tree)
    • Compression/minification of all JavaScript web service proxy classes (‘asmx/js’ requests that are made by registering web services with the ScriptManager
  • Much of the back office client scripting in 4.1 has been completely refactored. Most of the JavaScript has been rewritten and a ton of file cleanup has been done.

Compared to 4.0.3, this is a HUGE difference with some serious performance benefits!

Categories: .Net | Umbraco | ClientDependency

ClientDependency now supporting MVC

by Shannon Deminick 6. April 2010 18:38

I’m please to announce that the ClientDependency framework now supports MVC! It’s very easy to implement using HtmlHelper extension methods. Here’s some quick examples:

Make a view dependent on a CSS file based on a path defined as “Styles”

<% Html.RequiresCss("Content.css", "Styles"); %>

Make a view dependent on jQuery using a full path declaration:

<% Html.RequiresJs("/Js/jquery-1.3.2.min.js"); %>

Rendering the Style blocks and defining a global style path:

<%= Html.RenderCssHere(new BasicPath("Styles", "/Css")) %>

Rendering the Script block (no global script path defined):

<%= Html.RenderJsHere() %>

There’s still a provider model for MVC but it uses a slightly different implementation from Web Forms. The same compositeFiles provider model is used but instead of the fileRegistration provider model that is used in Web Forms, a new mvc renderers provider model is used. A renderer provider is similar to the Web Forms fileRegistration providers but instead of registering the markup in the page using the page life cycle, a renderer provider is used to render out the html block necessary to embed in the page.

All of the functionality that existed in Web Forms exists in MVC. You can make as many views that you want dependent on as many of the same or different client files that you want and the system will still sort by position and priority and remove all duplicate registrations. Rogue scripts & styles still get processed by the composite file provider in MVC. Currently however, if you place user or composite controls on your views that have Client Dependencies tagged with either the control or attribute method used in Web Forms, these will not be registered with the view and output with the renderer. 

MVC pages have been added to the demo project as examples so have a look! You can download the source HERE

For full details and documentation go HERE