Kendo UI Mobile and ASP.NET MVC : Building the Mobile Task Manager, Part 2 - Displaying Data

This website is no longer actively supported

Written by John DeVightJohn DeVight on 13 Apr 2012 12:19

Download the Sample Code at: Kendo UI ASP.NET Sample Applications, Source Code tab by clicking on "Download" under "Latest Version" and looking for Mobile -> TaskManager.

Overview

In Kendo UI Mobile and ASP.NET MVC : Building the Mobile Task Manager, Part 1 - Getting Started, I setup the environment for the Android application using PhoneGap and Kendo UI Mobile. The application displayed a Kendo UI Mobile ListView with 2 items in it, "Sprints" and "Developers". Now lets get some data to work with. To do this, I decided to add another controller to the TaskManager MVC application called MobileController. I didn't want to create any kind of dependancy or knowledge of the MobileController within the Task Manager MVC application. I also wanted the freedom to be able to update and deploy the MobileController without having to update the Task Manager MVC application. Fortunately, ASP.NET MVC 3 will search all the assemblies for any class that derives from System.Web.Mvc.Controller to find the requested controller. So all I needed to do is create a class library with the MobileController and deploy it to the bin folder of the Task Manager MVC application and I have my MobileController available for my Android application.

Creating the TaskManager.Mvc.Mobile Class Library

In Visual Studio 2010 Express, I created a new Class Library project called TaskManager.Mvc.Mobile. In the Solution Explorer, I right clicked on the Solution and in the popup menu I highlighted "Add" and selected "Existing Project". I browsed to the folder where I have the TaskManager MVC application (this project can be downloaded from Kendo UI ASP.NET Sample Applications, Source Code tab) and selected the TaskManager.Shared project. I repeated the steps and selected the TaskManager.Mvc project. I right clicked on the TaskManager.Mvc project and in the popup menu, I selected "Set as Startup Project". Next, I right clicked on the TaskManager.Mvc.Mobile project and in the popup menu, I selected "Add Reference". In the Projects tab, I selected TaskManager.Shared.

Since I do not want to create a dependancy between the TaskManager MVC application and the TaskManager MVC Mobile class library, I need to copy the assembly to the bin folder of the TaskManager MVC application. To do this, I right clicked on the TaskManager.Mvc.Mobile project and in the popup menu, I selected Properties. On the Output path, I typed the following path to the TaskManager MVC application's bin folder:

..\..\..\Web\TaskManager\TaskManager.Mvc\bin\

Next, I need to add the necessary references that will allow the class library to contain a controller. I right clicked on the TaskManager.Mvc.Mobile project and in the popup menu, I selected "Add Reference". In the .NET tab, I add the following references:

  • System.Web
  • System.Web.Extensions
  • System.Web.Mvc (version 3.0.0)

Creating the JsonpResult

Before I create the MobileController, I need to provide a method of returning data across domains. JSON only works within the same domain. To work across domains, I need to use JSONP. ASP.NET MVC 3 has a JsonResult, but nothing like a JsonpResult. I searched the internet a while ago and found one that works great (I wish I still had the url where I found it since I like to give credit where credit is due). In the TaskManager.Mvc.Mobile project, I create a folder called Extensions. I create a new class called JsonpResult and paste in the code for the JsonpResult class. Here is the code:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.Mvc;
using System.Web.Script.Serialization;
 
namespace TaskManager.Mvc.Mobile.Extensions
{
    public class JsonpResult : ActionResult
    {
        private readonly object _obj;
 
        public JsonpResult(object obj)
        {
            _obj = obj;
        }
 
        public override void ExecuteResult(ControllerContext context)
        {
            var serializer = new JavaScriptSerializer();
            var callbackname = context.HttpContext.Request["d"];
            var jsonp = string.Format("{0}({1})", callbackname, serializer.Serialize(_obj));
            var response = context.HttpContext.Response;
            response.ContentType = "application/json";
            response.Write(jsonp);
        }
    }
}

Creating the MobileController

In the TaskManager.Mvc.Mobile project, I create a folder called Controllers. I create a new class called MobileController and derive it from Controller. I would like to start with displaying a list of Sprints and Developers in my Android application. I add 2 Controller Actions called Sprints and Developers that get the information from the SprintRepository and return the data using the JsonpResult class. Here is the code:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.Mvc;
using TaskManager.Mvc.Mobile.Extensions;
using TaskManager.Data;
using TaskManager.Models;
 
