Getting started with TimelineJS

Posted by Joe Wilson on Friday, May 10, 2013 7:49 AM

I volunteered at Colorado GiveCamp in April.  It’s a weekend long hackathon for a charity.  The charity I worked with, GetSmartSchools, provides classes and certifications to people who are wanting to jump into school administration.

They needed a calendar/timeline to show the commitment they are asking of people who sign up.  I’ve done something similar to this before, but it was all custom code – custom colors, text, hover text, links, etc.  At a weekend long hackathon and with a charity using a CMS for their web site, that approach wasn’t going to work.

So I did what all modern web developers do.  I looked on Google to see if someone had created a JavaScript library I could just drop in.

Introducing TimelineJS

I stumbled upon TimelineJS and thought it was so cool it would be worth talking about.  It looks like this:

You can scroll or arrow key left/right to go backwards or forwards in time.  You can zoom in or out with the plus and minus buttons.  You can host and play media in the timeline points from YouTube, Vimeo, Vine, Instagram, Flickr, SoundCloud, Twitter, Wikipedia, Google Maps, or any web page.

Quick start

The TimelineJS build page holds your hand through the process of creating a timeline.  The audience here is people who aren’t web developers, but it’s a quick way to get something working and get feedback before investing more time.

The first step is to get some data.  They walk you through using a Google Spreadsheet and give you a template with the columns you would expect: start date, end date,caption text, and columns for media, which can be a hyperlink to media content or a web page.

Once you’ve got your spreadsheet filled out, you click “Publish to the Web”.

Then you copy that URL into the TimelineJS build page as the data source for your timeline.

Next, you can set a bunch of options to make the timeline your own.  The builder has choices for size, fonts, map API keys, flipping the order so the newest stuff is the first thing you see, wrapping the embed code as a WordPress Plugin, and more.

Finally, you are given some code to embed in your blog or CMS and you even can preview how your timeline will look on your site.

<iframe src='http://embed.verite.co/timeline/?source=0Agl_Dv6iEbDadHdKcHlHcTB5bzhvbF9iTWwyMmJHdkE&font=Bevan-PotanoSans&maptype=toner&width=600&height=600' width='600' height='600' frameborder='0'></iframe>

It’s an iframe that points to http://embed.verite.co, so if that goes down, so does your timeline.  If that bugs you or you just aren’t a fan of iframes, you can go the developer route.

Developer options

If an iframe is not your style or you’ve already got a JSON data source, you can still use TimelineJS.  Here’s what the basic HTML and JavaScript might look like:

    <head>
        <!-- jQuery -->
        <script type="text/javascript" src="http://ajax.googleapis.com/ajax/libs/jquery/1.7.2/jquery.min.js"></script>
        <!-- BEGIN TimelineJS -->
        <script type="text/javascript" src="path_to_js/storyjs-embed.js"></script>
        <script>
            $(document).ready(function() {
                createStoryJS({
                    type:       'timeline',
                    width:      '800',
                    height:     '600',
                    source:     'path_to_json/or_link_to_googlespreadsheet',
                    embed_id:   'my-timeline'
                });
            });
        </script>
        <!-- END TimelineJS -->
    </head>
    <body>  
        <div id="my-timeline"></div>
    </body>

The Source is the link to your JSON or your published Google Spreadsheet.  The Embed_Id is the ID of the element the timeline will go into in the HTML (“my-timeline” in this case).  There are more options listed on the GitHub readme.

Here’s what the sample JSON would look like (with the full model represented):

{
    "timeline":
    {
        "headline":"The Main Timeline Headline Goes here",
        "type":"default",
        "text":"<p>Intro body text goes here, some HTML is ok</p>",
        "asset": {
            "media":"http://yourdomain_or_socialmedialink_goes_here.jpg",
            "credit":"Credit Name Goes Here",
            "caption":"Caption text goes here"
        },
        "date": [
            {
                "startDate":"2011,12,10",
                "endDate":"2011,12,11",
                "headline":"Headline Goes Here",
                "text":"<p>Body text goes here, some HTML is OK</p>",
                "tag":"This is Optional",
                "classname":"optionaluniqueclassnamecanbeaddedhere",
                "asset": {
                    "media":"http://twitter.com/ArjunaSoriano/status/164181156147900416",
                    "thumbnail":"optional-32x32px.jpg",
                    "credit":"Credit Name Goes Here",
                    "caption":"Caption text goes here"
                }
            }
        ],
        "era": [
            {
                "startDate":"2011,12,10",
                "endDate":"2011,12,11",
                "headline":"Headline Goes Here",
                "text":"<p>Body text goes here, some HTML is OK</p>",
                "tag":"This is Optional"
            }

        ]
    }
}

Other details

  • It works with modern browsers and IE8 and up.
  • The TimelineJS folks say putting more than 20-30 items in your timeline will slow it down.   You don’t want to overwhelm the user with information anyway.
  • The licensing is Mozilla Public License, v2.0.
  • It’s open source and the source code is hosted on GitHub.

Tags: JavaScript
Categories: Technical

Setting default values for Kendo UI and DataTables controls

Posted by Joe Wilson on Monday, April 1, 2013 2:57 PM

Most JavaScript controls take a JSON object for their settings.  Here’s how you initialize a Kendo UI grid:

$("#myGrid").kendoGrid({
    pageable: true,
    sortable: { mode: "multiple", allowUnsort: true },
    filterable: true,
    dataSource: {
        data: myData,
        pageSize: 10
    }
});

And here’s how you initialize a DataTables grid:

$("#myGrid").dataTable({
    "bSort": true,
    "bAutoWidth": false,
    "bProcessing": true,
    "bJQueryUI": true,
    "bPaginate": true,
    "bServerSide": true,
    "bSortCellsTop": true,
    "sPaginationType": "full_numbers",
    "iDisplayLength": 25,
    "aLengthMenu": [[5, 10, 25, 100], [5, 10, 25, 100]],
    "aaSorting": [],
    "sAjaxSource": "/Api/MyData",
    "sAjaxDataProp": "Data"
});

Both controls take an object literal with all the settings to style how the control looks, works, etc.

But look at all those settings!  We have to set all that sorting and paging and filtering every time if we want non-default behavior?  And what if we change our mind later and want to use virtual paging instead of page numbers or another application-wide change? 

If there was only a way to centralize the common settings and reuse them with each call…

Reuse common settings

Here’s a snippet of JavaScript to store common settings for a Kendo UI grid:

var kendoGridDefaults = {
    filterable: {
        pageable: true,
        sortable: { mode: "multiple", allowUnsort: true },
        filterable: true
    },
    nonFilterable: {
        pageable: true,
        sortable: { mode: "multiple", allowUnsort: true },
        filterable: false
    },
    extend: function (defaults, settings) {
        return $.extend(true, {}, this[defaults], settings);
    }
};

And here are some common settings for a DataTables grid:

