Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

In ASP.Net MVC, how do you create widgets that put javascript and css where they're supposed to go?

I am trying to create a reusable widget in ASP.Net MVC. This is a widget that concatenates 2 dates into a string. The widget contains 3 files: Widget.cshtml, Widget.css, and Widget.js and dependencies to several jquery libraries.

Widget.cshtml

<h2>Create a date range</h2>

<div>
    <label for="startDate">Start:</label>
    <input id="startDate" class="date" name="startDate" type="text" />
</div>

<div>
    <label for="endDate">End:</label>
    <input id="endDate" class="date" name="endDate" type="text" />
</div>

<p id="output"></p>

<button id="createDateRange">Create</button>

Widget.css

label { color: #555;}

Widget.js

$(function () {
    $(".date").datepicker();

    $("#createDateRange").click(function () {
        var start = $("#startDate").val();
        var end = $("#endDate").val();
        $("#output").html(start + " to " + end);
    });
});

I want to make this widget reusable in my MVC pages. I have come up with several options, including:

  1. Create an HTML Helper
  2. RenderPartial
  3. RenderAction
  4. Remove all css and javascript dependencies from the widget file. Manually Copy-And-Paste into every view that I'm using it in the correct place.

I was about to use a helper, when I remembered Steve Souders rules for web site performance:

Rule 5: Put stylesheets at the top

Rule 6: Put scripts at the bottom

Options 1), 2) and 3) all seem like good ideas, but they all write the code inline and violate the performance rules. Option 4) allows me to manually put the javascript and css files where they belong, but copy-and-paste is prone to mistakes. A potential idea would be write a scaffolder that puts everything where it belongs, but that seems too much work for right now.

What is the best (hopefully simple) way to do widgets and still follow performance rules 5 & 6?

like image 531
John Avatar asked Nov 16 '11 15:11

John


2 Answers

You could use RenderSection() which is similar to ContentPlaceHolder's of old and put them in your _Layout.cshtml

   <head>
    <title>@ViewBag.Title</title>
    <link href="@Url.Content("~/Content/Site.css")" rel="stylesheet" type="text/css" />
    <script src="@Url.Content("~/Scripts/jquery-1.4.4.min.js")" type="text/javascript"></script>
   @RenderSection("head");
</head>

Then in your View put:

@{
    ViewBag.Title = "Home Page";
}
@section head
{
<link href="widget-style.css" type="text/css" rel="stylesheet" /><
}

<h2>@ViewBag.Message</h2>
<p>
    To learn more about ASP.NET MVC visit <a href="http://asp.net/mvc" title="ASP.NET MVC Website">http://asp.net/mvc</a>.
</p>

@section footer{
 <script type="text/javascript" src="wiget.js"></script>  
}

It's a simple alternative if you don't want to use Combress or Chirpy, you'll still have to add the code into the pages you use the control on, but it'll appear in the right places.

I also found a handy blog post on registering scripts from within a partial view that might be worth a read.

like image 132
danswain Avatar answered Nov 15 '22 07:11

danswain


  1. Change your widget to use unobtrusive javascript techniques so that the entire DOM can be scanned for instances of your widget. Rely on classes instead of IDs.
  2. Use a tool like Chirpy to package all your CSS files and Javascript files into a single library file (one .js and one .css) that you can append at the top and bottom (respectively) of your Master Page/Layout.
  3. Use an HTML Helper to produce your widget HTML. If you like, the HTML helper can render a partial view to keep the HTML view code in a view file. Because you're using unobtrusive javascript, there is no need to produce any script inline. Just let the widget's javascript code scan the DOM for instances of your widget.

Step 1

The original example already uses some unobtrusive javascript techniques, but here's how you could extend that pattern even further, as demonstrated in this jsfiddle.

Widget.cshtml

<div class="date-range-widget">
    <h2>Create a date range</h2>
    
    <div>
        <label for="startDate">Start:</label>
        <input id="startDate" class="date start-date" name="startDate" type="text" />
    </div>
    
    <div>
        <label for="endDate">End:</label>
        <input id="endDate" class="date end-date" name="endDate" type="text" />
    </div>
    
    <p class="output"></p>
    
    <button class="create-date-range">Create</button>
</div>

Widget.js

$(function () {
    // This line could probably be done generally, regardless of whether you're
    // in the widget.
    $("input.date").datepicker();

    $("div.date-range-widget").each(function(){
        var $t = $(this);
        var startBox = $t.find("input.start-date");
        var endBox = $t.find("input.end-date");
        var output = $t.find("p.output");
        $t.find("button.create-date-range").click(function () {
            output.html(startBox.val() + " to " + endBox.val());
        });
    });    
});

Note how you can now have multiple instances of this widget present on your page, with different id and name values, and they would function independently of one another.

When no data-range-widget is present, the javascript will still search the DOM, fail to find any, and move on. However, the cost of this operation is usually pretty lightweight compared to making a separate round-trip for a different javascript file. Keeping the CSS and javascript in separate, static files makes it so the browser can cache those files, which means it's pulling less data across the wire on average (since only the HTML changes from one request to the next).

Step 2

I've personally found it worthwhile to combine my javascript and CSS into one file, so the browser only has to pull one javascript and one css file in for a given page load (and both will be cached after the first page load). If you have a very large javascript file that is only used on certain pages, you may want to keep it separate and only load it on certain pages.

Regardless, your javascript should be written in such a way that you could have all the javascript files on your site loaded at once without stepping on each others' toes.

Step 3

Should be pretty self-explanatory.

like image 40
StriplingWarrior Avatar answered Nov 15 '22 06:11

StriplingWarrior