ASP.NET MVC Cascading drop down lists

Found this great post on how to create cascading drop downs in ASP.NET MVC.

I created a page for looking up shipping rates by carrier, weight and zip code. For my particular usage, I had 2 lists: Carrier and ShipMethod. I wanted the cascading list to be disabled when the initial list selection had yet to be made. I created functions to populate the target list as well as enable/disable the target list. I made the js into a reusable Razor helper which should allow me to use it anywhere I need this functionality.

Here is the helper code:

@helper DynamicDropDowns() {
<script type="text/javascript">
    function listChanged($list, $target, url) {
        var listId = $list.val();
        if (listId == "") { // User selected first option, 
                            // so clear and disable the list
            $target.empty();
            enableList($target, false);
            return;
        }
        $.getJSON(url, { id: listId }, function (data) {
            $target.empty(); // Clear the list
            $.each(data, function (idx, item) { // Add the data
                $target.append($("<option/>",
                {
                    value: item.Value,
                    text: item.Text
                }));
            });
            enableList($target, true); // Enable the list
        });
    }

    function enableList($list, enabled) {
        $list.attr("disabled", enabled ? null : "disabled");
    }
</script>
}

Gist

And here it is in action:

<script type="text/javascript">
    $(function () {
        var $carrier = $("#Carrier");
        var $ship = $("#ShipMethod");
        enableList($ship, false);
        $carrier.change(function () {
            listChanged($(this), $ship, "/Tools/GetShipMethodsByCarrier");
        });
    });
</script>

CSS Demo