var dataTableDefaults = {
    serverSide: {
        "bSort": true,
        "bAutoWidth": false,
        "bProcessing": true,
        "bJQueryUI": true,
        "bPaginate": true,
        "bServerSide": true,
        "bSortCellsTop": true,
        "sPaginationType": "full_numbers",
        "iDisplayLength": 25,
        "aLengthMenu": [[5, 10, 25, 100], [5, 10, 25, 100]],
        "aaSorting": []
    },
    extend: function(defaults, settings) {
        return $.extend(true, {}, this[defaults], settings);
    }
};

In both cases, you build up an object literal with with the common settings you want to reuse, then create an “extend” function (or another name, if you like) using the jQuery $.extend function, which merges your default settings with any new settings you pass in.

More than one set of common settings

You will likely have more than one set of reusable settings (server-side grid and client side grid, filterable and non-filterable grids, etc.).  Just define new objects in your settings object.  Above, I’ve got “filterable”, “nonFilterable”, and “serverSide” as property sets.

Usage

To take advantage of the default settings, call your control code and pass in the common settings object, then call the extend function with your overrides and/or additional settings:

$("#myGrid").kendoGrid(kendoGridDefaults.extend("filterable", {
    dataSource: {
        data: myData,
        pageSize: 10
    }
}));
$("#myGrid").dataTable(dataTableDefaults.extend("serverSide", {
    "sAjaxSource": "/api/myData",
    "sAjaxDataProp": "Data"
}));

This technique isn’t limited to grids, or to Kendo UI or DataTables.  It can be used with any controls that set their properties with a JavaScript object literal.

Tags: Kendo UI, DataTables, JavaScript, jQuery
Categories: Technical

Combining JavaScript bundling, minification, cache busting, and easier debugging

Posted by Joe Wilson on Monday, March 18, 2013 9:12 AM

ASP.NET MVC has had server-side bundling and minification for a couple versions now.  You can use this to reduce HTTP requests from the client browser to the web server.  This optimization is a must when you get lots of small JavaScript files, which is what most large web applications have these days.

Here’s the default BundleConfig.cs in an MVC4 default project:

using System.Web;
using System.Web.Optimization;

namespace BundlingSample
{
    public class BundleConfig
    {
        // For more information on Bundling, visit http://go.microsoft.com/fwlink/?LinkId=254725
        public static void RegisterBundles(BundleCollection bundles)
        {
            bundles.Add(new ScriptBundle("~/bundles/jquery").Include(
                        "~/Scripts/jquery-{version}.js"));

            bundles.Add(new ScriptBundle("~/bundles/jqueryui").Include(
                        "~/Scripts/jquery-ui-{version}.js"));

            bundles.Add(new ScriptBundle("~/bundles/jqueryval").Include(
                        "~/Scripts/jquery.unobtrusive*",
                        "~/Scripts/jquery.validate*"));

            // Use the development version of Modernizr to develop with and learn from. Then, when you're
            // ready for production, use the build tool at http://modernizr.com to pick only the tests you need.
            bundles.Add(new ScriptBundle("~/bundles/modernizr").Include(
                        "~/Scripts/modernizr-*"));

            bundles.Add(new StyleBundle("~/Content/css").Include("~/Content/site.css"));

            bundles.Add(new StyleBundle("~/Content/themes/base/css").Include(
                        "~/Content/themes/base/jquery.ui.core.css",
                        "~/Content/themes/base/jquery.ui.resizable.css",
                        "~/Content/themes/base/jquery.ui.selectable.css",
                        "~/Content/themes/base/jquery.ui.accordion.css",
                        "~/Content/themes/base/jquery.ui.autocomplete.css",
                        "~/Content/themes/base/jquery.ui.button.css",
                        "~/Content/themes/base/jquery.ui.dialog.css",
                        "~/Content/themes/base/jquery.ui.slider.css",
                        "~/Content/themes/base/jquery.ui.tabs.css",
                        "~/Content/themes/base/jquery.ui.datepicker.css",
                        "~/Content/themes/base/jquery.ui.progressbar.css",
                        "~/Content/themes/base/jquery.ui.theme.css"));
        }
    }
}

This approach works well for 3rd party libraries (jQuery, jQuery validation, knockout, etc.), and it has a cache-busting hash in the URL that keeps you from caching an old version of the bundled and minified script in your browser.

Here you can see jquery and modernizr have a query string after the file name with the unique key:

image

So we’ve got bundling of scripts, minification, and cache-busting.  What’s not to love?

Where do I put my application scripts?

The default MVC project gives you a \Scripts folder.  You could put your application scripts there.  NuGet would like you to leave 3rd party scripts there, and it’s the convention, but I prefer to move scripts I didn’t write for the current app into a \Scripts\lib folder, and scripts for the app go in a \Scripts\app folder.

image

You’ll have some moving around to do when you update a JavaScript via NuGet to get it in your preferred folder, but it’s a little easier to find the stuff you’ll really be working on for your app.  You’ll also need to update your BundleConfig.cs to point to the new \Scripts\lib folder.

So how to we bundle these app scripts?  You could bundle them all in one file.  Everything in that folder gets put in a "~/bundles/myApp” bundle.  The problem is, you’ll likely end up with lots of sequencing problems in the scripts and see “undefined” errors.  This could work if you only have a small amount of JavaScript in your app, but it’s not recommended.

You could also create a Razor section in the _Layout.cshtml and call it “scripts”, then each view can list the scripts it needs to work in that Razor section.  The default MVC4 _Layout.cshtml file already has this Razor section set up for you.  This approach works, but now we’re not bundling or minifying the JavaScript, and we don’t have any cache busting going on with our app scripts.

So I have to debug minified files?  Yuck.

The other drawback of using the built-in ASP.NET optimizations for bundling and minification is debugging minified files.  You’ve probably dealt with this when your JavaScript throws an exception and you’re in the middle of a jQuery file.  JQuery almost certainly doesn’t have a bug, but you sent it some values it wasn’t expecting.  Have fun sorting that out.

image

IE10 and Chrome have JavaScript pretty print tools that can make the minified script a little easier to read than one long line, but the variable names will all still be a, b, c, and whatever the minifier hadn’t already used. 

Using map files is a better way to go, and in Chrome, you turn this on in dev tools and click the gear at the bottom right, then check “Enable source maps”.

image

Now each minified file can have a map file that points to the original, unminified source code.  The built-in ASP.NET optimizations don’t work with map files yet.

Web Essentials bundling to the rescue

If you are a Visual Studio web developer and you don’t have Web Essentials, go get it now.  You’ll love it.  It’s a Visual Studio extension created my Mads Kristensen, who works for Microsoft for the Web Platform and Tools group.

One of the features is bundling and minification.  You select the files you want to bundle, right click, then choose Web Essentials > Create JavaScript bundle file.

image

Name your bundle, and several files will be created.  The first is the *.bundle file:

<?xml version="1.0" encoding="utf-8"?>
<bundle minify="true" runOnBuild="true">
  <!--The order of the <file> elements determines the order of them when bundled.-->
  <file>/Scripts/app/script1.js</file>
  <file>/Scripts/app/script2.js</file>