namespace TaskManager.Mvc.Mobile.Controllers
{
    public class MobileController : Controller
    {
        /// <summary>
        /// Get all the sprints.
        /// </summary>
        /// <returns></returns>
        public JsonpResult Sprints()
        {
            return new JsonpResult(new { sprints = SprintRepository.GetSprints() });
        }
 
        /// <summary>
        /// Get all the developers.
        /// </summary>
        /// <returns></returns>
        public JsonpResult Developers()
        {
            return new JsonpResult(new { developers = SprintRepository.GetDevelopers() });
        }
    }
}

Now I compile the TaskManager.Mvc.Mobile project, and I can see that the TaskManager.Mvc.Mobile.dll assembly has been copied to the bin folder of my TaskManager.Mvc application. I'm now ready to retrieve the data from my Android application.

Displaying Sprints in the TaskManager Mobile Application

Updating index.html

In the android-root/assets/www folder, I created the index.html page. In the index.html page, I need to add a new page for the list of sprints. I also need to modify the "Sprints" listitem in the "home-listview" unordered list to be a link to the sprints page. When the sprints page is displayed, I need to retrieve the list of sprints from the TaskManager.Mvc application and display the list on the page. It would also be nice to have tabs to be able to navigate between the "home" page and the "sprints" page. Before I explain it all, here is the code for the index.html page:

<!DOCTYPE html>
<html>
<head>
    <title>Overview</title>
 
    <link href="styles/kendo/kendo.mobile.all.min.css" rel="stylesheet" />
 
    <script src="js/cordova-1.5.0.js"></script>
    <script src="js/kendo/jquery.min.js"></script>
    <script src="js/kendo/kendo.mobile.min.js"></script>
</head>
<body>
    <!-- PAGES -->
 
    <div id="home" data-role="view" data-layout="layout" data-title="Task Manager">
        <ul id="home-listview" data-role="listview">
            <li><a href="#sprints">Sprints</a></li>
            <li>Developers</li>
        </ul>
    </div>
 
    <div id="sprints" data-role="view" data-layout="layout" data-title="Sprints" data-init="TaskManager.Sprints.init">
        <ul id="sprints-listview" data-role="listview">
        </ul>
    </div>
 
    <!-- LAYOUTS -->
 
    <div data-role="layout" data-id="layout">
        <footer data-role="footer">
            <div data-role="tabstrip">
                <a data-icon="compose" href="#home">Home</a>
                <a data-icon="organize" href="#sprints">Sprints</a>
            </div>
        </footer>
    </div>
 
    <!-- TEMPLATES -->
 
    <script id="sprints-listview-template" type="text/x-kendo-template">
        Sprint #= Id #
    </script>
 
    <!-- SCRIPTS -->
 
    <script src="js/json-arrays.js"></script>
    <script src="js/taskmanager.js"></script>
    <script src="js/taskmanager.dataaccess.js"></script>
    <script src="js/taskmanager.sprints.js"></script>
    <script>
        window.kendoMobileApplication = new kendo.mobile.Application(document.body, { platform: "android" });
    </script>
</body>
</html>

I modified the "Sprints" listitem in the "home-listview" unordered list to be a link to the sprints page by changing the contents of the listitem from just text, to an anchor with an href="#sprints". This tells Kendo UI to find the div with a data-role of "view" and an id of "sprints" and display the "page". I also modified the "home" page by adding a new attribute to the div element called data-layout and set the value to layout. This tells Kendo UI to find a div with the data-role of "layout" and a data-id of "layout". This div will define the layout for the page.

I added the "sprints" page defined the following attributes on the div element:

  • data-role="view" - indicates that the div is a view.
  • data-layout="layout" - defines the layout for the page.
  • data-title="Sprints" - defines the title for the page. This doesn't get displayed in an Android application, but would in an iPhone application.
  • data-init="TaskManager.Sprints.init" - the JavaScript function that will get called when the page is initially rendered.

The "sprints" page has an unordered list element with an id of "sprints-listview" and a data-role="listview" that will be populated with the list of sprints.

The script element with an id of "sprints-listview-template" is a Kendo UI template. This is used to define how each sprint will be rendered in the list.

The layout defines the footer for the page. Since this is an Android application, Kendo UI Mobile will render the footer at the top of the application instead of at the bottom of the page. This is the expected behavior of an Android application. In the footer, I define a tabstrip with 2 tabs, one for the home page and one for the sprints page. Kendo UI Mobile provides a list of icons that can be used. I picked the compose icon for the home page and the organize icon for the sprints page (See the Kendo UI Mobile documentation for Buttons for a complete list of icons).