Someone asked me to show them how to do something via CSS, and this is the example I came up with. Thought I’d post it on here for posterity:

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" >
<head>
    <title>Untitled Page</title>
    <style type="text/css">
        /* Layout */
        body { margin: 0; padding: 0; }
        #container { position: relative; top: 0; left: 0; width: 100%; height: 100%; }
        /* The header and search windows are just normal divs */
        #header { height: 40px; margin: 0; padding: 10px; }
        #search-box { height: 20px; padding: 5px 10px; }
        /* Content "top" is total height of header and search-box heights (including padding) */
        #content { position: fixed; left: 0; right: 0; top: 90px; bottom: 60px; }
        /* These elements are contained within the content div, so we can "absolutely" position them within the container
        If an element is within a positioned element, "absolute" postion is actually relative to the container!?! */
        #nav { position: absolute; top: 0; left: 0; bottom: 0; width: 200px; }
        /* Here we position the nav menus relative to the container */
        #nav-1, #nav-2 { position: relative; left: 0; height: 50%; overflow: auto; width: 180px; padding: 0 10px; z-index: 9999; }
        /* The content div is also absolutely position inside. The overflow allows us to have the nice scrollbar effect */
        #main { position: absolute; top: 0; left: 200px; right: 0; bottom: 0;  overflow: auto; padding: 10px; }
        /* The footer is absolutely position on the page at the bottom of the window */
        #footer { position: fixed; left: 0; right: 0; bottom: 0; height: 40px; margin: 0; padding: 10px; }
        /* Design */
        #header, #nav, #footer { background: #666; color: #FFF;  }
        #search-box { background: #CCC; }
        #search, #search-results { display: none; }
        #header h1 { margin: 0; padding: 0; }
        #search-box p { margin: 0 0 5px; }
        #main h2 { margin-top: 0; }
        #footer p { text-align: center; }
        /* BlockUI */
        div.blockMsg { background: #FFF; border: 3px solid #CCC; padding: 10px; }
    </style>
</head>
<body>
    <div id="container">
        <div id="header">
            <h1>Hello World!</h1>
        </div>
        <div id="search-box">
            <p><a href="#">Search</a></p>
            <p id="search"><input type="text" id="search-val" name="search-val" value="Search" />
                <input type="submit" id="search-submit" name="search-submit" value="Submit" /></p>
        </div>
        <div id="content">
            <div id="nav">
                <div id="nav-1">
                    <h3>Top Nav</h3>
                    <ul>
                        <li><a href="#">Nav Item</a></li>
                        <li><a href="#">Nav Item</a></li>
                        <li><a href="#">Nav Item</a></li>
                    </ul>
                </div>
                <div id="nav-2">
                    <h3>Bottom Nav</h3>
                    <ul>
                        <li><a href="#">Nav Item</a></li>
                        <li><a href="#">Nav Item</a></li>
                        <li><a href="#">Nav Item</a></li>
                    </ul>
                </div>
            </div>
            <div id="main">
                <h2>Welcome!</h2>
                <p>Quisque lobortis arcu ut augue molestie dignissim. Sed ipsum leo, tristique ut placerat ut, gravida sagittis est. Ut pharetra adipiscing sem, ac viverra arcu tristique at. Aliquam sed massa eu sem porta consectetur. Donec convallis, sem vitae laoreet faucibus, sapien nulla lacinia mi, ac tincidunt lectus quam nec purus. Sed tristique sapien non leo adipiscing eu porta justo tincidunt. Quisque quis vestibulum quam. Etiam ligula sapien, pharetra quis mollis in, faucibus non elit. Etiam fermentum tempor nunc et auctor. Morbi id leo tempor justo placerat accumsan at sit amet odio. Nunc tincidunt molestie aliquam. Phasellus ligula libero, auctor id tempus id, mollis a mauris. Sed eget sodales leo. Sed eget sem id nibh tincidunt semper non a arcu. Proin vel purus in diam congue tincidunt. Nam vitae augue lorem. Nam nec massa id turpis egestas tempor nec in neque. Nam pulvinar odio sagittis lectus feugiat hendrerit. In a erat lectus. Nulla ut purus sapien.</p>
                <p>Curabitur sollicitudin, dolor non sodales tempus, justo erat volutpat tortor, ac mollis dui urna id massa. Aliquam lorem metus, commodo sit amet sodales sit amet, accumsan ut felis. Sed felis nisi, pellentesque a adipiscing sit amet, sagittis a justo. Proin rutrum nunc sit amet eros bibendum sed rhoncus ligula sagittis. Maecenas consectetur, sapien a varius dictum, elit dui molestie erat, eu aliquam felis massa nec orci. Maecenas nec dui arcu, in elementum elit. Suspendisse in enim id tellus feugiat porttitor at et tellus. Class aptent taciti sociosqu ad litora torquent per conubia nostra, per inceptos himenaeos. Cum sociis natoque penatibus et magnis dis parturient montes, nascetur ridiculus mus. Phasellus id ipsum lorem, vel ullamcorper mi. Curabitur id odio eu quam faucibus commodo sed ac eros. Cras vel nulla in tellus cursus pellentesque. Donec magna purus, viverra non tempor ac, convallis et erat. Mauris ultricies nisl vel elit pretium et eleifend ligula commodo. Integer quam nulla, aliquam id malesuada eu, semper in mauris.</p>
                <p>Quisque sollicitudin suscipit sapien, in rhoncus sapien gravida eget. Duis at fermentum metus. Donec dui lacus, fringilla ut bibendum eu, laoreet vitae nunc. Vestibulum ante ipsum primis in faucibus orci luctus et ultrices posuere cubilia Curae; In a quam mi, eget faucibus nulla. Mauris viverra consectetur ante nec mattis. Nunc porta mi et purus convallis dignissim luctus urna ultricies. Vivamus metus neque, malesuada laoreet imperdiet non, tincidunt lacinia massa. Ut at lorem et dolor accumsan lobortis quis eu mi. Aliquam pulvinar gravida hendrerit. Duis ac ipsum neque. Phasellus vel mi sed justo feugiat auctor. Phasellus porta, turpis ornare rutrum porta, orci tortor tincidunt dolor, id volutpat justo erat non est. Phasellus dapibus tristique feugiat. Curabitur fermentum vehicula mauris ac accumsan. Sed hendrerit est vel turpis blandit sed sollicitudin quam accumsan.</p>
                <p>Fusce pharetra, eros id pharetra dapibus, lorem nunc facilisis libero, in tristique est ante et lacus. Donec eu nisi in augue blandit euismod. Maecenas tristique dapibus dolor, ut placerat nisi vestibulum id. Nulla feugiat, purus vitae auctor cursus, tellus lorem accumsan risus, id accumsan ante nulla id nisl. Proin ut lectus mollis tortor dictum venenatis. Vestibulum quis enim in purus condimentum viverra. Sed consectetur ullamcorper leo, vitae hendrerit sem varius et. Etiam consectetur metus vel dolor posuere eget porttitor metus facilisis. In bibendum, ipsum nec fermentum hendrerit, mi sem consectetur ligula, et luctus purus velit in sapien. Proin eget nibh id magna porttitor accumsan. Morbi scelerisque elementum massa et malesuada. Sed consequat tempor urna blandit egestas. Maecenas bibendum dui ac enim rutrum iaculis. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Etiam tempor mattis tellus, ac luctus dui mattis non. Pellentesque imperdiet facilisis orci, consectetur dictum velit eleifend in. Vestibulum ante ipsum primis in faucibus orci luctus et ultrices posuere cubilia Curae; Curabitur pellentesque ullamcorper est. Curabitur vel ipsum sit amet mi porta gravida. Sed ut libero neque, rhoncus eleifend diam.</p>
                <p>Donec rutrum, sapien sed laoreet volutpat, quam nisi scelerisque odio, a posuere sem nisl ac justo. Etiam a venenatis elit. Ut mattis, odio aliquam pellentesque fermentum, dui metus interdum dui, a ultricies dolor dui id sapien. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Ut in arcu a velit volutpat sollicitudin. Nulla pharetra diam at nisi varius rutrum. Pellentesque habitant morbi tristique senectus et netus et malesuada fames ac turpis egestas. Cras posuere dignissim velit vel scelerisque. Ut vel nibh nulla. Maecenas eleifend, orci sed cursus tincidunt, diam lectus vestibulum libero, id tincidunt enim mauris quis metus. Praesent ullamcorper accumsan felis, et molestie ligula egestas eget. Morbi auctor placerat sodales.</p>
                <p>Aenean euismod commodo sem in ultricies. Sed sed eros sem. Nunc a blandit ipsum. Vestibulum ante urna, vulputate sed pretium eu, posuere a massa. Nunc pellentesque venenatis odio, ac rhoncus odio sollicitudin sit amet. Praesent dui felis, euismod eget tempor vel, mattis et ligula. Vivamus adipiscing pellentesque luctus. Curabitur vitae iaculis nulla. Cras laoreet, urna tincidunt ornare fringilla, odio ante varius mi, ut imperdiet lectus quam vitae magna. Nam sagittis diam sit amet velit tristique lobortis. In hac habitasse platea dictumst.</p>
                <p>Duis dapibus molestie neque sed sollicitudin. Duis lorem lectus, gravida ut accumsan sit amet, aliquet sed libero. Aliquam at nisi dolor. Cras vulputate ipsum ut mauris ullamcorper sed elementum neque tristique. Nam enim leo, gravida id sodales id, euismod in lacus. Quisque justo lacus, facilisis dapibus mollis in, rutrum in est. Nullam sit amet nulla ante, non congue sem. Duis id suscipit purus. Nunc pulvinar, mauris sed aliquet aliquet, dolor ipsum suscipit massa, vel sodales mi mi eget ante. Nunc convallis hendrerit luctus. Aliquam accumsan lorem et velit gravida mattis. Donec sed sapien ac magna molestie condimentum. Suspendisse sed orci ac erat dictum laoreet sed vitae sem. Pellentesque quis mi leo. Curabitur pulvinar faucibus dapibus. Praesent id turpis id odio laoreet sollicitudin id non nisi.</p>
                <p>Suspendisse malesuada aliquam mattis. Vestibulum iaculis venenatis lectus, nec aliquam nunc ultrices id. Nullam quis nulla in ante rhoncus rhoncus vitae eget odio. Nunc molestie, nunc eu lobortis consectetur, odio mi cursus turpis, sit amet pulvinar arcu leo at diam. Mauris sit amet nibh elementum purus semper venenatis ac et neque. Etiam porta massa felis. Aliquam a urna vel massa facilisis dapibus. Nunc elit velit, ultricies non faucibus vel, cursus dapibus dui. Suspendisse potenti. Duis pretium est elementum leo eleifend tincidunt. Ut facilisis tincidunt orci, vel placerat massa porttitor id. Proin sit amet risus sed eros gravida varius non non elit.</p>
                <p>Quisque mattis lobortis auctor. Ut at accumsan libero. Sed tincidunt, erat sed iaculis mollis, libero nulla cursus nunc, quis pellentesque ipsum nibh ac nisi. Nulla facilisi. Vestibulum eget urna et nulla lobortis pulvinar in non ipsum. Praesent interdum lectus quis ligula consectetur vel placerat neque rhoncus. Suspendisse ac erat sed leo eleifend tempus. Donec mi enim, venenatis vitae sodales id, malesuada eget lacus. Nullam tincidunt pulvinar lectus, vitae vehicula purus sollicitudin semper. Etiam et erat mi, egestas egestas dolor. Morbi nisl ligula, dapibus et tristique ut, faucibus vel mauris. Curabitur vel ante dolor.</p>
                <p>In a est orci, sit amet suscipit lectus. Ut luctus interdum enim, eu sodales libero malesuada non. Donec sollicitudin risus sit amet mauris pharetra a ultricies neque blandit. Praesent vehicula lobortis vulputate. Etiam at lectus et est viverra facilisis ut id odio. Nulla augue nulla, vehicula vitae sollicitudin at, consectetur ut magna. Integer ac nibh et metus facilisis fringilla. Proin rutrum purus eget risus hendrerit auctor. Nullam mi felis, ullamcorper sed pellentesque a, suscipit quis elit. Morbi laoreet aliquam nisi, sollicitudin molestie ante sollicitudin quis. Nullam et venenatis neque. Donec non hendrerit tortor. Donec commodo magna at arcu luctus nec cursus nulla interdum. Aenean placerat scelerisque ligula, feugiat vestibulum mauris fermentum nec. Phasellus libero dolor, rutrum vitae lacinia quis, pretium non ipsum. Ut eget dolor libero, sed vehicula nisi. Nunc porta nisl non urna congue sit amet malesuada mauris tempor. Sed condimentum lacinia purus at porttitor. Maecenas accumsan lobortis ante at ullamcorper. Mauris augue nisl, tristique eu consequat a, dignissim ac augue.</p>
                <p>Duis adipiscing metus velit, quis dapibus diam. Morbi sollicitudin placerat vulputate. Ut ut felis nibh, quis tincidunt massa. Donec eros nisl, scelerisque nec ultricies sed, vulputate a turpis. Integer leo massa, condimentum sed luctus nec, tempor in tellus. Praesent nec leo quam. Sed dignissim imperdiet mollis. In vulputate lorem at magna molestie posuere. Sed ut eros at urna scelerisque tempor. Praesent vel urna purus. Vivamus sed tortor sit amet neque adipiscing porta. Praesent ac egestas tellus. Mauris leo erat, tincidunt eu tincidunt vitae, dictum in purus. Aenean semper, erat quis dapibus ornare, massa dolor ornare diam, a ultricies nunc augue in ligula.</p>
                <p>Nulla aliquam velit nec arcu commodo egestas sed in diam. Quisque tincidunt tincidunt odio, vitae pellentesque massa placerat eget. Donec at neque nulla. Ut eget erat quam. Mauris eu pellentesque dolor. Nam facilisis justo a risus elementum interdum. Pellentesque ligula eros, posuere sed hendrerit vitae, lacinia a eros. Sed nec purus lacus. Pellentesque habitant morbi tristique senectus et netus et malesuada fames ac turpis egestas. Class aptent taciti sociosqu ad litora torquent per conubia nostra, per inceptos himenaeos. Phasellus non sapien ut erat volutpat viverra vitae at est. Morbi vel pellentesque nibh.</p>
                <p>Pellentesque vel magna at augue ullamcorper pretium. Sed lacinia hendrerit vestibulum. Vestibulum rutrum porta placerat. Fusce vitae felis sit amet justo aliquam iaculis in nec dolor. Donec ac ligula neque, sed tristique lorem. In viverra feugiat magna, at hendrerit quam lacinia in. Fusce id dui ligula, ac convallis diam. Fusce tempus, arcu id varius rutrum, metus velit ullamcorper massa, nec posuere nisl eros dictum nulla. Donec eget tincidunt nisi. Nam quam nunc, dapibus quis luctus nec, semper ut nulla. In metus sapien, dictum rhoncus tincidunt eu, lobortis non massa. Maecenas a metus eu dolor dapibus convallis eget pretium tortor. Donec lobortis semper bibendum.</p>
                <p>Suspendisse euismod mi non enim posuere venenatis. Quisque sed felis nunc. In sem elit, vestibulum ac euismod vel, condimentum id lectus. Cum sociis natoque penatibus et magnis dis parturient montes, nascetur ridiculus mus. Ut dignissim faucibus varius. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Aenean pulvinar dignissim nulla, vitae rutrum dui cursus dapibus. Maecenas sem arcu, fringilla quis iaculis eget, consequat ac nunc. Vivamus id ligula orci, in interdum lectus. Vestibulum tincidunt lacinia mauris, eget venenatis erat hendrerit sed. Pellentesque non dolor risus. In hac habitasse platea dictumst. Etiam at mi justo. Aenean quis odio sed erat mattis rhoncus.</p>
                <p>Aliquam erat volutpat. Aliquam lacinia nisl vel orci egestas id pellentesque purus lacinia. Sed ultricies condimentum mi, sed laoreet purus iaculis nec. Donec et ipsum velit. Etiam tincidunt aliquam tellus sed ornare. Mauris cursus varius odio luctus blandit. Sed vel risus leo. Vivamus fringilla interdum varius. Suspendisse et tortor mi. Fusce ut metus nibh, et fermentum massa. Quisque a enim ac urna interdum rhoncus vel vel nulla. Quisque dictum mauris sit amet mi euismod a semper leo auctor. Etiam faucibus convallis quam, id bibendum dolor ornare vitae. Praesent sed purus non ligula adipiscing condimentum ut non leo. Pellentesque habitant morbi tristique senectus et netus et malesuada fames ac turpis egestas.</p>
            </div>
        </div>
        <div id="footer">
            <p><em>© Footer!</em></p>
        </div>
    </div>
    <div id="search-results">
        <h2>Search Results</h2>
        <p>You searched for <em id="search-term"></em></p>
        <p>In a est orci, sit amet suscipit lectus. Ut luctus interdum enim, eu sodales libero malesuada non. Donec sollicitudin risus sit amet mauris pharetra a ultricies neque blandit. Praesent vehicula lobortis vulputate. Etiam at lectus et est viverra facilisis ut id odio. Nulla augue nulla, vehicula vitae sollicitudin at, consectetur ut magna. Integer ac nibh et metus facilisis fringilla. Proin rutrum purus eget risus hendrerit auctor. Nullam mi felis, ullamcorper sed pellentesque a, suscipit quis elit. Morbi laoreet aliquam nisi, sollicitudin molestie ante sollicitudin quis. Nullam et venenatis neque. Donec non hendrerit tortor. Donec commodo magna at arcu luctus nec cursus nulla interdum. Aenean placerat scelerisque ligula, feugiat vestibulum mauris fermentum nec. Phasellus libero dolor, rutrum vitae lacinia quis, pretium non ipsum. Ut eget dolor libero, sed vehicula nisi. Nunc porta nisl non urna congue sit amet malesuada mauris tempor. Sed condimentum lacinia purus at porttitor. Maecenas accumsan lobortis ante at ullamcorper. Mauris augue nisl, tristique eu consequat a, dignissim ac augue.</p>
        <p>Duis adipiscing metus velit, quis dapibus diam. Morbi sollicitudin placerat vulputate. Ut ut felis nibh, quis tincidunt massa. Donec eros nisl, scelerisque nec ultricies sed, vulputate a turpis. Integer leo massa, condimentum sed luctus nec, tempor in tellus. Praesent nec leo quam. Sed dignissim imperdiet mollis. In vulputate lorem at magna molestie posuere. Sed ut eros at urna scelerisque tempor. Praesent vel urna purus. Vivamus sed tortor sit amet neque adipiscing porta. Praesent ac egestas tellus. Mauris leo erat, tincidunt eu tincidunt vitae, dictum in purus. Aenean semper, erat quis dapibus ornare, massa dolor ornare diam, a ultricies nunc augue in ligula.</p>
        <p>Nulla aliquam velit nec arcu commodo egestas sed in diam. Quisque tincidunt tincidunt odio, vitae pellentesque massa placerat eget. Donec at neque nulla. Ut eget erat quam. Mauris eu pellentesque dolor. Nam facilisis justo a risus elementum interdum. Pellentesque ligula eros, posuere sed hendrerit vitae, lacinia a eros. Sed nec purus lacus. Pellentesque habitant morbi tristique senectus et netus et malesuada fames ac turpis egestas. Class aptent taciti sociosqu ad litora torquent per conubia nostra, per inceptos himenaeos. Phasellus non sapien ut erat volutpat viverra vitae at est. Morbi vel pellentesque nibh.</p>
    </div>
    <script type="text/javascript" src="http://ajax.googleapis.com/ajax/libs/jquery/1.4.2/jquery.min.js"></script>
    <script type="text/javascript" src="http://github.com/malsup/blockui/raw/master/jquery.blockUI.js?v2.31"></script>
    <script type="text/javascript" language="javascript">
        $(document).ready(function () {
            $("#search-box a").toggle(function (e) {
                e.preventDefault();
                $("#search-box").css({ "height": "50px" });
                $("#content").css({ "top": "120px" });
                $("#search").css({ "display": "block" });
            },
            function (e) {
                e.preventDefault();
                $("#search-box").css({ "height": "20px" });
                $("#content").css({ "top": "90px" });
                $("#search").css({ "display": "none" });
            });
            $("#search-val").focus(function () {
                $(this).select();
            });
            $("#search-submit").click(function () {
                var searchval = $("#search-val").val();
                $.blockUI({ message: "<h3>Searching for '<em>" + searchval + "</em>'...</h3>" });
                setTimeout("searcher('" + searchval + "')", 2000);
            });
        });
        function searcher(searchval) {
            $.unblockUI();
            $("#search-term").html(searchval);
            $("#main").html($("#search-results").html());
        }
    </script>
</body>
</html>

Demo here

My first foray into ASP.NET MVC

I have recently begun the .NET MVC journey, and I see recurring “buzzwords”: Inversion of Control (IoC) and Dependency Injection (DI). If you are at the same point in your MVC journey as me, you may be wondering what the heck these things are?!?! They have certainly caused me a bit of confusion. I have yet to implement DI, but I have decided to stick with the MS stack and try out Unity.

I decided I’d give this whole thing another shot tonight. I recently built my first little MVC application – a guestbook. Remember those from 1996? I thought it would be something a bit different and I knew it would be pretty simple (the database is two tables, 1 of which I am really not using right now). I also brought it into the 21st century with a little Google mappage to show where the guestbook signer is located on our big, blue marble. I’ve got it mostly working and I’ll get it up online as soon as I get the membership stuff locked down.

I want to “do it the right way”, so I want to try out the DI stuff. I have been searching for a good starter post that would break down the whole IoC/DI stuff into small, digestible chunks that my overworked brain could handle. I finally found .NET HITMAN’s post that breaks down the history behind Unity, why DI is a good thing, and how to add references to the Unity DLL’s to your project, which is exactly what I was in need of. Since this is an ASP.NET MVC app, I also found Shiju Varghese’s post that explains how to add DI to the NerdDinner project. Since I “followed along” with the NerdDinner app while creating my Guestbook app, I found the post extremely helpful and easy to follow.

When I started off with my MVC project, I went with SubSonic 3.0 for data access. I am a big fan of SubSonic and I use 2.1 at work, so I was really interested to see what 3.0 had to offer and I knew that with Rob’s focus on MVC that 3.0 would work quite nicely. It also gave me a chance to use some LINQ goodness, which, to this point, I haven’t been able to use extensively. I was quite impressed with the T4 LinqTemplates and overall I think 3.0 is a great new addition to the SubSonic family.

Another thing I keep seeing in MVC apps is repositories. It is my understanding that the repository enables easier unit testing (but I am an .NET MVC n00b, so I could be wrong) since the controller isn’t talking directly to your data access code. So I went about creating a GuestbookRepository class for my controller to talk to.

Now that I am going the whole DI route, it seems I need to first create an interface, then inherit from that interface. So here is the IGuestbookRepository interface:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

using MvcApplication.Models;

namespace MvcApplication.Repositories {

    public interface IGuestbookRepository {

        IQueryable<mvc_guestbook> GetAllMessages();
        mvc_Guestbook GetMessage(int id);
        void Add(mvc_Guestbook guestbook);
        void Delete(mvc_Guestbook guestbook);
        void Save(mvc_Guestbook guestbook);
        void Update(mvc_Guestbook guestbook);
    }
}

* NOTE: I don’t really like the mvc_Guestbook references in there (they seem smelly) but I am not sure what I should put there, so this is going to have to do for now until I determine if this is correct or find out what is correct.

Now that I have my interface, I modifed my GuestbookRepository class to inherit from the interface. Here is the code:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;

using MvcApplication.Models;

namespace MvcApplication.Repositories {

    public class GuestbookRepository : IGuestbookRepository {

        private GuestbookDB db = new GuestbookDB();

        public IQueryable<mvc_guestbook> GetAllMessages() {
            return from guestbook in db.mvc_Guestbooks
                   orderby guestbook.MessageDate descending
                   select guestbook;
        }

        public mvc_Guestbook GetMessage(int id) {
            return db.mvc_Guestbooks.SingleOrDefault(g => g.GuestbookID == id);
        }

        public void Add(mvc_Guestbook guestbook) {
            guestbook.Add();
        }

        public void Delete(mvc_Guestbook guestbook) {
            guestbook.Delete();
        }

        public void Save(mvc_Guestbook guestbook) {
            guestbook.Save();
        }

        public void Update(mvc_Guestbook guestbook) {
            // Save message history
            SaveHistory(guestbook.GuestbookID);
            // Save updated message
            guestbook.Update();
        }

        private void SaveHistory(int id) {
            mvc_Guestbook past = mvc_Guestbook.SingleOrDefault(g => g.GuestbookID == id);
            mvc_GuestbookHistory history = new mvc_GuestbookHistory();
            history.GuestbookID = past.GuestbookID;
            history.Name = past.Name;
            history.Email = past.Email;
            history.Message = past.Message;
            history.Location = past.Location;
            history.Latitude = past.Latitude;
            history.Longitude = past.Longitude;
            history.EditDate = DateTime.Now;
            history.Add();
        }
    }
}

We’ve got all our basics: Get all, get one, create, delete. There are also methods for adding and saving. It seems SubSonic uses .Save() as either add or update, while .Add() and .Update() only work as, well, add or update. Also of note in the Update method is the call to SaveHistory. This is meant to show an update history of the messages, though I am not currently sure if I am going to enable editing. Better to have this in now and not need it, I guess.

Finally, we have the GuestbookController which uses the dependency injection I just set up. Here is the code:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.Mvc;
using System.Web.Mvc.Ajax;

using MvcApplication.Models;
using MvcApplication.Repositories;

namespace MvcApplication.Controllers {

    public class GuestbookController : Controller {

        private IGuestbookRepository gr;

        public GuestbookController(IGuestbookRepository repository) {
            gr = repository;
        }

        public ActionResult Index(int? page) {
            const int pageSize = 5;
            var guestbook = gr.GetAllMessages();
            var pagedGuestbook = new PaginatedList<mvc_guestbook>(guestbook,
                page ?? 0,
                pageSize);
            return View(pagedGuestbook);
        }

        public ActionResult AjaxIndex(int? page) {
            const int pageSize = 5;
            var guestbook = gr.GetAllMessages();
            var pagedGuestbook = new PaginatedList<mvc_guestbook>(guestbook,
                page ?? 0,
                pageSize);
            return PartialView("Messages", pagedGuestbook);
        }

        [AcceptVerbs(HttpVerbs.Get)]
        public ActionResult Create() {
            if (Session[Request.ServerVariables["REMOTE_ADDR"].ToString()] != null ? DateTime.Now.Subtract(Convert.ToDateTime(Session[Request.ServerVariables["REMOTE_ADDR"].ToString()])).Minutes >= 5 : true) {
                mvc_Guestbook guestbook = new mvc_Guestbook();
                return View(guestbook);
            } else {
                Session["DELAY"] = (5 + Convert.ToDateTime(Session[Request.ServerVariables["REMOTE_ADDR"].ToString()]).Subtract(DateTime.Now).Minutes).ToString();
                return View("Denied");
            }
        }

        [AcceptVerbs(HttpVerbs.Post)]
        public ActionResult Create(mvc_Guestbook guestbook) {
            if (ModelState.IsValid) {
                try {
                    guestbook.MessageDate = DateTime.Now;
                    gr.Save(guestbook);
                    Session[Request.ServerVariables["REMOTE_ADDR"].ToString()] = DateTime.Now.ToString();
                    return RedirectToAction("Details", new { id = guestbook.GuestbookID });
                } catch {
                    ModelState.AddModelError("Error", "Error saving message");
                }
            }
            return View();
        }

        [Authorize]
        [AcceptVerbs(HttpVerbs.Get)]
        public ActionResult Edit(int id) {
            mvc_Guestbook guestbook = gr.GetMessage(id);
            if (guestbook == null) {
                return View("Not Found");
            } else {
                return View(guestbook);
            }
        }

        [Authorize]
        [AcceptVerbs(HttpVerbs.Post)]
        public ActionResult Edit(int id, FormCollection collection) {
            mvc_Guestbook guestbook = gr.GetMessage(id);
            try {
                UpdateModel(guestbook);
                gr.Update(guestbook);
                return RedirectToAction("Details", new { id = guestbook.GuestbookID });
            } catch {
                ModelState.AddModelError("Error", "Error saving message");
                return View(guestbook);
            }
        }

        [Authorize]
        [AcceptVerbs(HttpVerbs.Get)]
        public ActionResult Delete(int id) {
            mvc_Guestbook guestbook = gr.GetMessage(id);
            if (guestbook == null) {
                return View("Not Found");
            } else {
                return View(guestbook);
            }
        }

        [Authorize]
        [AcceptVerbs(HttpVerbs.Post)]
        public ActionResult Delete(int id, string confirmButton) {
            mvc_Guestbook guestbook = gr.GetMessage(id);
            if (guestbook == null) {
                return View("Not Found");
            } else {
                gr.Delete(guestbook);
                return View("Deleted");
            }
        }

        public ActionResult Details(int id) {
            mvc_Guestbook guestbook = gr.GetMessage(id);
            if (guestbook == null) {
                return View("NotFound");
            } else {
                return View(guestbook);
            }
        }
    }
}

For the code to wire this all up, I used Shiju’s example of the HttpContextLifetimeManager class as well as the UnityControllerFactory class. However, since I wasn’t using the NerdDinner code but my own Guestbook, I had to make the following changes to the UnityControllerFactory.Configure method:

public static void Configure() {
    //create new instance of Unity Container
    IUnityContainer container = new UnityContainer();
    //Register dependencies
    container.RegisterType<IFormsAuthentication, FormsAuthenticationService>();
    container.RegisterType<IMembershipService, AccountMembershipService>();
    container.RegisterInstance<MembershipProvider>(Membership.Provider);

    container.RegisterType<IGuestbookRepository, GuestbookRepository>(new HttpContextLifetimeManager<IGuestbookRepository>());
    ControllerBuilder.Current.SetControllerFactory(new UnityControllerFactory(container));
}

Finally, as Shiju illustrates, I added the call to UnityControllerFactory.Configure() into the Application_Start() method in my Globals.asax file and all is well.

All in all, I wouldn’t say I am 100% comfortable with exactly all that is happening in the code, but it is a good start at understanding DI with .NET MVC.

Manipulating an unordered list of links using jQuery

WARNING: This code was copied/pasted in from my old blog and there was some problem with the code example. It currently won’t work

The online fantasy football hosting site I use has lots of capability to modify layout and CSS, but some things are hard-coded and are only able to be manipulated using the DOM. They offer capabilities similar to that of MySpace. By that I mean you can add CSS and scripting via “Home Page Messages” so you can enhance and manipulate the layout of your site.

Someone asked how to manipulate navigation using javascript. I thought I’d tackle the task and get some more jQuery practice while I was at it. Here is what I came up with:

$(document).ready(function() {
    removeMenuLink($hm, HM_MENU, "Message Board");
    addMenuLink($hm, HM_MENU, "Test", "#/Test/Link", 1);
    addMenuLink($vm, VM_LEAGUE, "Example", "http://www.example.com", -1);
    removeMenuLink($vm, VM_LEAGUE, "Accounting");
});

var $hm = "#hsubmenu ul";
var $vm = "#vsubmenu ul li ul";
var HM_MENU = 0;
var VM_FRANCHISE = 0;
var VM_LEAGUE = 1;
var VM_STANDINGS = 2;
var VM_PLAYERS = 3;
var VM_HELP = 4;

function addMenuLink(menu, section, name, url, idx) {
    var $ul = $(menu);
    var link = "" + name + "";
    if (idx > -1 && idx < $ul.find("li").length) {
        $($($ul[section]).find("li")[idx - 1]).before(link);
    } else {
        $($ul[section]).append(link);
    }
}

function removeMenuLink(menu, section, name) {
    var $ul = $(menu);
    $($ul[section]).find("li").each(function() {
        var txt = $(this).find("a").text();
        if (name == txt) {
            $(this).remove();
        }
    });
}

As you can see, there are two functions: addMenuLink and removeMenuLink. There were requests for both, so I took a shot at each. Since many of the users have no clue how to use javascript and/or CSS, I tried to make the example as simple as possible. I created variables to hold the jQuery selectors for each menu (the site users asked to modify a horizontal and/or vertical sub-menu on the site). I also defined “constants” for that define the index of sections of the menus: the horizontal menu is a single section, and the vertical is broken up in to 5 sections. I had to define the single horizontal section to enable code reuse.

Each of the functions is called with a reference to the menu and the section. The addMenuLink function also takes in the link text, URL, and the list position to place the new link. The removeMenuLink simply takes an additional string of the menu link text to remove. I wanted to make it more simple than asking for an index of the item to remove. As I said, these users don’t necessarily know what they are doing beyond the brief instructions and help given to them on the site forums.

Anyway, I don’t know how useful this is, but it may come in handy for someone out there on the tubes. Enjoy!

Flash Pageflip Code

I am cleaning out my old code snippets to see what I can post on the new blog. Here is a sweet little Flash pageflip app based off some source from PageFlip v2.13 coded by [email protected].

About the code

If you did not already know, I work for an educational publisher. We have an online subscription-based site which is essentially most of our titles chopped up and indexed into small teaching units. Users pay a fee to have unlimited print capability. When we started the site, we were using PDF’s with an encryption plug-in. We want to allow our users the print capability (it is the backbone of the site) but we did not want them to store the files. We wanted them to use the site to “store” them, and we also did not want them distributing PDF’s. The plug-in works, but there are issues.

First off, the Mac version is a bit behind the Windows version. Since we deal with teachers and schools, we must have a Mac solution. The plug-in also only works with Adobe Reader. Since Mac come with Preview, users got confused about why they could see other PDF’s but not ours. Since Preview couldn’t use the plug-in, the users basically saw a bunch of blank pages since the PDF contents were never decrypted. Lastly, we used a Java based virtual page-flip viewer so users could see the contents of the file before downloading the PDF and printing. The major headache is the users had 3 pieces of software to install the get the full experience. Not good.

Alternative solution

Since we were having so many issues with installations and third-party software (reader wouldn’t recognize the plug-in, but the manufacturer’s installer said it was installed, Reader version was too old, Reader’s browser plug-in stinks, etc.) Istarted to look to alternatives. We considered writing our own PDF viewer, but quickly scrapped that idea. Then I started to look at Flash Paper. This looked like a pretty good alternative, but since the Macromedia/Adobe merger, the product is now treated like an illegitimate child.

I found Scribd, a start-up based in SF,CA. They took Flash Paper and basically rewrote it and extended it. It is really a pretty sweet thing. There were a couple issues, however. First, they would not let us purchase the code or host content on our servers. We have over 10,000 units, and we weren’t really eager to let them go, let alone the time it would’ve taken to upload (my company had a T1 back then and it was slooooow). We tried to come to an agreement but they just wouldn’t budge. In retrospect, I think it had to do with there PDF to SWF system (based on SWF Tools) and the fact that the “viewer” is really pretty basic. I think if people could run this locally they’d see that the system isn’t really that complex. The service is free, however, so it’s not like we wouldn’t get our money’s worth, right? However, all that aside, we still had issues with some of our PDF’s and really slow load times. We couldn’t have that, so I had to look elsewhere.

Pageflip

At this time the Flash pageflip stuff was pretty new, and quite cool IMHO. I thought it’d be pretty slick to use something like that and make my own Flash viewer (to replace the Java solution) and also use the Flash printing capabilities. This would solve all our problems! So, I downloaded this code and got to work. The code was using static page references, so I got it working with the XML file to dynamically load images into the viewer. It has been a while, so I don’t recall where the original code ended and mine began. Beyond that, I am no Actionscript guru, so I just kinda futzed around to get what I needed.

In the end, however, this was not the solution we used. It turns out to get that fancy flip action requires multiple versions of each page (3 if I recall correctly) and I could not figure out how to optimize it. Other than that, Flash basically just sucks. There is no garbage collection that can be called. Flash just decides when it will GC. Also, Flash has the nice undocumented feature of consuming every last drop of resources from your PC. We deal with user’s that have older PC’s, and our tests could actually crash browsers and lock-up machines. Thanks, Adobe!

What you get

Included in the zip are 2 versions of the viewer. the 2nd has an improved zoom capability. I put them both in so you could choose which one you wanted, and to let you see the differences I implemented regarding the zoom.

If you keep the number of pages small, or even keep them pretty low res, this will actually work okay. Did I mention you can also print? Oh, well, yeah, you can also print. All in all it is pretty groovy, but it just did not work out.

    Features:

  • Zoom capability
  • Print entire “document”
  • Dynamic page loading
  • Ability to add custom logo to viewer

Sample 1 Sample 2

Download
Preview

I hope someone out there finds a use for this. Enjoy!

T4: Text Template Transformation Toolkit

T4 is a template-based code generation engine supported in VS 2008 (and available for download for VS 2005). SubSonic 3.0 uses these files (.TT extension) in place of the old templating system. T4 can be used to generate C#, VB, XML, etc. T4 templates are pretty sweet.

Oleg Sych has a great post detailing just about everything you’d want to know about T4 templates. David Hayden provides a great screencast on using T4 templates which is a great way to see them in action. It has been a while since I watched the screencast, but if I recall he does a little DAL generation. I know there are tons of tools to do this, but if you are like me, a real-world example really helps me understand the usefulness of the latest and greatest technologies.

Simple T4 Example

<#@ Template Language="C#" #>
<# for (int i = 0; i < 10; i++) {
    Write("Hello World!");
} #>