</bundle>

This file lists each script in the bundle and has flags for minify (default is “true”) and runOnBuild (default is “true”).  You may need to rearrange the order of the bundle files if there are functions defined in one file and called in another.

Any change you make to script1.js or script2.js, the *.bundle file, or building your app, will regenerate the files underneath the *.bundle file:

image

These three files are 1) the combined bundle without minification, 2) the bundle with minification, and 3) the map file for debugging the original script.  The first is a giant concatenation of the two files and isn’t that interesting.

The minified file looks like this:

function add(n,t){return n+t}function subtract(n,t){return n-t}function showMessage(n){alert(n)}
//@ sourceMappingURL=myAppBundle.min.js.map

And the map file looks like this:

{
"version":3,
"file":"myAppBundle.min.js",
"lineCount":1,
"mappings":"AAAAA,SAASA,GAAG,CAACC,CAAC,CAAEC,CAAJ,CAAO,CACf,OAAOD,CAAE,CAAEC,CADI,CAInBC,SAASA,QAAQ,CAACF,CAAC,CAAEC,CAAJ,CAAO,CACpB,OAAOD,CAAE,CAAEC,CADS,CCJxBE,SAASA,WAAW,CAACC,CAAD,CAAM,CACtBC,KAAK,CAACD,CAAD,CADiB",
"sources":["/Scripts/app/script1.js","/Scripts/app/script2.js"],
"names":["add","x","y","subtract","showMessage","msg","alert"]
}

So the minified file gives the browser instructions on how to get to the map file, which gives the browser instructions on how to render in unminified, made-for-humans format.

Using the bundles

I create a bundle of app scripts for each view (usually I name it after the view, like register.bundle, login.bundle, etc.) and use the Razor “scripts” section in the _Layout.cshtml file to place those scripts at the bottom of the html, just before the closing <body> tag.  The script reference points to the minified file.

@section scripts {
    <script src="~/Scripts/app/myAppBundle.min.js"></script>
}

In Chrome, the end result is this for the minified file:

image

And this for the mapped file:

image

This human-readable script can have breakpoints, etc.  It looks like the original file, but the browser has not created new HTTP requests to pull down the originals.  It’s map file magic.

image

Setting up view-specific bundles seems lighter weight to me than always editing the BundleConfig.cs file, plus you get the mapping files.  Now we just need to solve for the cache busting we lost with this approach.

Cache busting

Ever send your product owner to the CI web server to check out a new feature, only to have them tell you they got JavaScript errors on that page?  Did you tell them to type CTRL-F5 to force a refresh of the page?  If so, you have a caching problem where the browser is caching the file even though it has changed.

Mads has a blog post on cache busting that works well.  It’s based on getting the last changed date of the file and appending that date in ticks to the path of the request for the script or css.  His solution involves having URL rewriting working on IIS. 

In my case, I didn’t want to mess with URL rewriting, so I went with the somewhat less optimal but simpler solution of using a query string instead.  Here’s my Razor helper:

using System.IO;
using System.Web;
using System.Web.Caching;
using System.Web.Hosting;

namespace BundlingSample.Extensions
{
    public static class StaticFile
    {
        public static string Version(string rootRelativePath)
        {
            if (HttpRuntime.Cache[rootRelativePath] == null)
            {
                var absolutePath = HostingEnvironment.MapPath(rootRelativePath);
                var lastChangedDateTime = File.GetLastWriteTime(absolutePath);

                if (rootRelativePath.StartsWith("~"))
                {
                    rootRelativePath = rootRelativePath.Substring(1);
                }

                var versionedUrl = rootRelativePath + "?v=" + lastChangedDateTime.Ticks;

                HttpRuntime.Cache.Insert(rootRelativePath, versionedUrl, new CacheDependency(absolutePath));
            }

            return HttpRuntime.Cache[rootRelativePath] as string;
        }
    }
}

and how it’s used in the Razor view:

@section scripts {
    <script src="@StaticFile.Version("~/Scripts/app/myAppBundle.min.js")"></script>
}

and the resulting file name in the browser:

image

The query string pattern matches the built-in ASP.NET optimization pattern.  I say this is sub-optimal because some page speed tools will fuss at you for using query strings.  Google PageSpeed says, “Enabling public caching in the HTTP headers for static resources allows the browser to download resources from a nearby proxy server rather than from a remote origin server.”  I’m not too worried about that, but you’ll want to use Mads’ URL rewrite approach if you want a better PageSpeed score.

So there you have it.  Bundling, minification, debugging, and cache busting when you need it!

Tags: JavaScript, Web Essentials, optimizations, ASP.NET, MVC
Categories: Technical

Using Kendo UI grid with Web API and OData

Posted by Joe Wilson on Tuesday, February 19, 2013 10:48 PM

The Kendo UI grid is a snazzy little thing:

image

The grid works natively with OData, but the examples from the Kendo team are of OData services driven by WCF back ends.  I wanted to get the grid working with Web API and minimal coding to implement server-side sorting, filtering, and paging.

Until earlier this week, if you wanted to do that, you were on your own.  There was a partial OData implementation in Web API via a NuGet package, but it didn’t support $inlinecount, which is what the grid needs to know how many items there are total (e.g., the $inlinecount for the grid above is 830).

A more fully supported Web API OData implementation was rolled out this week and is available via NuGet.  Here are the steps to get it working with a Kendo UI grid.

Server side

Open your existing Web API project and install the Web API OData NuGet update:

image

Decide what data you want to send to the browser and model it.  I’ve got a Cat entity and a Dog entity:

namespace KendoGridWebApiOdata.Models
{
    public class Cat
    {
        public int Id { get; set; }
        public string Name { get; set; }
        public string Color { get; set; }
    }
}
namespace KendoGridWebApiOdata.Models
{
    public class Dog
    {
        public int Id { get; set; }
        public string Name { get; set; }
        public string Color { get; set; }
    }
}

Update your WebApiConfig.Register() method (in the App_Start folder):

using System.Web.Http;
using System.Web.Http.OData.Builder;
using KendoGridWebApiOdata.Models;

namespace KendoGridWebApiOdata.App_Start
{
    public static class WebApiConfig
    {
        public static void Register(HttpConfiguration config)
        {
            var modelBuilder = new ODataConventionModelBuilder();
            modelBuilder.EntitySet<Cat>("Cats");
            modelBuilder.EntitySet<Dog>("Dogs");
            var model = modelBuilder.GetEdmModel();

            config.Routes.MapODataRoute(
                routeName: "OData",
                routePrefix: "api",
                model: model
                );

            config.Routes.MapHttpRoute(
                name: "DefaultApi",
                routeTemplate: "api/{controller}/{id}",
                defaults: new { id = RouteParameter.Optional }
                );

            // Uncomment the following line of code to enable query support for actions with an IQueryable or IQueryable<T> return type.
            // To avoid processing unexpected or malicious queries, use the validation settings on QueryableAttribute to validate incoming queries.
            // For more information, visit http://go.microsoft.com/fwlink/?LinkId=279712.
            config.EnableQuerySupport();

            // To disable tracing in your application, please comment out or remove the following line of code
            // For more information, refer to: http://www.asp.net/web-api
            config.EnableSystemDiagnosticsTracing();
        }
    }
}

