DotNetNuclear
  • About
    • Contact
  • Products
    • Intensity Slider
    • MVP Template
    • Support
  • Learning

Cascading Country / State Dropdowns: A Study in DotNetNuke 7 WebAPI Services and Ajax
Article Rating

By  Scott Wilkinson

Posted Jan 05, 2013 19:14

Tweet

Comments 8

Cascading Combos jQuery DNN Services

Introduction

In this study, we are going to build a cascading set of dropdown boxes: one with a lists of all countries, the other a list of regions or states. When the user selects the country, the region list will dynamically change to the list of regions that belong to the selected country.

The architecture behind this is a couple DNN framework services that serve up the list of countries or regions from the DotNetNuke Common Lists API (the data for these lists are maintained in the Admin>Lists interface) as JSON and a jQuery component in an HTML module that can call the service and render the resulting lists as an html select list.

The sample services project, html code, and jQuery component are in the Related files section. The service framework project I built is based on the WebAPI implementation that is in DNN 7. If anyone needs it, I have the code that works on the MVC implementation of DNN 6.


Building the Services

Building a service starts out in much the same way we build a module. We create a library project in Visual Studio (required version of .NET framework is 4.0), include references, and build to the DNN bin folder.

Adding References

DotNetNuke 7 the Services Framework has been updated to be based on the new WebAPI stack instead of ASP.Net MVC like in version 6. The following references need to be added to the project. Browse to the /bin folder of your DNN7 install using the Add Reference dialog box.

  • DotNetNuke.dll
  • DotNetNuke.Web.dll
  • System.Net.Http.dll
  • System.Net.Http.Formatting.dll
  • System.Web.Http.dll

DotNetNuke Service References


Create the Controller and Actions

using System;
using System.Collections.Generic;
using System.Linq;
using System.Web.Http;
using DotNetNuke.Common.Lists;
using DotNetNuke.Web.Api;

namespace DNNuclear.DNN.ListServices.Services.Controllers
{
    public class ListsController : DnnApiController
    {
        [HttpGet] // GET: /Countries/
        [ActionName("countries")]
        [AllowAnonymous]
        public List GetCountryList()
        {
            DotNetNuke.Common.Lists.ListController lc = new DotNetNuke.Common.Lists.ListController();
            List leCountries = lc.GetListEntryInfoItems("Country", "", base.PortalSettings.PortalId).ToList();
            return leCountries.Select(x => new NameValueItem { Text = x.Text, Value = x.Value }).ToList();
        }

        [HttpGet] // GET: /Regions/{code}
        [ActionName("regions")]
        [AllowAnonymous]
        public List GetRegionList(string code)
        {
            DotNetNuke.Common.Lists.ListController lc = new DotNetNuke.Common.Lists.ListController();
            List leRegions = lc.GetListEntryInfoItems("Region", "Country." + code, base.PortalSettings.PortalId).ToList();
            return leRegions.Select(x => new NameValueItem { Text = x.Text, Value = x.Value }).ToList();
        }

        public class NameValueItem
        {
            public string Value { get; set; }
            public string Text { get; set; }
        }
    }
}

WebAPI requires that the name of all controllers end with the word Controller hence the name of our class ListsController which inherits DnnApiController. I decorated the methods with [HttpGet] to indicate that it is responding to GET requests. The ActionName indicates the action in the route url. The AllowAnonymous attribute is part of the framework security. This method requires no authentication and can be accessed by any application external or internal. I won't go further on the security aspects of the services framework because it is beyond the scope of this basic tutorial.

