Government 2013

Volume 28 Number 10A

Windows 8.1 - Engage Governments and Communities with Open311

By Tim Kulp

Originally, the 311 phone number was designated as a nonemergency call center so that citizens could report issues to local governments or get questions answered. But with the ever-growing presence of smartphones and the ability to access search engines based on GPS location, 311 has evolved primarily for reporting. Open311 (open311.org/learn) is an open standard that’s used for tracking civic issues—such as road closures, abandoned vehicles and similar needs.

The Open311 project aims to provide an asynchronous framework through which citizens and local governments can cooperate and converse. This will help citizens notify local governments of incidents and issues needing attention. And Windows Store apps provide a lot of tools with which you can extend Open311 into a platform for civic engagement, collaboration and social action. 

Working with Open311

Many cities—including San Francisco, Washington, D.C., and Baltimore—are using robust Open311 implementations. (A complete list can be found on the Open311 site at wiki.open311.org/GeoReport_v2/Servers.) These cities provide APIs developers can use to pump their Open311 implementations full of data from users. This data can then be reviewed, analyzed and assessed by anyone with access to the API (cities often provide open access to their APIs). This data can be used to build and budget community improvement projects, alert authorities to persistent problems (such as the presence of graffiti), or praise city employees.

Building apps around the Open311 API is straightforward. Using REST endpoints, you simply request data via a URL. Open311 has three important components:

  1. Service discovery: The service discovery document provides data for an Open311 implementation, such as the endpoint URLs for production, testing and staging environments. The service discovery document also identifies the output formats supported by the Open311 implementation. You can see an example at bit.ly/1aWBtEi.
  2. Services: These are the individual services offered by the Open311 API for a city. Services are organized in categories such as City Employee Praise, Potholes, Graffiti Removal and so on. A complete listing of services is available by providing the service URL without any parameters—for example: https://311.baltimorecity.gov/open311/v2/services.xml. To request the service list in JSON, just change .xml to .json. This updates the output to JSON content instead of XML.
  3. Requests: These are the issues submitted by users of the system. Requests are associated with a service, which categorizes the request by type. People who submit a request can attach media (such as a photo) as well as commentary. Be mindful that the commentary can be colorful at times, and depending on your content filtering, it can be harmful to your application. Always remember to use input validation and sanitization to mitigate risk to your users and application.

Integrating Open311 into Windows Store Apps

For this sample application, I’ll use the Open311 API for my favorite city: Baltimore. I’ll demonstrate a small Windows Store app that lets users browse, submit and share requests with others.

To start, I create a new Windows Store app using the Split App template in Visual Studio 2013. This gives you much of the framework for displaying the Open311 data. In the end, you’ll have an app resembling Figures 1 and 2.

Open311 Windows Store App Service List Screen
Figure 1 Open311 Windows Store App Service List Screen

Open311 Windows Store App Request List/Details Screen
Figure 2 Open311 Windows Store App Request List/Details Screen

Using the Split App template gives me a jump on the app’s design and code. I get the items page (where the service list appears) and the split page (the request list and details). The split page is where most of the action in the application occurs (such as sharing and creating new requests). I need to add more content, but this is a great start.

The Split App template provides the data.js file as a place to hold and acquire data. I need to modify the data.js file and be sure that the right data is coming into the app. This entails cleaning out the data.js file so all that remains is the anonymous function shell. Then, the following code is added directly under the use strict directive:

var serviceList = new WinJS.Binding.List();
var serviceGroups = serviceList.createGrouped(
 function groupKeySelector(item) { return item.group; },
 function groupDataSelector(item) { return item; }
);
var serviceDescription = null;
var requestList = new WinJS.Binding.List();

These initial declarations will hold lists for services and requests. Using a WinJS.Binding.List is like using an array but with more features, such as data binding, sorting and grouping. You implement this code to get the service list so users can identify the service they want to see. For this, I’ll use the new Windows.Web.Http.HttpClient object to make a call to the Open311 service. The code is shown here:

httpClient.getStringAsync(new Windows.Foundation.Uri(
  "https://311test.baltimorecity.gov/open311/v2/services.json"))
  .then(
    function complete(result) {
      var items = JSON.parse(result);
      items.forEach(function (item) {
      item.backgroundImage = "/images/icons/" + item.group + ".png";
      serviceList.push(item);
    });
  }
);

The HttpClient object will be my go-to object for connecting to Open311 and getting (or posting) information. The Windows.Web.Http API is new to Windows 8.1, and while not replacing WinJS.xhr, it provides benefits beyond XHR that make it the preferred API for calling data from Open311. To learn more about this library, check out Peter Smith’s Build 2013 talk: “Five Great Reasons to Use the New HttpClient API to Connect to Web Services” at bit.ly/13SRQ1N

This object works with promises to perform asynchronous actions, in this case waiting for Open311 to return the list of services. (Programming with JavaScript promises is beyond the scope of this article. Find more information about the promise object and working with it at bit.ly/Lh5Pkp.)

For my purposes, I’ll use the then method of the promise object returned by HttpClient.getStringAsync to populate the serviceList. The getStringAsync method returns the response from the service.json page as a string. The string is then passed to the JSON.parse method to convert it into an array of Open311Service objects.

Using JSON.parse is a form of input validation that confirms the content being returned can be converted to a JavaScript object. If the string isn’t JSON formatted, the code raises an error. Historically, eval was used to build JSON objects, but this opened the door for script injection. Use JSON.parse to convert text into a JSON object to avoid executing any unknown code.

Any time code interacts with external services, be ready to catch exceptions. When connecting to an external service (in this case, the Open311 server), there’s a chance the server will be down or unavailable. Exception handling is omitted here, but can be found in the downloadable sample code.

With the JSON data parsed, the code runs through a foreach loop to add a new property to each service item—in this case, a background image. Adding this property enhances the appearance of the service list so there’s a nice grid display with tile images. JavaScript is a prototype-based language, which means that to add a new property, all you need to do is define the property with a value. The service item is then added to the serviceList object—just like adding an item to an array—by using the push method.

With a method to retrieve services, the data.js code now needs a way to retrieve a list of requests associated with a service or services. Requests are the specific incidents reported by the community. Using the Open311 GeoReport standard (wiki.open311.org/GeoReport_v2), there are five parameters a request can support:

  • service_request_id: The specific ID of the request
  • service_code: The ID of the service you want to view (this would be used to return all requests for a specific service_code instance)
  • start_date: Returns all requests since the specified start date
  • end_date: Returns all requests up to the specified end date
  • status: Returns requests that are open or closed (the only two permitted values)

To support building a request, the code takes these parameters and formats a request using their values, as shown in Figure 3.

Figure 3 getRequests Function

function getRequests(serviceRequestId, status, startDate, endDate, serviceCode) {
  var url = "";
  if (serviceRequestId !== ""){
    url = "https://311.baltimorecity.gov/open311/v2/requests.json?jurisdiction_
    id=baltimorecity.gov&service_request_id=" + serviceRequestId;
  }
  else {
    url = "https://311.baltimorecity.gov/open311/v2/requests.json?jurisdiction_
    id=baltimorecity.gov";
    if (startDate !== "")
      url += "&start_date=" + startDate.toISOString();
    if (endDate !== "")
      url += "&end_date=" + endDate.toISOString();
    if (serviceCode !== "")
      url += "&service_code=" + serviceCode;
    if (status !== "")
      url += "&status=" + status;
  }
return httpClient.getStringAsync(new Windows.Foundation.Uri(url))
 .then(
    function complete(results) {
      var items = JSON.parse(results);
      items.forEach(function (item) {
        if (requestList.indexOf(item) == -1) {
          item.backgroundImage = "https://dev.virtualearth.net/REST/v1/Imagery/Map/AerialWithLabels/" + 
            item.lat + "," + item.long + "/15?mapSize=100,100&format=png&pushpin=" + item.lat + 
            "," + item.long + ";65;&key=[BING KEY]";
          requestList.push(item);
        }
      });
  });}