You’ll define the OData models you want to expose and set up special OData routes for them.  You can add multiple EntitySets to the model builder as needed for your OData endpoints.  Here, I’m adding Cat and Dog to the model builder.

The MapODataRoute uses the model builder values to create an OData route.  Note this is not exactly a Web API route, but you can use the same /api/{controller} convention if you like (or /odata/{controller} if you prefer) by setting the routePrefix to whatever you like. 

I tried toggling config.EnableQuerySupport on and off, and it worked in both cases, so I don’t know if that matters any more after the Web API OData update.

For more details about what is going on here, check out Youssef Moussaoui’s blog post.

Finally, you’ll need a controller, but this isn’t a Web API controller, it’s an ODataContoller.  That’s kinda close, and it inherits from the Web API controller, but it’s specifically designed for the OData calls you just set up in routing.

using System.Linq;
using System.Web.Http;
using System.Web.Http.OData;
using FizzWare.NBuilder;
using KendoGridWebApiOdata.Models;

namespace KendoGridWebApiOdata.Controllers
{
    public class CatsController : ODataController
    {
        [Queryable]
        public IQueryable<Cat> Get()
        {
            return BuildTestData();
        }

        private IQueryable<Cat> BuildTestData()
        {
            return Builder<Cat>
                .CreateListOfSize(100)
                .Build()
                .AsQueryable();
        }
    }
}

Here, I’ve got a CatsController inheriting from the ODataController.  I also have a [Queryable] attribute over the Get() action method.  This is another piece you supposedly need, and this was used in the older version of the Web API OData NuGet package, but I found my grid worked whether the attribute was on there or not.  You will likely want to leave it as a place to lock down incoming requests, since this is where you can disallow certain query parameters, limit records returned, etc.

The BuildTestData() method is using NBuilder to crank out 100 Cat entities and pretend like it’s queryable.

Test it out

That should do it for the server side.  Let’s test the route setup and see if we get data back.  I like using the Chrome plugin Advanced REST Client, but since we’re testing GET requests, you can also just type the URL in your browser.

For the URL /api/Cats, the JSON coming back looks like:

{
"odata.metadata": "http://localhost:56126/api/$metadata#Cats",
"value": [
{
"Id": 1,
"Name": "Name1",
"Color": "Color1"
},
{
"Id": 2,
"Name": "Name2",
"Color": "Color2"
},
...]}

To add some OData parameters to the URL, let’s try /api/Cats?$inlinecount=allpages.  Now the JSON has “odata.count” for the $inlinecount value:

{
"odata.metadata": "http://localhost:56126/api/$metadata#Cats",
"odata.count": "100",
"value": [
{
"Id": 1,
"Name": "Name1",
"Color": "Color1"
},
{
"Id": 2,
"Name": "Name2",
"Color": "Color2"
},
...]}

See how the records are coming across in the “value” array?  We’ll need that and the “odata.count” to help the grid know how to parse this JSON.

Client side

The HTML for the grid is minimal:

<div id="grid"></div>

And the JavaScript isn’t bad:

$(function () {
    var dataSource = new kendo.data.DataSource({
        type: "odata",
        transport: {
            read: {
                url: "/api/Cats",
                dataType: "json"
            },
        },
        schema: {
            data: function (data) {
                return data["value"];
            },
            total: function (data) {
                return data["odata.count"];
            },
            model: {
                fields: {
                    Id: { type: "number" },
                    Name: { type: "string" },
                    Color: { type: "string" }
                }
            }
        },
        pageSize: 10,
        serverPaging: true,
        serverFiltering: true,
        serverSorting: true
    });

    $("#grid").kendoGrid({
        dataSource: dataSource,
        filterable: true,
        sortable: true,
        pageable: true,
        columns: [
            { field: "Id" },
            { field: "Name" },
            { field: "Color" }
        ]
    });
});

In the dataSource variable, we’re setting the type as “odata”.  Seems like that would be enough, but you have to also set the read transport’s dataType to “json” or your request will go to the server like /api/Cats?$callback=jQuery183021786115039139986_1361332885733&$inlinecount=allpages &$format=json&$top=10.  This is a JSONP request.  We want the $callback and $format parameters to go away, and setting the dataType to “json” does that and sets the request’s Accept headers to “application/json”.

There is also some extra stuff going on in the schema section.  Normally, this is where you tell Kendo UI what your data types are so it can sort and filter correctly.  Here, we’ve added data and total to pull the values out of the JSON response so the grid needs can render correctly.  The data function is returning the “value” from the JSON we saw above, and the total function is returning the “odata.count”.

The rest of the dataSource properties are to force things to be done server side and to set the page size.  The remainder of the JavaScript is firing up the grid with this dataSource and setting properties of the grid.

When you run this, you get a Kendo UI grid working with server-side OData.  In this case, only 10 records are sent per request instead of the 100 Cat records we created on the server.  The OData handling works with sorting, filtering, and paging on the grid. 

If you get it running on your machine, check the XHRs to see the OData query string parameters getting added to the request and the response coming back with just the records requested.

Other nifty things

You can hit a URL to get the schemas of the entities you can query with /api/$metadata.  You’ll get back some XML like this:

<?xml version="1.0" encoding="utf-8" ?>
<edmx:Edmx Version="1.0">
 <edmx:DataServices m:DataServiceVersion="3.0" m:MaxDataServiceVersion="3.0">
 <Schema Namespace="KendoGridWebApiOdata.Models">
 <EntityType Name="Cat">
<Key>
<PropertyRef Name="Id" />
</Key>
<Property Name="Id" Type="Edm.Int32" Nullable="false" />
<Property Name="Name" Type="Edm.String" />
<Property Name="Color" Type="Edm.String" />
 </EntityType>
 <EntityType Name="Dog">
<Key>
<PropertyRef Name="Id" />
</Key>
<Property Name="Id" Type="Edm.Int32" Nullable="false" />
<Property Name="Name" Type="Edm.String" />
<Property Name="Color" Type="Edm.String" />
 </EntityType>
 </Schema>
<Schema Namespace="Default">
 <EntityContainer Name="Container" m:IsDefaultEntityContainer="true">
<EntitySet Name="Cats" EntityType="KendoGridWebApiOdata.Models.Cat" />
<EntitySet Name="Dogs" EntityType="KendoGridWebApiOdata.Models.Dog" />
 </EntityContainer>
</Schema>
 </edmx:DataServices>
</edmx:Edmx>

If you are worried about exposing your data online, read up on OData Security Guidance.

Limitations

