Tuesday, April 19, 2011

Using RazorJS with MVC3

I've been working on a project where I've managed to accumulate 1200+ lines of javascript in my View file. There are two reasons for this:
  1. Since the application is still in development I don't have to worry about the browser caching JS files if the code is in the View.
  2. I often have Url.Action and other snippets of Razor mixed in with my javascript. 
I was just getting to the point where I was ready to move this code to its own .js file but I was worried about how to deal with the Razor that was mixed in. Thanks to John Katsiotis's (aka djsolid) RazorJS project I now have a solution!

Installation is easy, just use Nuget to add RazorJS to your solution in Visual Studio. The command line to do is:

Install-Package RazorJS

By default @Url.Action will not work and you'll get the error:
error CS0103: The name 'Url' does not exist in the current context

To get it to work add the following at the top of your javascript file.
@{
    var Url = new System.Web.Mvc.UrlHelper(System.Web.HttpContext.Current.Request.RequestContext);
}

If you run into other errors you should be able to use similar workarounds. If you do use other workarounds, let me know and I can add them to this post.

UPDATE: In version 0.4.2 you no longer need this work around for the UrlHelper. Thanks to John for adding this so fast!

Wednesday, April 6, 2011

Sort Table Rows By Drag and Drop Using jQuery

jQuery UI allows you to make a list of elements sortable, but if you want to use it to sort table rows you have to do a few workarounds. This is the code I started with.

$(document).ready(function () {

    //make table rows sortable
    $('#myTable tbody').sortable({    
        helper: function (e, ui) {
            ui.children().each(function () {
                $(this).width($(this).width());
            });
            return ui;
        },
        scroll: true,
        stop: function (event, ui) {
            //SAVE YOUR SORT ORDER                    
        }
    }).disableSelection();

});



The helper function (courtesy of The Foliotek Dev Blog) fixes the width of the row which collapses as soon as you remove it from the table.

This should work for most scenarios, but I've found a bug in Firefox where the row will jump up and float above the cursor when there are scrollbars present on the web page. It seems to be a position issue caused by parent elements of the table. I still haven't figured out exactly what the magic combination is that causes this but I can replicate it with HTML5 Boilerplate when using the default style sheet and table below.

I've replicated the bug on jsFiddle here:
http://jsfiddle.net/cdeutsch/2Yxw2/

<div id="container">
<header>

</header>
<div id="main" role="main">
    <div style="position: relative;">
    <table id="myTable">
        <tbody>
        <tr>
            <td>1</td>
            <td>Blah Blah Blah Blah</td>
        </tr>
        <tr>
            <td>2</td>
            <td>2222 22222 22222 2222 22222</td>
        </tr>
        <tr>
            <td>3</td>
            <td>Test Test Test Test Test</td>
        </tr>
        <tr>
            <td>4</td>
            <td>4444 4444 4444 4444 4444</td>
        </tr>
        <tr>
            <td>5</td>
            <td>Hi Hi Hi Hi Hi Hi</td>
        </tr>
        <tr>
            <td>6</td>
            <td>Bye Bye Bye Bye Bye Bye Bye</td>
        </tr>
        </tbody>
    </table>
    </div>
</div>
<footer>

</footer>
</div> <!--! end of #container -->


If this happens use the code below (inspired by this Stackoverflow question with some tweaks) to fix the issue.

Try it in jsFiddle here:
http://jsfiddle.net/cdeutsch/WysJL/

$(document).ready(function () {
    //make table rows sortable
    $('#myTable tbody').sortable({
        start: function (event, ui) {
            //fix firefox position issue when dragging.
            if (navigator.userAgent.toLowerCase().match(/firefox/) && ui.helper !== undefined) {
                ui.helper.css('position', 'absolute').css('margin-top', $(window).scrollTop());
                //wire up event that changes the margin whenever the window scrolls.
                $(window).bind('scroll.sortableplaylist', function () {
                    ui.helper.css('position', 'absolute').css('margin-top', $(window).scrollTop());
                });
            }
        },
        beforeStop: function (event, ui) {
            //undo the firefox fix.
            if (navigator.userAgent.toLowerCase().match(/firefox/) && ui.offset !== undefined) {
                $(window).unbind('scroll.sortableplaylist');
                ui.helper.css('margin-top', 0);
            }
        },
        helper: function (e, ui) {
            ui.children().each(function () {
                $(this).width($(this).width());
            });
            return ui;
        },
        scroll: true,
        stop: function (event, ui) {
            //SAVE YOUR SORT ORDER                    
        }
    }).disableSelection();
});


Hope this helps someone. Took me about 1/2 a day to fix the Firefox issue.