Much of this code is the same as the service request in get­Services. In the complete function of the then method, the requestList is populated with data from Open311 based on the URL’s parameters. For each request item, the Bing Maps API is called for the background image instead of using a standard image from the asset library. This gives the request a point on a map to illustrate the location with more detail.

As you can imagine, the app could do a lot with mapping, and this is where the Bing Maps Beta SDK for Windows 8.1 Store apps would come in. (Learn more about the Bing Maps Beta SDK for Windows 8.1 Store apps at bit.ly/1csyG0G.) To filter which requests are returned, the URL request is built based on the data provided to the function. Some Open311 implementations filter on every parameter provided regardless of whether it has a value. To avoid unintentional filtering here, any parameter that isn’t being used is left off.

With the getServices and getRequests methods defined, the code needs a way to access them. Fortunately, WinJS provides a simple mechanism for creating objects, called WinJS.Namespace.define. An object (in this case “Data”) and the members of the object just need to be specified:

WinJS.Namespace.define("Data", {
  services: serviceGroups,
  serviceGroups: serviceGroups.groups,
  requests: requestList,
  GetServices: getServices,
  GetRequests: getRequests});

Instead of using the serviceList variable, which was populated in the getServices method, the serviceGroups object is used. The serviceGroups object is a “live projection” of the serviceList list. In other words, serviceGroups is a grouped representation of the serviceList. Any changes that occur to the serviceList are propagated to the serviceGroups object so the app can add, remove and update the serviceList without having to reapply the grouping.

Debrief: Engage, Collaborate, Simplify

Open311 provides a communication vehicle for the community to bring social and infrastructure issues to the government. As Gov 2.0 focuses on transparency and trust, Open311 is a perfect model for providing transparency on reported issues and how they’re being resolved. Windows 8.1 allows users to have a more connected experience through the Share contract and the engaging UI.

IT Brief:

With today’s limited budgets and high expectations, you need to get solutions to market quickly with existing skills. By using JavaScript, you can bring native apps to life on Windows 8 with a modest learning curve, helping you ship apps with your existing team.

  • Simplify your app to focus on engagement
  • Leverage existing skill sets to keep development costs down
  • Use the resources available through MSDN and Visual Studio to speed your time to market

Dev Brief:

Working with Web-based APIs such as Open311 isn’t new for most Web developers, but building native apps can be. Using JavaScript and a core set of skills, you can build rich apps that provide engaging experiences. With JavaScript as a first-class code citizen in Windows 8.1, you can develop creative apps with a language you already know.

  • Be more creative with apps by focusing on the app
  • Leverage Windows 8.1 charms to bring new features to your apps easily
  • Work with the Open311 API to dynamically create content in your applications

More Information:

Service List Screen (Items Page)

The Split template provides a few screens for the Windows Store app. The first is the Items page, which is used to list the groups of data in the template. This screen will be updated to use the serviceGroups list. The data bindings will also be updated to render the Service object model.

Changes to Items.html The only update needed here is to the data template used to display the service items. In the items.html page (pages/items/items.html), change the binding fields to reflect the Service object model, as shown here:

<div class="itemtemplate" data-win-control="WinJS.Binding.Template">
  <div class="item">
    <img class="item-image" src="#" data-win-bind="src: backgroundImage;
      alt: service_name" />
    <div class="item-overlay">
      <h3 class="item-title" data-win-bind="textContent: service_name"></h3>
      <h4 class="item-subtitle win-type-ellipsis"
        data-win-bind="textContent: group"></h4>
    </div>
  </div>
</div>

The ListView control uses the template in the main content section to render each service item. For each item, the template displays an image with a text overlay that describes the service. Using the data-win-bind attribute, the code defines which field in the object model to render for each attribute. While the <h3> and <h4> tags are rendering only text, the <img> tag binds the image source and alternate text. You can bind as many attributes as you need by using attribute:field; value.