URLs are case sensitive.  If you browse to /api/Cats, you get data.  If you browse to /api/cats (lower case “C”), you get a 406 error.

Kendo UI grid allows sorting by more than one column, but the current Web API OData implementation doesn’t seem to support it.

Download

You can download the code for this project from GitHub.

Tags: Kendo UI, Web API, OData, JavaScript
Categories: Technical

Integrating Kendo UI drop downs with Knockout JS

Posted by Joe Wilson on Monday, February 18, 2013 7:07 AM

Kendo UI drop downs look great.  Here is a Kendo UI drop down with default styling in closed and open view:

image

image

It is built with this HTML:

    <input id="kendoDropDown" type="text" />

and this JavaScript:

// Init the drop down
$("#kendoDropDown").kendoDropDownList({
    dataValueField: "id",
    dataTextField: "name",
    dataSource: [
        { id: "1", name: "apple" }, 
        { id: "2", name: "orange" }, 
        { id: "3", name: "banana" }
    ]
});

The snag

Unfortunately, this handsome drop down doesn’t integrate smoothly with Knockout JS when it comes to data binding.

We should be able to update our HTML to use a Knockout binding:

<p>
    <label for="kendoDropDown">Kendo UI Drop Down</label>
    <input id="kendoDropDown" type="text" data-bind="value: fruitId"
    />
</p>

Then add a view model and apply the Knockout binding:

// Define and init the Knockout view model
var ViewModel = function () {
        var self = this;
        self.fruitId = ko.observable();
    },
    vm = new ViewModel();   

// Set the initial fruit value
vm.fruitId("2"); // orange

// Wire up KO binding
ko.applyBindings(vm);

We should see a drop down loaded with the view model’s initial value of “2” (orange), but the first value in the list (“1” apple) is selected instead.  What’s going on?

More investigation

I thought maybe I had a Knockout bug in my code, so I played around with a plain (non-Kendo) select list and a plain text box by adding the following HTML:

<p>
    <label for="select">Plain old select</label>
    <select id="select" data-bind="value: fruitId">
        <option value="1">apple</option>
        <option value="2">orange</option>
        <option value="3">banana</option>
    </select>
</p>
<p>
    <label for="textBox">Plain old text box</label>
    <input id="textBox" type="text" data-bind="value: fruitId" />
</p>

But these controls were set to the correct initial value (“2” orange). 

It gets weirder when you change the view model’s value programmatically.  Here, I’ve added some HTML buttons to do that:

<p>
    <button id="changeTo3" class="k-button">change fruitId to "3" (banana) programmatically</button>
</p>
<p>
    <button id="changeTo2" class="k-button">change fruitId to "2" (orange) programmatically</button>
</p>

and some JavaScript to change the view model’s values:

// Wire up the buttons
$("#changeTo3").click(function () {
    vm.fruitId("3"); // banana
});
$("#changeTo2").click(function () {
    vm.fruitId("2"); // orange
});

Clicking the buttons should change the view model’s value, which should change the controls’ values.  It works for the plain HTML controls, but not for the Kendo UI drop down.

Here’s the JSFiddle to see all this in action.

The work around

A Kendo UI team blog post describes a work around for this issue, but it didn’t work for me. 

The trick here is that the Kendo UI drop down is really a hidden input and a bunch of spans.  Knockout changes the value of the hidden input when the view model changes, but you have to hook into that jQuery DOM change event and call the code to select a new value in the Kendo drop down.  Kind of lame, but it’s supposed to work.

$("#kendoDropDown").bind("change", function() {
    $(this).data("kendoDropDownList").select(this.selectedIndex);
});

You can even unhide the hidden text box inside the Kendo UI drop down list and watch its value change as the view model changes, but the drop down just sits there.  Arg!

The real work around

I was pulling my hair out by now, and asked for help on Stack Overflow.  Unfortunately, the answer I got was use another library that does the integration in a different way, the Knockout-Kendo.js library.  That’s probably a good answer, but I was hoping to avoid adding another library just to use Knockout and Kendo UI together.

Instead, I found I could get everything working as expected with a Knockout subscription.  It’s like a view model data change listener.  My thinking was that I could use the same concept as the Kendo UI team blog post (listen for a change and then manually set the drop down).  Not ideal, but oh well. 

Here’s what I changed in the view model:

        self.fruitId.subscribe(function (newValue) {
            $("#kendoDropDown").data("kendoDropDownList").value(newValue);
        });

And the final, working JSFiddle.

Tags: Kendo UI, Knockout, jQuery, JavaScript
Categories: Technical

Resizing a Kendo UI drop down to fit its data without wrapping

Posted by Joe Wilson on Saturday, February 2, 2013 10:19 PM

Telerik’s Kendo UI Web makes a spiffy looking drop down with JavaScript and CSS.

image

The demos on the Telerik site show drop downs with default widths and not much text. 

That breaks down when the text in the drop down gets longer because it wraps.  I don’t like this look.

image

So I wrote a JavaScript function that resizes the drop down to fit the width of the data so you avoid the wrapping and get:

image

Here’s the JavaScript:

var resizeDropDown = function (e) {
    var $dropDown = $(e.sender.element),
        dataWidth = $dropDown.data("kendoDropDownList").list.width(),
        listWidth = dataWidth + 20,
        containerWidth = listWidth + 6;

    // Set widths to the new values
    $dropDown.data("kendoDropDownList").list.width(listWidth);
    $dropDown.closest(".k-widget").width(containerWidth);
}

You have to call this on the drop down’s dataBound event.  That way, if you’re pulling remote data through something like Web API, you won’t resize until you’ve gotten the data back and fired the dataBound event.

Here’s a JSFiddle to play around with it.  The fiddle shows two drop downs – one with the default sizing and wrapping, and the other with the resizeDropDown function called on the dataBound event.

Tags: Kendo UI, JavaScript, jQuery, Web API
Categories: Technical

SpecFlow and WatiN Worst Practices: What *NOT* to do

Posted by Joe Wilson on Sunday, January 6, 2013 2:05 PM

The best way to learn something is to jump right in.  Best practices don’t really emerge until you’ve found out what works and what doesn’t.  This is my list of things that didn’t work on my four SpecFlow and WatiN projects in the last two years.  Of course, I’ll also talk about what did work.

Acknowledgements

First off, almost everything I know about SpecFlow/Cucumber/Gherkin I learned from Paul Rayner and pairing with with him on two projects.  I’m grateful for the experience and the knowledge gained.

I’ve also tried to glean as much as I can from Marcus Hammarberg, Darren Cauthon, Richard Lawrence, and Brandon Satrom in their twitter feeds and articles.

What level of your app should you test?

One big issue that comes up around SpecFlow is what level of your application should you be testing – internal or external.  The answer has a big impact on how you write your feature files, what side benefits you get from the feature files, and the speed of your tests.

Developer-facing

You can use SpecFlow as a BDD-style tool and do unit or light integration testing with it.  I call these developer-facing feature files, because they are expressed in language internal to the application (“Then I post the form values to the AccountController”).  The nice thing about this is the readability of the test from the dev side and the speed of execution, since you are explicitly testing code internals.

