Archive for January, 2010


How to make a parent/child drop down list using JQuery

If you need to make a drop down list populate based on the selection of another drop down list, here’s one way.

· You’ll want to use a jquery ajax call so it does proper error handling (using the default error delegate we built in a previous email).

· Make sure the datatype of your ajax call is json and that your controller action returns a JsonResult.

· Use jquery show/hide to hide the child drop down list on page load and show it when the user makes a selection from the parent drop down list.

· In the ajax success function, you will:

o Add options to your select control (a.k.a. drop down list) using jquery’s each statement. Note: the “option” object in the each function delegate is not strongly typed, so you will just need to know your CV properties and type them in manually (sorry, no intellisense for that). For example, if you return a List of DDLDispValueCVs in your BLL method, your option object’s properties will be “Disp” and “Value”.

o Then bind a change event handler to your select control that calls the load function for your second select control.

o Wrap the functions with jquery block/unblockUI calls so the user sees a soothing “In Progress…” popup while the ajax calls are filling the drop down lists.

Here’s an example of the view page:

<script type="text/javascript">
    $(document).ready
    (
        function()
        {
            $('#divTransferTo').hide();
            $.ajax
            (
                {
                    url: '<%= Url.Content("~/MyController/GetList") %>',
                    success: function(data) {
                        // fill the ddl with the data from the json call
                        var ddl = $('#ddlTransferFrom');
                        optionArray = [];
                        $.each(data, function(i, option) {
                            optionArray[i] = "<option value='" + option.Value + "'>" + option.Display + "</option>";
                        });
                        ddl.append(optionArray.join(''));
                        // bind an "on selected change" event to the ddl
                        ddl.change
                        (
                            function() {
                                $.blockUI({ message: $('#information'), css: { width: '275px'} });
                                LoadTransferToDDL($(this).val());
                                $.unblockUI();
                            }
                        );
                    },
                    dataType: 'json'
                }
           );
        }
    );
     function LoadTransferToDDL(id)
    {
        $('#divTransferTo').show();
        var transferToUrl = '<%= Url.Content("~/MyController/GetOtherList") %>';
        $.ajax
        (
            {
                url: transferToUrl + '/' + id,
                success: function(data)
                {
                    $('#ddlTransferTo').vis
                                       // fill the ddl with the data from the json call
                    var ddl = $('#ddlTransferTo');
                    optionArray = [];
                    $.each(data, function(i, option)
                    {
                        optionArray[i] = "<option value='" + option.Value + "'>" + option.Display + "</option>";
                    });
                    ddl.append(optionArray.join(''));
                     // bind an "on selected change" event to the ddl
                    ddl.change
                    (
                        function()
                        {
                            $.blockUI({ message: $('#message'), css: { width: '275px'} });
                            alert('You selected this value: ' + $(this).val());
                            $.unblockUI();
                        }
                    );
                },
                dataType: 'json'
            }
       );
    }</script>
<form id="mvcForm" method="post" action="">
    <h2>Reassign Something</h2>
Transfer From:
    <div id="divTransferFrom"><select id="ddlTransferFrom"></select></div>
Transfer To:
    <div id="divTransferTo"><select id="ddlTransferTo"></select></div>
</form>

Here’s an example of the controller actions used by the ajax calls. Notice that the Json method built into MVC.Controller automagically converts your list of cvs into json:

[AcceptVerbs(HttpVerbs.Get)]
public JsonResult GetList()
{
 try 
{
  return Json(myBLL.GetList());
 }
 catch (Exception ex)
 {
  return HandleError(ex, null);
 }
}