The code itself is using the List API to get the collection of items to create a generic list of the NameValueItem class. WebAPI will automatically convert any POCO (Plain Old C# Class) and output it as Json (or Xml) based on the content disposition.


Create the Routes

using System;
using DotNetNuke.Web.Api;

namespace DNNuclear.DNN.ListServices.Services
{
    public class DNNListServices : IServiceRouteMapper
    {
        const string moduleFolderName = "DNNListServices";

        public void RegisterRoutes(IMapRoute routeManager)
        {
            routeManager.MapHttpRoute(moduleFolderName, "list", "{controller}/{action}",
                    new[] { "DNNuclear.DNN.ListServices.Services.Controllers" });

            routeManager.MapHttpRoute(moduleFolderName, "sublist", "{controller}/{action}/{code}",
                    new[] { "DNNuclear.DNN.ListServices.Services.Controllers" });
        }
    }
}

Here is a break down of each of the parameters passed to IMapRoute.MapHttpRoute:

  • moduleFolderName - set to the constant: This parameter is unique to DNN and should be a name unique to your module/service, and will typically match the name of the DesktopModules folder in which your module is installed. It is important to use a name unique to your service to avoid routing conflicts with other services that may be installed.
  • "list" - string routeName: A name for your route. This makes up part of the total route name when routes are registered in ASP.Net. The combination of moduleFolderName and routeName must always be unique.
  • "{controller}/{action}" - string url: Standard WebAPI parameter that defines the unique characteristics of your route.
  • "new [] {"MyServices"} = string[] namespaces: Unique to DNN, this is an array of namespaces to search for controllers for this route. The parameter helps prevent conflict between services built by different developers.

With the two routes defined, our two service endpoints examples will be the following:

  • /DesktopModules/DNNListServices/api/lists/countries
  • /DesktopModules/DNNListServices/api/lists/regions/US

Therefore the service endpoint consists of DesktopModules / [ModuleName] / api / [ControllerName] / [Action] / [CountryCode(for Region)]

The Ajax Client

Now that our REST services are ready, we are going to build a client to consume the services. The client will consist of two select lists - one for country and the other for state. We are going to use a jQuery component called jCombo that hooks up both the ajax loading AND the dependency loading (making one dropdown load based on the value of the other).

The Html

<div class="dnnForm" id="dnnucForm_JComboTest">
<fieldset>
<div class="dnnFormItem dnnFormShort">
  <div><label><span id="lblCountry">Country:</span></label></div>
  <select name="ddlCountry" id="ddlCountry"></select>
</div>
<div class="dnnFormItem dnnFormShort">
<div><label><span id="lblRegion_lblNoHelpLabel">State:</span></label></div>
<select name="ddlState" id="ddlState"></select>
</div>
</fieldset>
</div>

I added an HTML module to a test page and added the following html to it to create two empty select lists named: ddlCountry and ddlState.

The Javascript

Next I added the following javascript to the Header of the HTML module (Module Settings>Advanced Settings>Header)

<script id="aJQCombo" type="text/javascript" src="/Portals/0/Scripts/jquery.jCombo.js"></script>
<script type="text/javascript">
    $(document).ready(function () {
        bindSelectLists();
    });

    function bindSelectLists() {
        var basePath = "/DesktopModules/DNNListServices/";
        $("#ddlCountry").jCombo(basePath + "api/lists/countries", { selected_value : 'US' } );

        $("#ddlState").jCombo(basePath + "api/lists/regions/", { 
			        parent: "#ddlCountry"
		        });
    }
</script>

Be sure to drop the jquery.jCombo.js into the correct folder on the server. I chose to put it in a scripts folder under my portal root.

The script simply initializes the select lists as jCombo components passing the path of our GET json service to the constructor. Notice the State list gets another parameter called 'parent'. jCombo will create an event on change of the parent dropdown which will take the value of the parent's selected item and add it to the GET url in the first parameter of the child dropdown. So if I select 'US' as the country, it will create an ajax call for the state list as basePath + "api/lists/regions/' + 'US' -- which matches our route for the region lookup with country code.

Related Files

  • DNNListServices (82.4 KB)
  • jquery.jCombo (837 Bytes)
  • DNNListServices_VS2012 (117 KB)

Rate this


Post Comment

Name (required)

Email (required)

CAPTCHA image
Enter the code shown above:

8 Comments

Ian Robinson
# Ian Robinson
Wednesday, February 06, 2013 2:05 PM
Great blog post, although any particular reason you're not using the Client Resource Management API to register the jCombo library? :)
host
# host
Wednesday, February 06, 2013 4:42 PM
Ian,

I have used the Client Resource Management API in my skins via the DnnJsInclude aspx tag or in code using DotNetNuke.Web.Client.RegisterScript(). In this example, I am using an HTML module to house all of my front end which is decoupled from the services. But yes, if this technique is used as part of module development, we should add an include in the View's Page_Prerender event with a call to DotNetNuke.Web.Client.RegisterScript().
Damon
# Damon
Thursday, February 14, 2013 2:27 PM
I cannot get the project to load in vs2012. It basically shows the migration page but does not load. I installed the Build Tools because it was complaining about needing them but that did not remedy the issue. I hate when people post comments like this but not sure how to contact you.
host
# host
Thursday, February 14, 2013 2:39 PM
Damon,

All my contact info is on my Contact page. Send me your email address and I would be happy to give you the project in VS2012.
host
# host
Thursday, February 14, 2013 3:07 PM
Actually, I just added the VS2012 project version in the Related files section. Enjoy!
taofeek
# taofeek
Tuesday, February 04, 2014 7:01 AM
pleaseI need the code that works on the MVC implementation of DNN 6.
jordan
# jordan
Sunday, May 11, 2014 1:24 AM
I got stuck on the controller name

the endpoint url will uses the controller name WITHOUT the "controller" at the end.

Lets say my controller is name ProductContorller then:
/DesktopModules/ModuleName/api/ProductController/ActionName
does not work

you have to use:
/DesktopModules/ModuleName/api/Product/ActionName

I hope this helps someone - it took me a while to figure it out
host
# host
Sunday, May 11, 2014 12:16 PM
Jordan,

Yes, exactly. Hence the name of the API method in my sample was ListsController, but the path is: /DesktopModules/DNNListServices/api/*lists*/countries

Sorry if I didn't stress that point in the article! Thanks!!
Did this help you? Give back... Give $5 so we can continue creating these articles Why?
25 Latest Articles Current Articles | Archives | Search
  • © 2019 DotNetNuclear LLC
  • Terms Of Use |
  • Privacy Statement |
  • Login
  • DNN Software
  • YouTube
  • Twitter
  • LinkedIn
  • Facebook
  • Stack Overflow