Business-facing

But I think this misses an opportunity to have richer language in the feature files.  If the SpecFlow features are worded the way the product owner sees the app working, you are fostering a ubiquitous language between the business and the developers.  You have a clearer vision of your app’s external behavior expressed through concrete examples.

You end up with “Then I click the save button” or even better, “Then I save” instead of “Then I post the form values to the AccountController”.  The bad thing about this style of spec wording is you end up testing through UI test automation tools like WatiN.  This testing style is slow and can be buggy and fragile if you aren’t careful (see below for tips).

In my experience, one of the main problems in software development is getting the dev team to understand what the business wants built and why.  SpecFlow is a great tool for getting both sides together to define that behavior through examples.

Furthermore, UI testing becomes much more important as web applications take on more and more JavaScript and other client-side code.  Since the external behavior of the app is how the business and users will evaluate the app, I think it makes sense to test at this level.

How not to set up your test project

Acceptance tests that drive the browser are not unit tests.  They are closer to integration tests, but they are even slower, since your local web server and the browser have to spin up each time and fill out forms, click buttons, etc.

Because of this, don’t add SpecFlow to your current unit test project or even your current integration test project, if you separate those.  Let it live in its own project with a name like MyProject.AcceptanceTests.  For quick test running and CI, it’s much easier to omit an entire assembly of tests, so keep these guys separate. 

I find that I often want to run the faster unit tests and run the acceptance tests only around the parts of the application we are working on.  I run the full suite maybe 2-5 times a week, especially before a hand-off to a stakeholder or a big demo.

How not to install SpecFlow and WatiN

Don’t bother with DLL downloads or manually setting up file references.  Install the SpecFlow NuGet package and the Visual Studio Extension.  Let NuGet handle the assembly references and setting up the app.config for you.  The VS Extension adds some nice feature file editing and right-click features to run a single test.

You’ll need to decide on MSTest versus NUnit.  The first comes with .NET, the later is free open source and you can add it with NuGet.  If you’re in a shop that heavily favors one over the other, use that.  All you’re using these for in the SpecFlow world is the generated code and the attributes marking your test fixtures and methods.

If you’re using NUnit (the default), you’ll need to be sure ApartmentState is set to “STA” in the app.config for your test project like this.  If you use the NuGet download, it should take care of that.

Finally, you’ll need WatiN, which can also be installed with a NuGet package.  I’ve sometimes had to set some of the WatiN assemblies to Copy Local = true in the acceptance test project to get everything working.  Kind of a hassle, but once it’s set up right, you’re done forever with it.

How not to set up a WebBrowser class and app.config

WatiN can run UI tests with Internet Explorer or FireFox (not Chrome, Safari, Opera, or anything else).  The FireFox API didn’t have parity with the IE one last time I checked, so I’ve gotten used to using the WatiN IE class.

However, don’t reference this class directly, or all your code is IE dependent.  What if the next version of WatiN supports an iPad emulator you want to use?  Do you really trust your find-replace skills that much?

Instead, wrap the references with a static class, and reference that in your step definitions.  Here’s the one I’m using these days that gets an instance of IE in a static constructor:

namespace Helpers
{
    public static class WebBrowser
    {
        private static IE _browser;

        static WebBrowser()
        {
            _browser = new IE();
            _browser.ShowWindow(NativeMethods.WindowShowStyle.Maximize);
        }

        public static IE Current
        {
            get { return _browser; }
            set { _browser = value; }
        }

        public static string BaseUrl
        {
            get { return ConfigurationManager.AppSettings["BaseUrl"]; }
        }
    }
}

This is maximizing the IE window after it opens and adding a reference to a BaseUrl property, which is set in the app.config. 

<appSettings>
    <clear/>
    <add key="BaseUrl" value="http://localhost:1234/"/>
    <!--<add key="BaseUrl" value="http://myapp.apphb.com"/>-->
</appSettings>

I use the BaseUrl to switch out the location of the test target in the step definitions (not shown above).  So if I want to test locally, the BaseUrl is “http://localhost:1234/”.  If I want to hit the AppHarbor version, the BaseUrl “http://myapp.apphb.com/”.  Just change the app.config and run the tests.

How not to set up your SpecFlow Before and After attributes

SpecFlow has attributes that can be put over your step definitions for your setup and teardown code.  Here are the hooks you have:

  • BeforeTestRun / AfterTestRun
  • BeforeFeature / AfterFeature
  • BeforeScenario / AfterScenario

I’ve struggled with where these go before, but the last couple projects I created a BeforeAndAfter.cs class (clever, huh?), set the SpecFlow [Binding] attribute to the top of the class, then added all my before/after code.

This is a great place to put database clean ups, security code if your app has a login, any custom SpecFlow tags you’ve created, etc.  Here’s the code I used on my last project:

using System.Configuration;
using TechTalk.SpecFlow;
using WatiN.Core;

namespace Helpers
{
    [Binding]
    public class BeforeAndAfter
    {
        [BeforeTestRun]
        public static void BeforeTestRun()
        {
            // Ignore all features and scenarios when running on AppHarbor
            if (ConfigurationManager.AppSettings["Environment"] == "Test")
            {
                Assert.Ignore();
                return;
            }

            // Always login first
            Security.ILoginAsAdministrator();
        }

        [AfterTestRun]
        public static void AfterTestRun()
        {
            // Logout to be ready for next session
            Security.ILogout();
            
            // Close the browser and dispose
            WebBrowser.Current.Close();
            WebBrowser.Current.Dispose();

            // Reset the data to be ready for the next session
            TestData.CleanUp();
        }
    }
}

I put all my helper methods like the WebBrowser.cs and BeforeAndAfter.cs in a /Helpers folder in the acceptance test project.

How not to organize your feature files

Feature files have one or more features in them.  You could put each separate feature in it’s on .feature file, but that’s overkill.

I used to create a new feature file for each view in the app.  That probably makes sense in some cases, but this approach often misses the value of those pages.  For instance, if there are 2-3 pages for an e-commerce checkout process, it’s probably not that helpful to have a feature file for BillingAndShippingAddress.feature, CreditCard.feature, and Confirmation.feature.  It might be helpful to developers, but it’s likely non-developers on the team see the checkout process more holistically, so why not start with a Checkout.feature file?  If this gets too big, split out pieces into their own logical .feature files later.

I put all my feature files in a /Features folder in the acceptance test project.  It’s mostly cosmetic, but it does help a little.

How not to organize your step definition files

Step definitions are the implementation of the steps in the feature files.  Since I was organizing feature files by web app page, I started out doing the same with step definitions.  When you try this approach, you quickly see  the problem, since step definitions can and should be reused across features. 

If there is a step definition “Then I go to /MyPage”, the step definition might be clicking a menu, a link in the page, or just opening a URL.  As you can imagine, this can be helpful in lots of different feature files, so it makes more sense to group these in a Navigation.cs file or something like that.  Maybe you have a separate Menu.cs with those step definitions, or maybe you lump them in with Navigation. 