While on the html page, update the ListView at the bottom of the screen to set data-win-options only to selectionMode: ‘none.’ The other settings will be handled by the items.js code file.

Changes to Items.js Here, again, use the Split template and just tweak the code. First, the services need to be loaded into the Data object. The getServices method defined in the Data object will be called to populate the serviceList, but only if the serviceList is empty. The serviceList is a WinJS.Binding.List object, which has a lot of the same properties as an array. Check the length of the list to avoid unnecessary loading.

Next, in the ready event handler in items.js, update the ListView object to use the Data.services list for its data binding. Finally, update the _itemInvoked method at the bottom of items.js to use the Data.services object instead of the Data.groups object from the template. Complete code changes can be found in the sample code available for download at msdn.com/magazine/msdnmaggov13 and have been omitted here for brevity.

Run the app. The items page lists the Open311 services for Baltimore. The images will be blank because they haven’t been defined in the app yet. (These images are included in the sample code.) In the code, the _itemInvoked method is associated with the onItemInvoked event of the ListView.

When the user taps or clicks an item in the ListView, the groupKey is retrieved from the Data.services list and is passed to the split.html page through the navigate method’s initialState argument. WinJS.Navigation.navigate accepts two arguments: where to go (the string path of the destination page) and any data the new page should receive from the calling page (initialState). In this case, the code passes groupKey to the split.html page, which is the specific Service object activated by the user. The initialState argument of the navigate method lets apps easily pass data from one page to another. Tapping a service at this point will error out because split.js hasn’t yet been updated. I’ll fix that now.

Requests Page (Split Page)

The items.html page redirects to the split.html page and passes along the Service data the user selects. On this screen, the app displays a list of requests for the selected service as well as details about a specific request. Again, the existing split.html page needs to be modified only to implement the specific functionality.

Developing Split.html This page consists of two parts. The first part is the list of requests based on the selected service from the items.html page. The second part is the detailed view of the specific request selected.

Defining the list requires code similar to what I added to the items.html page. First, reference the code sample and update the template in split.html to reflect the Request data model. Next, replace the pre-generated article section with the code sample’s article section to display a specific request item’s detail view. This HTML is updated to the Request data model.

Notice the accessible rich Internet application (ARIA) attributes that appear throughout this code. These attributes are used in ARIAs for assistive technologies. ARIA attributes are one of the semantic enhancements in HTML5. Many government organizations have strict accessibility standards for their Web sites and media. You can learn more about the ARIA attributes at bit.ly/19kuuyd.

Finally, AppBar is added to the page. AppBar is where users create a request to send to Open311. A single Add button allows users to add a new request, but other buttons can be added later, such as Refresh to pull updates from the server:

<div id="commandsAppBar" data-win-control="WinJS.UI.AppBar" 
  data-win-options="">
  <button data-win-control="WinJS.UI.AppBarCommand"
    data-win-options="{id:'cmdAdd',label:'Add',icon:'add',
    section:'global',tooltip:'Add new
    request'}"></button>
</div>

One of the great things about the AppBar class is that it provides a common area for locating app commands. This keeps your app’s interface clear of distraction or clutter. For this app, users might want to just browse and not post. To support this experience, place the add functionality in a standard location (the AppBar) away from users who don’t want it.

Changes to Split.js To bring these views to life, reconfigure the codebehind page to work with the Data.requests object.

First, at the top of the page, under the declaration of the utils variable, add two new variables the code will reference:

var appBar = null;
var serviceItem = null;

Now set up the code to bind the ListView to the requests for the selected service. Start by setting the serviceItem to the data that comes from items.html. Notice the ready function has two arguments: element and options. Options is the data sent from the previous page and contains the groupKey object from items.html. In the ready function defined in the split.js file, set the serviceItem to the groupKey:

serviceItem = options.groupKey;

