Quantcast
Channel: Scott Hanselman's Blog
Viewing all articles
Browse latest Browse all 1148

Making a switchable Desktop and Mobile site with ASP.NET MVC 4 and jQuery Mobile

$
0
0

I really enjoy working on and thinking about mobile websites. There's something about making an experience great on a pocket supercomputer phone that is so much more satisfying than a desktop. I actually got this blog looking nice on mobile devices back in 2006 when nobody was mobile except doctors and, well, all of us techies here on the blogs.

I've talked about the importance of a good mobile site before in posts like Create a great mobile experience for your website today. Please. However, some folks had asked me if I'd do a post on how to do a combination Desktop and Mobile site using ASP.NET MVC similar to the examples I used in my talks in Russia on mobile earlier this year. (There's video of those ASP.NET mobile presentations available)

When you start Visual Studio 2012 and go File | New ASP.NET MVC 4 app, there's an Internet Application template and a Mobile template. One gets you a standard desktop site - although with responsive design elements so it works on small screens - and the other gets you a jQuery Mobile application meant primarily for phones and tablets. Let's make one that switches between both.

We will do a small site in ASP.NET MVC for the Desktop, do some quick DB access, add jQuery Mobile and a View Switcher switch back and forth. I'll be using the Electric Mobile Studio from Electric Plum to simulate an iPhone. You can get a 7 day trial or you can get the Lite version of the Electric Plum Mobile Simulator with WebMatrix 2.

Quick CRUD Example

First, a model for DVDs.

public class DVD

{
public int ID { get; set; }
public string Title { get; set; }
public int Year { get; set; }
public Rating rating { get; set; }
}

public enum Rating
{
G, PG, PG13, R
}

Next, I scaffold out the Index, Create, Edit, Delete, etc. Unfortunately scaffolding doesn't do Enums (I'm sad) for my Movie Ratings so I add EditorFor() calls to my Create and Edits, and update my Index.

<div class="editor-label">

@Html.LabelFor(model => model.rating)
</div>
<div class="editor-field">
@Html.EditorFor(model => model.rating)
@Html.ValidationMessageFor(model => model.rating)
</div>

Shared/EditorTemplates/Rating.cshtmlI could have used DropDownList I suppose, but I have always found that helper confusing. Instead, I'll create a Rating.cshtml that makes a dropdown. I could change this at some future point to be fancier and not even use a DropDown.

Aside: How do you guys usually handle Enums? I've seen it done a few ways. I'd like this to be more generic, but here's what I did for the Rating editor Template. Note the nullable ? as it has to work for both Create and Delete

@model MyApp.Models.Rating?

@using MyApp.Models

<select id="@ViewData.TemplateInfo.GetFullHtmlFieldId("")" name="@ViewData.TemplateInfo.GetFullHtmlFieldName("")">
@foreach (int rating in Enum.GetValues(typeof(Rating))) {
var name = Enum.GetName(typeof(Rating), rating);
<option value="@name" selected="@(Model.HasValue ? (int)Model == rating : false)">@name</option>
}
</select>

OK, so there's a basic Desktop CRUD app.

Editing a DVD List of DVDs

Making it Mobile

iPhone in the Visual Studio browser menuAs I mentioned, you've probably noticed when making an ASP.NET MVC application that you can choose a Mobile template with jQuery Mobile along with the standard responsive "desktop" Internet Application. However, there isn't a switchable template. That is, one that is the regular template on Desktops but switches to jQuery Mobile (or KendoUI, or whatever makes you happy) on mobile devices.

Using NuGet, install the jQuery.Mobile.MVC package. You can either right click on References, select Manage NuGet Packages, or you can use the Package Manager Console and type:

install-package jQuery.Mobile.MVC

This package will automatically bring in jQuery Mobile as well as:

  • A ViewSwitcher partial view and supporting Controller
    • This is what lets us switch manually between Desktop and Mobile
  • A basic _Layout.Mobile.cshtml and supporting stylesheet
  • A BundleMobileConfig.cs used with ASP.NET Optimization (the bundler for CSS and JS)

NOTE: There's nothing jQuery Mobile specific about the ViewSwitcher or the techniques here. You can happily change this package for any other popular mobile framework.

We start by adding the new Bundles in the Global.asax:

BundleConfig.RegisterBundles(BundleTable.Bundles);

BundleMobileConfig.RegisterBundles(BundleTable.Bundles); // <-- ADD THIS ONE

Since I installed the Electric Plum iPhone simulator, I'll select it from my browser dropdown in Visual Studio and run my app and navigate to /DVD.

The system sees that I'm on a mobile device and rather than using _Layout.cshtml it uses _Layout.mobile.cshtml.

This doesn't really look nice for a few reasons. First, I don't like the default style. Just go into _Layout.Mobile.cshtml and change the data-theme attribute to a value that makes you happy. I changed it from theme a to theme b.

Basic App in an iPhoneSame baisic app with a light background

Second, while I'm using the _Layout.Mobile.cshtml I'm still using the desktop Index.cshtml which looks lousy when displayed in the mobile layout. Remember that I only have an single Index.cshtml. If I had an Index.mobile.cshtml the system would use that page instead when rendering on a mobile device.

I can make an Index.Mobile.cshtml with simpler markup, change the table into an unordered list and add some jQuery Mobile specific attributes like this:

@model IEnumerable<MvcApplication2.Models.DVD>

@{
ViewBag.Title = "My DVDs";
}
<ul data-role="listview" data-filter="true" >
@foreach (var item in Model) {
<li>
<a href="@Url.Action("Details", new { item.ID })">
<h2>@Html.DisplayFor(modelItem => item.Title)</h2>
<p>@Html.DisplayFor(modelItem => item.Year) - @Html.DisplayFor(modelItem => item.rating)</p>
</a>
</li>
}
</ul>

Note the data-role and very basic elements like <ul>, <li>, <h2< and <p>. I also added the client side data-filter attribute to get some nice client-side searching. Hit refresh and now it's starting to look like a real mobile site.

See the ViewSwitcher at the top there? That's a partial view called _ViewSwitcher.cshtml.

A lovely jQuery Mobile example list of DVDs

By default it will let you switch back and forth between Desktop and Mobile but only on mobile devices. Why? Check out the first line of code:

@if (Request.Browser.IsMobileDevice && Request.HttpMethod == "GET")

{
<div class="view-switcher ui-bar-a">
@if (ViewContext.HttpContext.GetOverriddenBrowser().IsMobileDevice)
{
@: Displaying mobile view
@Html.ActionLink("Desktop view", "SwitchView", "ViewSwitcher", new { mobile = false, returnUrl = Request.Url.PathAndQuery }, new { rel = "external" })
}
else
{
@: Displaying desktop view
@Html.ActionLink("Mobile view", "SwitchView", "ViewSwitcher", new { mobile = true, returnUrl = Request.Url.PathAndQuery }, new { rel = "external" })
}
</div>
}

That first line checks if it's a mobile device making the request. Just surround that with /* comments */ and you'll be able to view and debug mobile layouts with your desktop browser without faking User Agents.

So far we've been talking about "mobile" and creating files with "mobile" in their file names and it's all been magic. Turns out we have lots of control over these things. Perhaps we want more than Index.mobile.cshtml but also, perhaps, Index.iPhone.cshtml and Index.WP7.cshtml.

Create lines like these in your Global.asax:

DisplayModeProvider.Instance.Modes.Insert(0, new DefaultDisplayMode("WP7") {

ContextCondition = ctx => ctx.GetOverriddenUserAgent().Contains("Windows Phone OS")
});
DisplayModeProvider.Instance.Modes.Insert(0, new DefaultDisplayMode("iPhone") {
ContextCondition = ctx => ctx.GetOverriddenUserAgent().Contains("iPhone")
});

You can affect the DisplayMode collection (of which "mobile" is one) by inserting in your own. Here I'm making two more specific mobile ones but you don't need to do mobile things to change DisplayModes.  A DisplayMode could be right to left for RTL languages, or "gold" and "bronze" for customers or whatever makes you happy, based on the ContextCondition you evaluate on each request.

There's lots of choices and you've got the flexibility to do things however you like. I'll show an example of a totally offline iPhone site using cache.manifest in a future post.

Related Links



© 2012 Scott Hanselman. All rights reserved.

Viewing all articles
Browse latest Browse all 1148

Trending Articles



<script src="https://jsc.adskeeper.com/r/s/rssing.com.1596347.js" async> </script>