How you group these is up to you.  SpecFlow sees all *.cs files with a [Binding] on them as places where your step definitions could live, and it uses regex matching to find the right method.  SpecFlow doesn’t care if you put your methods in one class or 100 classes, but you will.  So put some thought into how you group these, because it’s easier to see opportunities to refactor out similar code you find within the same .cs file versus across .cs files.

This makes the file names much more generic, but I’ve had the most success with this style.  On recent projects, my step definition files have included Forms.cs, Menu.cs, Messages.cs, Navigation.cs, Search.cs, Security.cs, and SEO.cs.

I put all my step definition code in a /Steps folder.  In the end, it looks like this:

image

How not to code step definitions

Step definition code in each method should be brief – like a couple lines.  If you’ve got large blocks of code, you’ve probably got too much going on. 

Maybe you have a giant form with tons of fields and a SpecFlow table feeding you the values.  You can probably get away with something like this:

        public void IFillOutTheForm(Table table) 
        {
            foreach (var row in table.Rows)
            {
                var field = row["Field"];
                var value = row["Value"];
                switch (field)
                {
                    case "Something Different":  // Put exceptions here for drop downs, date pickers, radio buttons, etc.
                        WebBrowser.Current.SelectList(Find.ByName("SomethingDifferentId")).Select(value);
                        break;
                    default:
                        WebBrowser.Current.TextField(Find.ByName(field)).Value = value;
                        break;
                }
            }
        }

Here, the “Something Different” case is where you would put non-textbox filling out code.  But if your forms are like most business apps, there are lots of text boxes, a handful of drop downs, and a smattering of radio, checkbox, date pickers, sliders, etc. that require some different WatiN code. 

Of course, if the exceptions outnumber the convention, make a separate method and see if you can factor out some common code to use somewhere else.  Remember, the key to being productive with SpecFlow is reusing the step definitions, so once you start seeing duplication, look for ways to refactor to a common convention in your feature files and step definitions.  It takes a while to get a critical mass of these built up, but once you do, you really start picking up speed.

Tags: SpecFlow, WatiN, ATDD
Categories: Technical

Fix for weird errors in MVC4 when adding a new Area

Posted by Joe Wilson on Tuesday, December 11, 2012 6:51 PM

MVC has let you add Areas to your web project for a couple versions now.  Areas help you group large chunks of code in your web app.  You can group along a technical axes (like “Api”) or a functional axes (like “Admin”).

The first error