Throughout the JavaScript code, the serviceItem provides context for which requests the app needs to display to the user. Set the title of the page to reflect the service selected by the user:

element.querySelector("header[role=banner] .pagetitle").textContent =
  serviceItem.service_name;

Now call the getRequests method of the Data object to pull the requests for the Service object. The following function calls for all open requests for the selected service. Add this call at the top of the ready event handler in split.js:

Data.GetRequests("", "open", "", "", serviceItem.service_code);

As the user moves between services, the request list grows with data for the various services. Instead of clearing out the requests each time the user comes to the split page, just build a filter to display only the data for the selected service. By using createFiltered and createSorted (see Figure 4), another live projection (as with the serviceGroups) is created on top of the WinJS.Binding.List object. As data populates the list, the filtering and sorting projections remain so that they don’t need to be reapplied after the data is loaded.

Figure 4 Create Filtered Projection for Requests

this._items = Data.requests.createFiltered(
  function filterList(item){
    if (item.service_code == serviceItem.service_code)
      return true;
    else
      return false;
  }).createSorted(
  function sorter(item1, item2) {
    var date1 = item1.requested_datetime;
    var date2 = item2.requested_datetime;
    if (date1 == date2)
      return 0;
    else if (date1 < date2)
      return 1;
    else
      return -1;
  });

The createFiltered method loops through each item and checks the service_code value of the Request object to determine whether it matches the service_code of the serviceItem. If it does, the Request object is represented in the projection. If not, the projection ignores the entry. The filtered list is then sorted by comparing the requested_datetime of each item to determine placement in the projection, with the most recent date displayed at the top of the list to create a descending order for the requests.

The request list and details sections are now provided with data. By using the existing code in the split page and updating just the pieces needed for the data model, you have working code very quickly. As a last step at this point, add the cmdAdd_click callback to the bottom of the split.js page, right above the closure of the anonymous function:

function cmdAdd_Click(e) {
  WinJS.Navigation.navigate("/pages/create/create.html",
    { serviceItem: serviceItem });
  }

Associate this function with the cmdAdd button’s click event in the ready function of the split.js page, and the page is ready to be tested:

appBar = document.getElementById("commandsAppBar").winControl;
appBar.getCommandById("cmdAdd").addEventListener("click", cmdAdd_Click, false);

Creating Service Requests

Users need to be able to create a request for a service and send it to the Open311 framework. Different service requests have different data points that need to be defined, so the app must dynamically render controls based on the type of service. Using the service definition from Open311, you can determine what data fields need to be provided, whether a field is required and possible values.

First, add a method to the Data object in the data.js file, called getServiceDescription, with the following code:

function getServiceDescription(serviceCode) {
  return httpClient.getStringAsync(
    new Windows.Foundation.Uri(
      "https://311test.baltimorecity.gov/open311/v2/services/" +
      serviceCode + ".json?jurisdiction_id=baltimorecity.gov"));
 }

Here, the HttpClient.getStringAsync method’s promise object is returned. You can use this to determine how to render the service description. For the Windows Store app, the code takes the result and renders appropriate controls. To make this function available to other pages, make sure to add it to the WinJS.Namespace.define call at the end of data.js as getServiceDescription.

Create a new folder under pages and name it “create.” In that folder, add a Page control named create.html to use for submitting a request to Open311. Requests include some standard information, represented here along with a “custom-controls” div populated from the service definition. Use the HTML in Figure 5 for the input form.

Figure 5 HTML for Adding a Request