I added several JavaScript files. They are:

  • json-arrays.js - extends the JSON object to include functions to find and sort JSON objects.
  • taskmanager.js - defines the TaskManager namspace and the variables and functions that will be shared across the entire application.
  • taskmanager.dataaccess.js - contains the functions that will handle retrieving and saving the data to and from the TaskManager MVC application.
  • taskmanager.sprints.js - contains the functions for working with sprints.

Creating taskmanager.js

In the android-root/assets/www/js folder, I created the taskmanager.js file. Here is the code:

var TaskManager = {}
TaskManager._server = "http://192.168.56.102/TaskManager.Mvc/Mobile";

I do all my development in virtual machines using VirtualBox. The virtual machine uses the Host-only Adapter and has a static ip address of 192.168.56.102. The application is hosted in IIS and has a virtual directory of "TaskManager.Mvc" and the name of the controller is Mobile.

Creating taskmanager.dataaccess.js

In the android-root/assets/www/js folder, I created the taskmanager.dataaccess.js file. Here is the code:

TaskManager.DataAccess = {}
 
TaskManager.DataAccess._sprints = null;
 
TaskManager.DataAccess.getSprints = function(callback) {
    try {
        if (TaskManager.DataAccess._sprints == null) {
            $.ajax({
                url: TaskManager._server + "/Sprints",
                dataType: "jsonp",
                jsonp: "d",
                cache: false
            })
            .done(function (response) {
                TaskManager.DataAccess._sprints = response.sprints;
                callback(TaskManager.DataAccess._sprints);
            })
        }
    } catch (err) {
    }
}

The TaskManager.DataAccess.getSprints makes an ajax call the Sprints Controller Action to get the sprints from the TaskManager MVC application. When the results are returned, the sprints are stored in the TaskManager.DataAccess._sprints member variable and the callback function is called and passed the sprints.

Creating taskmanager.sprints.js

In the android-root/assets/www/js folder, I created the taskmanager.sprints.js file. Here is the code:

TaskManager.Sprints = {}
 
TaskManager.Sprints.init = function() {
    TaskManager.DataAccess.getSprints(function(sprints) {
        // Initialize the sprints listview.
        $("#sprints-listview").kendoMobileListView({
            dataSource: kendo.data.DataSource.create({
                data: sprints
            }),
            template: kendo.template($("#sprints-listview-template").html())
        });
    });
}

The TaskManager.Sprints.init function gets called when the Sprints page is initialized. The function calls TaskManager.DataAccess.getSprints and passes in a callback function. The callback is passed the list of sprints. The sprints are used to initialize the sprints-listview as a Kendo UI Mobile ListView. The template //(defined in index.html) is passed in as the template configuration setting to define how the list of sprints are displayed.

Running the Application

At this point, you can run the applciation using the Android Virtual Mobile Device. Here are some screenshots of the application:

Home Page Sprints Page
taskmanager-mobile-part2-home-page.png
taskmanager-mobile-part2-sprints-page.png

Displaying Developers in the TaskManager Mobile Application

Updating index.html

The process of displaying the list of developers is very similar to displaying the list of sprints. I need to add a new page for the list of developers. I also need to modify the "Developers" listitem in the "home-listview" unordered list to be a link to the developers page. When the developers page is displayed, I need to retrieve the list of developers from the TaskManager.Mvc application and display the list on the page. Here is the code for the index.html page:

<!DOCTYPE html>
<html>
<head>
    <title>Overview</title>
 
    <link href="styles/kendo/kendo.mobile.all.min.css" rel="stylesheet" />
 
    <script src="js/cordova-1.5.0.js"></script>
    <script src="js/kendo/jquery.min.js"></script>
    <script src="js/kendo/kendo.mobile.min.js"></script>