[AcceptVerbs(HttpVerbs.Get)]
public JsonResult GetOtherList(object id)
{
 try 
{
  return Json(myBLL.GetOtherList(Convert.ToInt64(id)
 }
 catch (Exception ex)
 {
  return HandleError(ex, null); 
}
}

How to setup intellisense for javascript files (like JQuery) in Visual Studio 2008

Visual Studio 2008 already shows intellisense for javascript if you have the javascript embedded in the page, but if you want to get intellisense on javascript in js files referenced in your master page, you’ll need to make some mods. In the four steps detailed below, you’ll get javascript intellisense for external js files like jquery.

Making the mods

First, you’ll need to install SP1 for Visual Studio 2008 from here: http://www.microsoft.com/downloads/details.aspx?FamilyId=FBEE1648-7106-44A7-9649-6D9F6D58056E&displaylang=en . This service pack adds a lot of enhancements to intellisense and other features of visual studio, so it’s useful even if you don’t want js file intellisense.

Then you need to install Microsoft’s hotfix for Visual Studio 2008 from here: http://code.msdn.microsoft.com/KB958502 . This hotfix makes Visual Studio automatically load vs-doc.js files as long as they are in the same folder as the dependent js file.
Place any vs-doc.js files in the same folder as their dependent js file, if you don’t have the vs-doc file for jquery, you can download it here: http://code.google.com/p/jqueryjs/downloads/detail?name=jquery-1.3.2-vsdoc2.js (you’ll need to remove the 2 at the end of the file name or Visual Studio won’t see it). For example, if you are using jquery-1.3.2.js, be sure to have the jquery-1.3.2-vsdoc.js file in the same folder. But if you’re using jquery-1.3.2.min.js, you’ll need the jquery-1.3.2.min-vsdoc.js file instead.
So far, pretty straightforward. Here’s the hacky part: You’ll need to put this code block into your Site.Master and any other master page you want to derive from and get javascript intellisense:

<!--Load javascript into intellisense, but do not render it on the user page. 
Note: this must be in the master page, not an include file, and it must use absolute reference, not dynamic Url.Content. 
Only some js files will load in intellisense; 
some js files cause all the js files to fail to load into intellisense. --->
<!--if (false){-->
<script src="~/Assets/JavaScript/jquery-1.3.2.js" type="text/javascript"></script>
<script src="~/Assets/JavaScript/jquery.blockUI.js" type="text/javascript"></script>
<script src="~/Assets/JavaScript/json2.js" type="text/javascript"></script>
<!---->

Here’s why: The Visual Studio hotfix won’t see the intellisense info for your js files in an MVC environment because of the way we need to dynamically fetch the runtime url. You will need to add an absolute url reference, which is why we can’t use the MVC Url.Content method. Additionally, you don’t want that reference to get rendered to the client, hence the “if false” clause the always prevents it from rendering on the client, but tricks Visual Studio into seeing the reference for intellisense. Furthermore, VS2008 won’t see js file references in include files like MasterHeadElement.ascx, it only sees the ref if it is in the master page itself.

One warning: I found that some of the js file references fry the system and prevent all of the js files from loading into intellisense. I’m not sure why, but so far I know that jquery, jquery.blockUI, and json2 always work, but some other js files don’t.

Easy way to setup default error handling for your JQuery ajax calls

If you don’t want to keep copy/pasting the same error function for your jquery ajax calls over and over again, here’s a quick way to set up error handling in one place.
Add this javascript block below your jquery library links (such as in your MasterHeadElement.ascx control or master pages):

<script type="text/javascript">
// THIS MUST BE LEFT AT THE BOTTOM OF THE HEAD ELEMENT...the code below needs to have the jquery libraries loaded
// setup all jquery ajax calls to use this error function by default (this can be overridden simply by specifying the error property as normal in the ajax call).
$().ready(function() {
    $.ajaxSetup({
        // put your favorite error function here:
        error: 
            function(XMLHttpRequest, textStatus, errorThrown) {
                // release any existing ui blocks
                $.unblockUI;
                var errorObj = JSON.parse(XMLHttpRequest.responseText);
                // send the user to the system error page if system error, otherwise popup the user error div
                if (!errorObj.Success) {
                    if (errorObj.ErrorType != "system") {
                        $('#UserError').html(errorObj.Message);
                        $.blockUI({ message: $('#UserErrorWrapper'), 
                        css: { width: '400px', height: '300px', overflow: 'scroll' }
                     });
                }
                else {
                    window.location = errorObj.ErrorPageUrl;
                }
            }
        }
    });
});
</script>

Now all ajax calls in your app will default to this error function. All you have to do is leave out the error delegate in your ajax calls (i.e. include the url, success, datatype, and other properties on your $.ajax call, but leave out the error property). Like this:

$.ajax({
    url: // some link,
    success: function(data){
        // do something
    },
    dataType: 'json'
});

If you want to use a different error function on an ajax call, simply write in the error function into that ajax call as normal, the global default error function will be ignored. Like this:

$.ajax({
    url: // some link,
    success: function(data){
        // do something},
    dataType: 'json',
    error: function(XMLHttpRequest, textStatus, errorThrown) {
        alert("Oh noes!");
    }
});

How to create an automated unit test for your controller in ASP.NET MVC

In this example, we will create an automated unit test for a controller action. This is a great tool for isolating issues with javascript and ajax calls by verifying the presentation layer (i.e. the page) is actually getting the correct info from the controller and lower layers.
Don’t make your base controller decide if the site menu should appear on a group of pages, let a master page decide if the menu should appear on a group of pages

Make sure your login pages (i.e. the aspx views in YourSolution/Views/Account) use Logon.Master instead of Site.Master. Your login pages will not be showing the site map menu, so you’ll need to leave out the SiteMap line from your Logon.Master entirely.

Create the test for the controller

As long as you have a separation of concerns in your application, you can easily create a controller test like the example below which uses the Arrange-Act-Assert pattern. Notice that our FillDDL action returns a JsonResult (which would be consumed by an ajax call such from a jqgrid or jq getjson). In the test, the JsonResult is verified to contain the expected data.

[Test]
public void FillDDL_ReturnsCorrectJsonResult()
{
    // *** ARRANGE ***
    // create a mock of the dependencies
    IMyBLL mockMyBLL = MockRepository.GenerateMock<imybll>();
    IMyOtherBLL mockMyOtherBLL = MockRepository.GenerateMock<imyotherbll>();
    DDL test = new DDL {Disp = "XXX", Value = "111"};
    List<ddl> testList = new List<ddl> {test};
    mockMyBLL.Stub(c => c.GetList()).Return(testList);
    
    // *** ACT ***
    // get the concrete class that will be tested; inject all the mocks into the concrete class
    // Note: if a dependency can't be injected thru a constructor, you can use this:
    ObjectFactory.Inject(typeof(IMyOtherBLL), mockMyOtherBLL);
    MyController controller = new MyController(mockMyBLL);
    // run the test method
    JsonResult resultJson = controller.FillDDL();

    // *** ASSERT ***
    // verify test results
    List<ddl> resultList = (List<ddl>) resultJson.Data;
    Assert.AreEqual(test.Disp, resultList[0].Disp);
    Assert.AreEqual(test.Value, resultList[0].Value);
}

How to get failed login attempts and last login date from AD server

You can fetch a user’s last good login date and number of failed login attempts from the AD server. First, you will need these keys in your section in your web.config (which will need to be updated as the app is passed to QA/Prod):
We will store the login attempts:

public void StoreLoginAttempts(string userName)
{
string adServer = GetConfigAppSettings("AD_DomainIP");
string adServerUser = GetConfigAppSettings("AD_User");
string adServerPassword = GetConfigAppSettings("AD_Pass");
string adServerContainer = GetConfigAppSettings("AD_Container");

// looks like this: "CN=Users,DC=AD,DC=MyDomain,DC=org"
if (!string.IsNullOrEmpty(adServer) 
&& !string.IsNullOrEmpty(adServerUser) 
&& !string.IsNullOrEmpty(adServerPassword) 
&& !string.IsNullOrEmpty(adServerContainer))
{
PrincipalContext principalContext =
new PrincipalContext
(ContextType.Domain,adServer,adServerContainer,adServerUser,adServerPassword);
UserPrincipal user = 
UserPrincipal.FindByIdentity(principalContext, userName);
int numberOfFailedLoginAttempts = user.BadLogonCount;
DateTime? lastSuccessfulLogin = null;
if (user.LastLogon != null)
{
lastSuccessfulLogin = user.LastLogon.Value.ToLocalTime();
}

// save to session 
storeSetSession("NumberOfFailedLoginAttempts", 
numberOfFailedLoginAttempts.ToString());
SetSession("LastSuccessfulLogin", lastSuccessfulLogin.ToString());
}
}

You can fetch the login attempts and last login by getting the session object. For example, you can use a code block like this in your code to populate the login attempts message in your app’s header or footer:

private string GetLoginAttemptsMessage()
{
// get values from session saved during LogOn
string numberOfFailedLoginAttempts = GetSession("NumberOfFailedLoginAttempts");
string lastSuccessfulLogin = GetSession("LastSuccessfulLogin");
return
string.Format("There have been {0} unsuccessful login attempt(s) since your last successful AD login on {1}"
,numberOfFailedLoginAttempts, lastSuccessfulLogin);
}
© 2017 Robert Corvus