<h2>Adding a request: <span id="serviceName"></span></h2>
  <form id="service-description">
    <p>
      <label for="txtAddress">Address</label>
      <input type="text" id="txtAddress" aria-label="Address for the request"/>
    </p>
    <p>
      <label for="txtDescription">Description</label>
      <textarea id="txtDescription" aria-label="Request description"
        rows="6" cols="45"></textarea>
    </p>
    <div id="custom-content"></div>
    <fieldset>
      <legend>Your contact information (OPTIONAL)</legend>
      <ul>
        <li>
          <label for="txtFN">First Name</label>
          <input type="text" id="txtFN" aria-label="First Name of requestor"/>
        </li>
        <li>
          <label for="txtLN">Last Name</label>
          <input type="text" id="txtLN" aria-label="Last Name of requestor"/>
        </li>
        <li>
          <label for="txtEmail">Email Address</label>
          <input type="email" id="txtEmail"
            aria-label="Email address of requestor"/>
        </li>
        <li>
          <label for="txtPhone">Phone Number</label>
          <input type="tel" id="txtPhone" aria-label="Phone number of requestor"/>
        </li>
      </ul>
    </fieldset>
    <p>
      <input type="button" id="btnSend" aria-label="Save" value="Send Request" />
    </p>
  </form>

Now, open create.js, and add the following declarations within the immediately invoked anonymous function, under the use strict directive:

var serviceItem = null;
var locator = null;
var lat;
var long;

Open311 is an ideal location-awareness technology for tracking specifically where an issue occurs. To support this, the app uses a GeoLocator object to get the latitude and longitude of where the incident is reported. In cases when users don’t want to provide their exact location, the address field can be populated manually. Inside the ready function, add the event listener for the btnSend button and connect the locator so that it polls a user’s location information. In the example shown in Figure 6, the location is pulled in the ready function so this information is gathered behind the scenes while the user fills out the request form.

Figure 6 Pulling Location Information

var btnSend = document.getElementById("btnSend");
btnSend.addEventListener("click", btnSend_Click, false);
locator = new Windows.Devices.Geolocation.Geolocator();
var locatorPromise = locator.getGeopositionAsync();
locatorPromise.then(
  function complete(position) {
    lat = position.coordinate.latitude;
    long = position.coordinate.longitude;
  },
  function error(ex) {
    // Handle the error
});

Using getGeopositionAsync, the code returns a Geoposition object with a user’s location. This location is derived from a GPS device or the user’s Internet connection. When this function is called, the user is prompted to allow or deny the app’s access to location information.

In Open311, the user’s location must be specified, but this can be done with latitude and longitude or an address. Anytime your app needs data to which a user could deny access, be sure you build an alternative mechanism that’s available should this denial occur. Here, the app has the address field for when location information is denied.

The previous page (split.js) sent create.js the service data. Now you can check the metadata of the service data object to see whether this service item has any additional fields beyond the basic ones. This code is shown in Figure 7.

Figure 7 Check for Service Item Metadata and Get Service Description

serviceItem = options.serviceItem;
if (serviceItem.metadata) {
  Data.GetServiceDescription(serviceItem.service_code).done(
    function complete(result) {
      if (result.status == 200) {
        var serviceDescription = JSON.parse(result.responseText);
        var dynamicObject = document.getElementById("custom-content");
        serviceDescription.attributes.forEach(function (attribute) {
        if (attribute.variable) {
          // ...
        }
      });
    }
  });
}

As you can see, a pattern that’s used elsewhere in the app is being used. In this case, retrieve the serviceDescription and loop through each of the attribute elements to render the control. The code also checks to see whether an attribute’s value is variable—not a fixed value. As an example, for graffiti reports, one of the attributes is “surface,” which is a variable that can be set to a variety of values, such as concrete, brick and so on. Attributes also have data types, such as string, number, datetime, text (as in textarea), singlevaluelist (a dropdown list) and multivaluelist (cascading dropdown lists). Replace the ellipsis in Figure 7 with the code in Figure 8.

Figure 8 Specific Data Types for a Variable

var rootElement;
switch (attribute.datatype) {
  case "string":
    // Create a label and input box with type text
    break;
  case "number":
    // Create a label and input box with type number
    break;
  case "datetime":
    // Create a label and input box with type datetime
    break;
  case "text":
    // Create a label and a textarea
    break;
  case "singlevaluelist":
    // Create a label and a select list
    break;
  case "multivaluelist":
    // Create a label and select list for each item
    break;
}