</head>
<body>
    <!-- PAGES -->
 
    <div id="home" data-role="view" data-layout="layout" data-title="Task Manager">
        <ul id="home-listview" data-role="listview">
            <li><a href="#sprints">Sprints</a></li>
            <li><a href="#developers">Developers</a></li>
        </ul>
    </div>
 
    <div id="sprints" data-role="view" data-layout="layout" data-title="Sprints" data-init="TaskManager.Sprints.init">
        <ul id="sprints-listview" data-role="listview">
        </ul>
    </div>
 
    <div id="developers" data-role="view" data-layout="layout" data-title="Developers" data-init="TaskManager.Developers.init">
        <ul id="developers-listview" data-role="listview">
        </ul>
    </div>
 
    <!-- LAYOUTS -->
 
    <div data-role="layout" data-id="layout">
        <footer data-role="footer">
            <div data-role="tabstrip">
                <a data-icon="compose" href="#home">Home</a>
                <a data-icon="organize" href="#sprints">Sprints</a>
                <a data-icon="contacts" href="#developers">Developers</a>
            </div>
        </footer>
    </div>
 
    <!-- TEMPLATES -->
 
    <script id="sprints-listview-template" type="text/x-kendo-template">
        Sprint #= Id #
    </script>
 
    <script id="developers-listview-template" type="text/x-kendo-template">
        #= FirstName + ' ' + LastName #
    </script>
 
    <!-- SCRIPTS -->
 
    <script src="js/json-arrays.js"></script>
    <script src="js/taskmanager.js"></script>
    <script src="js/taskmanager.dataaccess.js"></script>
    <script src="js/taskmanager.sprints.js"></script>
    <script src="js/taskmanager.developers.js"></script>
    <script>
        window.kendoMobileApplication = new kendo.mobile.Application(document.body, { platform: "android" });
    </script>
</body>
</html>

I modified the "Developers" listitem in the "home-listview" unordered list to be a link to the sprints page by changing the contents of the listitem from just text, to an anchor with an href="#developers". This tells Kendo UI to find the div with a data-role of "view" and an id of "developers" and display the "page".

I added the "developers" page defined the following attributes on the div element:

  • data-role="view" - indicates that the div is a view.
  • data-layout="layout" - defines the layout for the page.
  • data-title="Developers" - defines the title for the page. This doesn't get displayed in an Android application, but would in an iPhone application.
  • data-init="TaskManager.Developers.init" - the JavaScript function that will get called when the page is initially rendered.

The "developers" page has an unordered list element with an id of "developers-listview" and a data-role="listview" that will be populated with the list of developers.

In the footer of the layout, I added another tab for the developers page and used the contacts icon for it.

The script element with an id of "developers-listview-template" is a Kendo UI template. This is used to define how each developer will be rendered in the list.

I added another JavaScript file called taskmanager.developers.js that contains the functions for working with developers.

Adding the TaskManager.DataAccess.getDevelopers function to taskmanager.dataaccess.js

In the taskmanager.dataaccess.js, I added the TaskManager.DataAccess.getDevelopers function:

TaskManager.DataAccess.getDevelopers = function(callback) {
    try {
        if (TaskManager.DataAccess._developers == null) {
            $.ajax({
                url: TaskManager._server + "/Developers",
                dataType: "jsonp",
                jsonp: "d",
                cache: false
            })
            .done(function (response) {
                TaskManager.DataAccess._developers = response.developers;
                callback(TaskManager.DataAccess._developers);
            })
        }
    } catch (err) {
    }
}

The TaskManager.DataAccess.getDevelopers makes an ajax call the Developers Controller Action to get the developers from the TaskManager MVC application. When the results are returned, the developers are stored in the TaskManager.DataAccess._developers member variable and the callback function is called and passed the developers.

Creating taskmanager.developers.js

In the android-root/assets/www/js folder, I created the taskmanager.developers.js file. Here is the code:

TaskManager.Developers = {}
 
// Initialize the developers view.
TaskManager.Developers.init = function() {
    TaskManager.DataAccess.getDevelopers(function(developers) {
        // Initialize the developers listview.
        $("#developers-listview").kendoMobileListView({
            dataSource: kendo.data.DataSource.create({
                data: developers
            }),
            template: kendo.template($("#developers-listview-template").html())
        });
    });
}

The TaskManager.Developers.init function gets called when the Developers page is initialized. The function calls TaskManager.DataAccess.getDevelopers and passes in a callback function. The callback is passed the list of developers. The developers are used to initialize the developers-listview as a Kendo UI Mobile ListView. The template is passed in as the template configuration setting to define how the list of developers are displayed.

Running the Application

Here are some screenshots of the application using the Android Virtual Mobile Device:

Home Page Developers Page
taskmanager-mobile-part2-home-page-2.png
taskmanager-mobile-part2-developers-page.png

Conclusion

Adding the MobileController to the TaskManager MVC application and using the JsonpResult made it easy to share the data with a mobile application. Getting the data and displaying it in the Task Manager Mobile application was just as easy. Kendo UI Mobile does a great job of rendering the application and making it look good on an Android device. Part 3 will get into editig data.

References


Unless otherwise stated, the content of this page is licensed under Creative Commons Attribution-ShareAlike 3.0 License