I created a new MVC4 project, added an area called “Test”, and browsed to the home page (http://localhost:59669).  Works!  Then I browse to the new area (http://localhost:59669/Test) and get this:

image

What’s going on with those underscores?  The error message shows the problem is in my Web.config, but not the application root one, the one under /Areas/Test/Views/Web.config.  I open it in Visual Studio, and sure enough, underscores.

  <configSections>
    <sectionGroup name="system.web.webPages.razor" type="System.Web.WebPages.Razor.Configuration.RazorWebSectionGroup, System.Web.WebPages.Razor, Version=__WebPagesVersion__.0.0, Culture=neutral, PublicKeyToken=31BF3856AD364E35">
      <section name="host" type="System.Web.WebPages.Razor.Configuration.HostSection, System.Web.WebPages.Razor, Version=__WebPagesVersion__.0.0, Culture=neutral, PublicKeyToken=31BF3856AD364E35" requirePermission="false" />
      <section name="pages" type="System.Web.WebPages.Razor.Configuration.RazorPagesSection, System.Web.WebPages.Razor, Version=__WebPagesVersion__.0.0, Culture=neutral, PublicKeyToken=31BF3856AD364E35" requirePermission="false" />
    </sectionGroup>
  </configSections>

I compare that to the other Web.config under the root /Views/Web.config, and copy and paste those values in. 

  <configSections>
    <sectionGroup name="system.web.webPages.razor" type="System.Web.WebPages.Razor.Configuration.RazorWebSectionGroup, System.Web.WebPages.Razor, Version=2.0.0.0, Culture=neutral, PublicKeyToken=31BF3856AD364E35">
      <section name="host" type="System.Web.WebPages.Razor.Configuration.HostSection, System.Web.WebPages.Razor, Version=2.0.0.0, Culture=neutral, PublicKeyToken=31BF3856AD364E35" requirePermission="false" />
      <section name="pages" type="System.Web.WebPages.Razor.Configuration.RazorPagesSection, System.Web.WebPages.Razor, Version=2.0.0.0, Culture=neutral, PublicKeyToken=31BF3856AD364E35" requirePermission="false" />
    </sectionGroup>
  </configSections>

At this point, I’m assuming there is a bug in the “Add Area” template.  So let’s build and refresh the browser and it should work now, right?

The second error

Of course not.

image

Hmm.  Lots of details in that stack trace, but not much to go on here.  I checked my route registration code and checked my area registration code.  Both looked fine:

using System.Web.Mvc;
using System.Web.Routing;

namespace MvcApplication2.App_Start
{
    public class RouteConfig
    {
        public static void RegisterRoutes(RouteCollection routes)
        {
            routes.IgnoreRoute("{resource}.axd/{*pathInfo}");

            routes.MapRoute(
                name: "Default",
                url: "{controller}/{action}/{id}",
                defaults: new {controller = "Home", action = "Index", id = UrlParameter.Optional},
                namespaces: new[] { "MvcApplication2.Controllers" }
                );
        }
    }
}
using System.Web.Mvc;

namespace MvcApplication2.Areas.Test
{
    public class TestAreaRegistration : AreaRegistration
    {
        public override string AreaName
        {
            get { return "Test"; }
        }

        public override void RegisterArea(AreaRegistrationContext context)
        {
            context.MapRoute(
                name: "Test_default",
                url: "Test/{controller}/{action}/{id}",
                defaults: new {action = "Index", id = UrlParameter.Optional},
                namespaces: new[] {"MvcApplication2.Areas.Test.Controllers"}
                );
        }
    }
}

I also added the namespaces to the area (the last parameter above) to help the routing engine find the right controller.  That looked fine, too.

The error message was about a bad assembly (“The given assembly name or codebase was invalid.”).  Maybe something didn’t get built or loaded right ?  I tried a Clean and Rebuild.  No luck.

Maybe ASP.NET temp files were cached?  Let’s clear browser cache, close and re-open the browser, close and re-open Visual Studio, restart IIS Express, and see if that helps….and no, no luck.

The solution

I was running out of ideas, but since the first problem was the Web.config, maybe this was, too.  I opened up the generated Web.config under /Areas/Test/Views/Web.config again and compared it to the one under the root /Views/Web.config.

Sure enough, more freaking underscores.  I just copied ALL the values from the root view’s Web.config into the areas one, built, refreshed, and finally got the areas showing up.

Maybe I have a wacky install.  Maybe I got hold of some bad pre-release NuGet package.  But it seems there is a template bug here when adding Areas to an MVC project.  I have the Fall Update on Visual Studio 2012, so my About screen looks like this:

image

I’m writing this up in the hopes that I save someone else some time.  If you have suggestions on where I got an underscore happy template, please let me know in the comments below.

Tags: MVC, Areas
Categories: Technical

Press Release: Volare Systems Sponsors Colorado Give Camp

Posted by Joe Wilson on Wednesday, October 24, 2012 3:00 PM

Custom software company supports coding for charity.

Volare Systems is pleased to sponsor Colorado Give Camp in Colorado Springs this weekend, October 26-28, 2012. This is the third time Volare has sponsored the Give Camp.

GiveCamp is a weekend-long event where technology professionals from designers, developers and database administrators to marketers and web strategists donate their time to provide solutions for non-profit organizations. Since its inception in 2007, the GiveCamp program has provided benefits to over 150 charities, with a value of developer and designer time exceeding $1,000,000 in services.

“GiveCamp is a free, non-profit event that could not be possible without sponsors,” says Gabriel Villa, Colorado Give Camp Co-Organizer. “When we called for sponsors, Volare Systems stepped up to the cause. Not only have they provided food for a group of volunteers developing for non-profits, but also volunteered their President to develop for the weekend as well. Volare is a real leader in development and a leader in their community.”

About Volare Systems, Inc.

Volare Systems is a Denver, Colorado based custom software development consultancy specializing in Microsoft-based web applications, web-based mobile apps, and Windows 8 app development. We follow agile/lean software development principals, and our technical strengths are ASP.NET MVC, C#, SQL Server, HTML5, CSS3, JavaScript/jQuery, jQuery Mobile, Windows 8, and open-source software. For more information, call 303-532-5838 or visit http://volaresystems.com

Tags:
Categories: Business

Altitude app is now in the Windows Store!

Posted by Joe Wilson on Wednesday, October 24, 2012 2:16 PM

We created a simple but useful Windows 8 app using HTML and JavaScript (and the new WinJS libraries).  You’ve seen me writing about some of the technical aspects here on the blog.  I’m very pleased that it passed the technical reviews from Microsoft and it’s available in the Windows Store now.

Altitude for Windows 8 shows your current altitude (in meters or feet) and your current location (latitude and longitude and terrain map) based on your GPS, cellular, wifi, network, and/or internet connection. The values will then update based on either the time lapse or the movement threshold you choose in Settings. A live tile can optionally update with your current altitude. Finally, you can share your current location with other apps on your Windows 8 devices, like email, Twitter, Facebook, etc.

Landscape View

Snapped View

Settings

More information

Here’s more information on Altitude and links to the support FAQs.

Tags: Windows 8, WinJS, JavaScript
Categories: Business, Technical

<< Previous posts
  • Next posts
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • Previous posts

Blog links

  • Subscribe to this blogRSS feed
  • Archive of old posts

Popular posts

  • Autocomplete dropdown with jQuery UI and MVC
  • Handling Exceptions in ASP.NET MVC
  • Don't mock HttpContext
  • Review of Sharp Architecture
  • Evolution of a View in ASP.NET MVC
  • Comparison of Typemock Isolator and Rhino Mocks
  • Building a Windows 8 Live Tile with JavaScript
  • Setting Default Values for Multiple Value Parameters in Reporting Services
  • Buy, Build, or Both?
  • Why bother writing unit tests?

Tag cloud

  • AppHarbor
  • Areas
  • ASP.NET
  • ATDD
  • BDD
  • Castle Windsor
  • Coding Standards
  • Common Service Locator
  • continuous integration
  • Cookies
  • CRM
  • CSS
  • Custom Software
  • Data Annotations
  • DataTables
  • DDD
  • Dell
  • Dependency Injection
  • DTOs
  • ELMAH
  • git
  • GitHub
  • Html Helpers
  • HttpContext
  • IOC
  • iPad
  • iPhone
  • JavaScript
  • jQuery
  • jQuery Mobile
  • JSON
  • Kendo UI
  • Knockout
  • Microsoft Accounting
  • Moq
  • MVC
  • NHibernate
  • NuGet
  • NUnit
  • OData
  • optimizations
  • Patterns
  • POCOs
  • QuickBooks
  • Rails
  • Refactoring
  • Reporting Services
  • REST
  • Rhino Mocks
  • Session
  • Sharp Architecture
  • SOLID
  • SpecFlow
  • SQL Server
  • SSRS
  • TDD
  • TeamCity
  • TempData
  • Typemock
  • unit testing
  • Validation
  • Visual Studio
  • VMWare
  • WatiN
  • WCF
  • Web API
  • Web Essentials
  • Web Forms
  • Windows 7
  • Windows 8
  • WinJS

Archive

  • 2013
    • May (1)
    • April (1)
    • March (1)
    • February (3)
    • January (1)
  • 2012
    • December (1)
    • October (6)
    • September (3)
    • March (1)
  • 2011
    • October (1)
    • August (1)
    • June (3)
    • March (2)
    • February (2)
    • January (4)
  • 2010
    • December (2)
    • October (3)
    • September (1)
    • August (2)
    • July (1)
    • May (1)
    • April (2)
    • March (2)
    • February (3)
    • January (2)
  • 2009
    • November (3)
    • October (2)
    • September (5)
    • August (2)
    • July (3)

Blogroll

  • RSS feed for Dan WahlinDan Wahlin
  • RSS feed for Jimmy BogardJimmy Bogard
  • RSS feed for John PapaJohn Papa
  • RSS feed for Josh TwistJosh Twist
  • RSS feed for Los TechiesLos Techies
  • RSS feed for Phil HaackPhil Haack
  • RSS feed for Scott GuthrieScott Guthrie
  • RSS feed for Scott HanselmanScott Hanselman
  • RSS feed for Steve SandersonSteve Sanderson

Twitter

  • Twitter May 15, 7:20 PM

    At Denver .NET meetup to hear @rlacovara talk about SpecFlow

  • Twitter May 15, 9:26 AM

    @eriklane @extofer @greeleygeek That sounds weird to me, too. Values on the query string, sure, but not JSON.

  • Twitter May 15, 9:24 AM

    @kevinkrueger otherwise, people will not be as forthcoming about areas they hope the team can improve

  • Twitter May 15, 9:24 AM

    @kevinkrueger I think retros are best if they are for the team only, so they can have a frank discussion of how to get better.

  • Twitter May 14, 10:49 AM

    Terrific talk from @zekeli @html5denver last night "Cross domain Pong with window.postMessage" http://t.co/iO0AAlbJ7l http://t.co/5mnf2fZL0p

  • Follow me on TwitterFollow me on Twitter

Recognition

  • INETA Community Champions

Blog license

  • Creative Commons License
    Blog by Volare Systems is licensed under a Creative Commons Attribution 3.0 Unported License.
    Based on a work at http://volaresystems.com/blog/.