Creating these controls is simple and is a familiar operation for JavaScript developers. Replace the comment under the string case with the following:

rootElement = document.createElement("p");
var textBox = document.createElement("input");
textBox.setAttribute("type", "text");
textBox.setAttribute("data-attribute-code", attribute.code);
textBox.id = "input" + attribute.code;
if (attribute.required)
  textBox.setAttribute("required", "required");
var label = document.createElement("label");
label.textContent = attribute.description;
rootElement.appendChild(label);
rootElement.appendChild(textBox);

This is straightforward DOM element creation with values assigned. One difference is that you create your own data attribute to store the attribute object’s code value. The code is used later to identify the value of this attribute when the request is submitted to the server. To make the value easier to pull, a standardized value— “data-attribute-code”—is created so it can be queried. While this simple text box is easy to build, the most common attribute data type for Baltimore is singlevaluelist, which renders as a select box. Replace the singlevaluelist comment text with the code in Figure 9.

Figure 9 Control Code

rootElement = document.createElement("p");
var select = document.createElement("select");
select.id = "select" + attribute.code;
var label = document.createElement("label");
label.textContent = attribute.description;
rootElement.appendChild(label);
attribute.values.forEach(function (value) {
  var option = document.createElement("option");
  option.value = key;
  option.text = value;  select.appendChild(option);
});
select.setAttribute("data-attribute-code", attribute.code);
rootElement.appendChild(select);

One notable difference in this code and the code for a text box is the use of the values collection of the Open311 attribute object. This is a sequence of key/name pairs that provide a value (key) with text to describe the value (name). In the code in Figure 9, the createOption function simply takes the key and name properties of the value object and outputs an option tag with the value set to the key and the text set to the name.

At the end of the switch statement, add the rootElement object to the dynamicObject object to render the controls to the screen:

document.getElementById("custom-content").appendChild(rootElement);

The result should look something like Figure 10.

Open311 Windows Store App Create Request Screen
Figure 10 Open311 Windows Store App Create Request Screen

With this form, users can enter data and click Send Request to invoke the btnSend_click function. To make this function do something, you need to add one last function to the Data namespace: PostRequest. To cut down on arguments, use an options object as a single argument with multiple properties. Add the code in Figure 11 to the data.js file.

Figure 11 postRequest function

function postRequest(options) {
  var data = {
    api_key: "[YOUR OPEN311 API KEY]",
    service_code: options.serviceCode,
    lat: options.lat,
    long: options.long,
    address_string: options.address_string,
    address_id: options.address_id,
    email: options.email,
    device_id: options.device_id,
    account_id: options.account_id,
    first_name: options.first_name,
    last_name: options.last_name,
    phone: options.phone,
    description: options.description,
    media_url: options.media_url
  }
    options.attributes.forEach(function (attribute) {
      data["attribute[" + attribute.code + "]"] =
      encodeURIComponent(attribute.value);
    });
    var strData = "";
    for (var key in data) {
      if (data.hasOwnProperty(key)) {
        if(data[key] !== "")
          strData += key + "=" + data[key] + "&";
      }
    }
    var httpContent = new Windows.Web.Http.HttpStringContent(strData,
      Windows.Storage.Streams.UnicodeEncoding.utf8,
      "application/x-www-form-urlencoded;
      charset=utf-8");
    return httpClient.postAsync(new
      Windows.Foundation.Uri(
      "https://311test.baltimorecity.gov/open311/v2/requests.json"),
      httpContent).then(
      function complete(result) {
      // Do something with the result
        var something = result;
      },
      function error(ex){
      var e = ex;  
    });
  }

First, the code builds a data object that’s used to hold the values of the parameters you want to post to Open311. Then you need to include all the attributes in the format “attribute[CODE]=KEY.” To do this, the code loops through the attributes collection (the attributes value is an array of attribute objects) and then appends the attribute to the JSON object.

To send the data to the server, the code needs to be in a URL format, so each key of the JSON object is looped through and the value is written out as property name = property value. This constructs the data string passed to the server as form data. Finally, the HttpClient.PostAsync method is set up to send the data to the server. For Open311 to post, Content-Type must be set to application/x-www-form-urlencoded, as it is in the code sample.

The postRequest function returns the HttpClient.PostAsync promise object the app can interact with further. After posting data to request.json results, the token value of the service request is received from the Open311 server. You can then use this token to look up the details for the request. By adding a then function to the postRequest function, you can build additional actions and workflows from the returned token value. As an example, the app might navigate to another screen when the token value returns or load the request data for the submitted request.

To complete the sample, register the postRequest function with the Data namespace and add the event handler code for btnSend in create.js, shown in Figure 12.

Figure 12 btnSend_Click Signature

function btnSend_Click(e) {
  var description = document.getElementById("txtDescription").innerText;
  var address = document.getElementById("txtAddress").value;
  if ((lat == undefined || long == undefined) && address == "")
    return; // Display an error message to the user
  var options = {};
  options.first_name = document.getElementById("txtFN").value;
  options.last_name = document.getElementById("txtLN").value;
  options.email = document.getElementById("txtEmail").value;
  options.phone = document.getElementById("txtPhone").value;
  options.attributes = new Array();
  var attributeCollection = document.querySelectorAll("[data-attribute-code]");
  for (var i = 0; i < attributeCollection.length; i++) {
    var value = document.getElementById(attributeCollection[i].id).value;
    var code = document.getElementById(attributeCollection[i].id)
      .getAttribute("data-attribute-code");
    options.attributes.push({ code: code, value: value });
  }
  options.address_string = address;
  options.address_id = "";
  options.lat = lat;
  options.long = long;
  options.device_id = "";
  options.account_id = "";
  options.media_url = "";
  options.description = description;
  options.serviceCode = serviceItem.service_code;       
  Data.PostRequest(options);

With all this input acceptance, one thing that’s missing is validation. Always validate input to ensure you receive known good data from users. While I’ve omitted vali­dation code here for brevity, it’s included in the sample code. This code also uses the querySelectorAll function to find all the elements with a data-attribute-code attribute to easily access the dynamic input elements built-in to the ready function.

What About Privacy?

By using the Open311 standard, users submit data to a specific city, just as though they were reporting the data to the city over the phone. What the city does with that data is up to the city, but developers of Windows Store apps need to include a privacy statement that outlines exactly what’s happening with the data their apps collect.

Think of building an app for Facebook. The app can collect data, but it will store some of that data on Facebook. After the data goes to Facebook, Facebook can do what it wants to with the data (in conformance with its own usage policies). When you develop and manage apps with Open311, be sure the privacy statement clearly indicates that users are providing data to the specific city for the city’s use.

Gov 2.0: Build Trust Through Collaboration

Open311 is an excellent model for Gov 2.0 as local governments try to engage their communities in social issues. It illustrates how Gov 2.0 lets the community directly communicate with government through mobile technologies.

Simple reporting tools, with built-in opportunities to engage friends and other community members, lets users report issues and spread the word. By using a Visual Studio template, you can easily connect Open311 to an existing UI, helping to reduce development costs and speed to market. Windows 8.1 provides a wealth of opportunities for this app, such as using the Windows.Storage.Pickers.FileOpenPicker class to attach a media URL to the request, integrating the Search charm to allow users to find requests from anywhere, and quickly building rich interactions with Windows Azure. Open311 and some basic JavaScript skills can provide a deeply engaging experience for your community, enabling the community and its government to collaborate on important social issues.


Tim Kulp leads the development team at United Healthcare International in Baltimore. You can find Kulp on his blog at seccode.blogspot.com or on Twitter at twitter.com/seccode, where he talks code, security and the Baltimore foodie scene.

Thanks to the following technical expert for reviewing this article: Eric Schmidt (Microsoft)