Popular Posts
-
Kendo UI applying SUM in footer template kendo ui - KendoUI: Grid Summary values in Footer - Stack Overflow : ", footerTemplate:...
-
MVC grid example ASP.NET MVC Flexigrid sample - CodeProject : 'via Blog this'
-
A possible way of handling distributed transaction for multiple contexts without alleviation to MSDTC??? c# - Entity Framework - Using Trans...
Saturday, June 1, 2013
JQuery mobile and ASP.Net MVC 4 Transcript
Transcript:
Introduction to jQuery Mobile
Introduction
(Pause) This is Scott Allen and this course is going to cover jQuery Mobile framework. jQuery Mobile is a JavaScript library that builds on top of the popular jQuery library. It allows you to build web applications that will work with patch interfaces and it looks great on all the popular mobile web browsers. It supports iPhones, iPads, Androids, Windows phones and BlackBerry. It also supports the Kindle Tree and the Kindle Fire as well as palm devices running webOS. And it also supports desktop browsers like Chrome, Firefox, Opera and Internet Explorer because this is just JavaScript, HTML, CSS, and web standards all the way down. In this course, we're going to add mobile features to an existing ASP.NET MVC application, the MVC Music Store sample application that will be using MVC 4, a beta, a Visual Studio 2011, jQuery Mobile and the entity framework. We'll mostly be focusing on jQuery Mobile though so you can see how this library works and you can use these concepts with any service sever side technology, Ruby on Rails, PHP, anything that's out there. But in this course, we'll get to see some of the new features in ASP.NET MVC4. jQuery Mobile, the library, you can download it from jquerymobile.com. It's not just a widget library or a loose collection of UI features like some of the other libraries and plug-ins in the jQuery space. JQM gives you a unified user interface system, meaning it does have some constraints and rules that you have to obey or you're going to fade against the framework. When you do follow up these constraints, what you'll produce is an application where jQuery Mobile automatically does a lot of hard work for you, adding features like asynchronous page navigation, progressive enhancement, responsive design, accessibility, touch support, theming. If you don't know what any of that stuff means, don't worry. I'm going to be demonstrating those features during this course.
Setup
( Pause ) One way to get started with jQuery Mobile is to go to the jQuery Mobile website and click the download link. On this page you'll find instructions to use jQuery Mobile from a content delivery network. That is you'll let someone else host the files that you need. But you can also find a zip file that you can download if you want to host the files on your own server. Once you've downloaded that file and extracted it, what you'll find inside are script files, CSS files, and there's also an images directory that contains images that are used by some of the widgets. Of course the files here that have min in the filename are the minified versions of the files, which means they've been processed to be as small as possible. And these are generally the versions you want to include in your website for production use. The unminified files are a little bit larger and if you want to read the source code or debug through the source code, those are the files you want to work with. Another way to get started with jQuery Mobile if you're using Visual Studio is to use NuGet. NuGet is a package manger for Visual Studio. We can use it to go out and find and download software that we want. So if I tell it to go out and find jQuery Mobile, and I think I just have to scroll down a little bit. There's jQuery Mobile. It knows that jQuery Mobile depends on jQuery so it can download jQuery and jQuery Mobile for me. Download those files and add them into an existing web project that I have. It has placed the jQuery and jQuery Mobile scripts in the scripts directory. It's placed the jQuery Mobile CSS files in the content directory, and also brought in the images folder that I need for jQuery Mobile. Ultimately, what we want to do is add JQM support to an existing ASP.NET MVC application, but before we do that I want to show you the simplest possible jQuery Mobile page. So here is an empty webpage with an HTML 5 dot type. The first thing we'll need to do is include links to the style sheet for jQuery Mobile, as well as links to jQuery and jQuery Mobile scripts. I'm just going to include the unminified versions of these. But this represents three quarters of everything that we need to add into the head tag. We'll come back and talk about one more piece in just a bit. But one thing I do want to point out is that since jQuery Mobile requires jQuery, we have to make sure that the jQuery script is included in this page before jQuery Mobile, otherwise, to solve false apart and won't work. But with our CSS in place, with our two scripts in place, what I want to do next is come in and add some content to the page. So inside of the body element, I'm going to paste in a div with a data-role equal to page. With jQuery Mobile, you identify certain structural elements and widgets using data dash attributes like data-role. If you haven't seen data- attributes before, they are an extensibility mechanism that is part of an HTML5 specification. The specs says that you can add as many customer attributes as you want to any element as long as you prefix those custom attributes with a data-. And even though that's part of an HTML5 specification, data dash attribute work anywhere even in old browsers like Internet Explorer 6 because browsers just ignore attributes that they don't understand like data- attributes. And it's up to the scripts that you write or in this case the jQuery Mobile script, to find those attributes and do something with them. What we're going to see in this course is that you can use many different data- attributes in many different places to tell jQuery Mobile what to do. For instance, data-role equals page is telling jQuery Mobile this is a structural part of this resource. This is the page. Inside of that can be headers and footers and content areas, it's going to arrange things so that they look nice on a mobile device. That's a little bit different programming model compared to something like jQuery UI for example. With jQuery UI, you write script codes to create widgets. With jQuery Mobile, you can use data-role attributes and other data- attributes that we're going to see. jQuery Mobile will find those attributes and transform your mark up into fancy UI widgets. And now that we have our scripts in place, our CSS in place, we have some basic content in place, I should be able to open this page in a browser and see that we have some output and it's a little bit style. There's certain font settings that are in place already but we go one step further though and actually add a header to this page. So a header is something that I specify with a data-role attribute. In this case, data- role equals header so I'm going to save this page again and refresh, and you can see that the header appears there and it's styled. And this is working in a desktop browser and jQuery Mobile does work in desktop versions of Chrome and Internet Explorer. You might be wondering what it looks like in a mobile browser right now. That's what we'll look at next.
Viewports
What we're looking at now is an Android device emulator and I've loaded a new page into the mobile browser on the device. But it's very hard to see the content because the browser is in a zoomed out mode. The reason is most mobile web browser have the concept of a view port, and a view port looks into a virtual window that is much larger than the physical screen on a device. It allows the user to view all f the content on a page by panning and zooming. But by default, many web browsers assume that a web page is designed for a wide desktop screen, 980 pixels wide to be exact, so they will automatically display zoomed out view of the page making all the text and images small and hard to see. Fortunately, there's an easy solution to this problem and it's known as the viewport meta tag. The viewport meta tag is essentially an instruction from mobile web browsers. They are going to detect and follow what you tell them to do in this viewport meta tag. And the tag allows you to control the size and the scale of the viewport. The viewport tag that I've just pasted in is making sure that the zoom level is 1, so we're not zoomed out, we're not zoomed in. And we've also set the width of the page to match the width of the device's screen. Mobile web browsers on phones and tablets will see that now under the content that's designed to work with their form factor and the render page in a much different fashion. So let me save that and we'll come back into our emulator and refresh this page. ( Pause ) And now we have a much nicer display, something that is easy to read and comes up the way we would expect on a mobile phone.
Widgets and Attributes
Back in the desktop browser, you can see that the viewport meta tag doesn't really have any impact on the display here. That meta tag is only detected by mobile browsers and they will change their layout to obey what you put in the tag. But now that we're here in Visual Studio again, let's add some additional content to the page. I can put any content that I want inside of this content area. I can put forms and buttons and in this case, I've added a simple unordered list. It's a simple list with anchor tags inside. If those anchor tags pointed to real URLs, we could navigate to new pages but we'll talk about navigation with jQuery Mobile a little bit later. Let me save this file and see what it looks like in our desktop browser, and it should look like what you would expect. Now these data- attributes, they can identify headers, footers and content areas, as well as jQuery Mobile widgets. For instance, I can give this unordered list a data role of list view, and now it's not going to be just a simple unordered list. It's going to be a jQuery Mobile list view. So let me refresh the browser and you can see we have icons, we have hover effects. And this is one of the ideas behind jQuery Mobile. It transforms a regular web page into something that's going to look really good on a mobile device, something that looks almost like a real application on that device so you'll forget you're in a web browser. It's graphically rich and full of little animations like this hover effects. So some data- attributes identify widgets and other data- attributes can change the look of a particular widget. For instance, I can come into the unordered list now and say, data-inset equals true, and refresh this in the browser. Now we have some margin between the list and the edges of the window. And also notice I have nice, rounded corners on the edges. It's not just displaying both the square buttons. So in addition to data-inset, I can also add some data- attributes to add additional features or behaviors to a page or to some widget. For instance, I can come into this list and I can say "data-filter equals true." And what that will do is give the user the ability to filter that list. There's a search box there with a nice icon with rounded corners with watermark text that tells the user what they should be able to do like filter items. So if I type in P-E-P, I only see the Pepsi products, and that looks great in the desktop browser. Let's also try it in our emulator and so I'll refresh the mobile browser. And this looks pretty sharp inside of our mobile browser, too. And I'll be-- really done so far is add the jQuery Mobile script, the jQuery Mobile style sheet, and add in some data dash attributes to our HTML markup, and of course the meta viewport tag. Now this looks great on a mobile device. ( Pause )
Themes and Swatches
You might find that the page we've been building that it's missing a certain something like color. Some people enjoy gray and black and white colors like the people behind the UI design for Visual Studio 2011. But other people like bright and vibrant colors. Fortunately, jQuery Mobile comes with a theme framework. It's implemented entirely in CSS and this framework supports up to 26 sets of colors for all the tool bars and buttons and list views. These sets of colors are called "swatches" and they have letters associated with them. Out of the box jQuery Mobile ships with five swatches, A, B, C, D and E. If you want to change the colors for a swatch or build your own, you can come to jquerymobile.com/themeroller. It's here where you have an interactive designer. You can import your existing CSS file if you tweaked it at all. You can start something from scratch. And here I could, for instance, go to swatch A and I could change what the content body is going to look like. I want the background color to be this and I want the text color to be, maybe blue, and I want the link color to be sort of green. And as usual, I've produce something truly hideous here. But if you're good with colors and you just want to tweak a few small things, you can always jump into the CSS files themselves. There are three sets of CSS deliver of jQuery Mobile. There's the structure file, the theme file, and then the jQuery Mobile file itself. And of course there're minified and unminified versions of each of those three sets. The jQuery Mobile CSS file, that's what we're using in the webpage. It combines both the structure and the theme together. But broken out the way it is, you could keep the structure from jQuery Mobile and build your own theme from scratch or vice versa. In this combined filed, I could just open it up if I wanted to tweak a few of the built in colors, open it up and in here I'll find the definition for swatch A for example. These are all the colors and styles that are associated with swatch A. And if I hit control I to open up the immediate search and look for your UI dash bar dash B, that would be swatch B. Themes and swatches are all about colors and backgrounds while the structural part of this file is about layout, margin, padding and space related issues, things like that. So I could make changes in the CSS file itself. I could make changes using theme roller. But so far in our application, we've only been using the default theme. In the default theme, we'll use swatch A which is the black swatch. It will use that for high priority visual elements like a header. And then we'll use swatch C, which is gray, that's the default for the content area. Let's mix things up a bit because we can override these defaults using a data- attribute, the data-theme attribute actually. Let me go to the header and give it a data-theme equals B and save the file and refresh. And now we have a blue header. And I could go into the content area and I could go into the content area and say data theme equals C. Well, C is the default so we'll make it E. And you can see that that has a different visual appearance. That's the accented swatch. Let's also add a button to the content area. And one of the things to notice is that button will inherit the theme set by its parent which is the content area. So these themes, these swatches can be inherited throughout an area. But of course, we can override them at any level. So I could come into that button and say, "No, the data theme for you, I want you to stand out a little bit." That gives us a blue button and now let's add one additional piece to this page which is F of the content area. I'm also going to add a footer. And one thing you'll notice about the footer is that it doesn't align itself with the bottom. In fact, if I scroll up here so that there's not enough content, I need to scroll down to see the footer. That's another behavior or positioning that I can change when I can come in and I can say "data-position equals fixed." And now what we should have is a fixed footer so it doesn't matter how much content I have. That footer is always going to stick to the bottom.
Summary
In this module, we look at the jQuery Mobile basics. We saw how to set up a page with the proper script in CSS files and we added the all important meta viewport tag to make sure a page displays nicely on a mobile web browser. I also introduced you to the basic structure of a JQM page which uses data dash roll attributes to specify headers, footers and content areas, all of which are actually optional but they're there if you want to make something that uses the basic building blocks of jQuery Mobile. In the next module, we're going to take a look at what we've learned and apply it to an existing MVC 4 application in order to make the app friendly for our mobile users.
Page, Dialogs, and Navigation with jQuery Mobile
Introduction
Hi this is Scott Allen and in this second module on jQuery Mobile will build upon what we learned in the introduction and add mobile features to an existing web application. As we do this I want to focus on some of the page navigation features provided by jQuery Mobile, understanding what JQM is doing behind the scenes for us with regards to navigation, its crucial to working with this framework and avoiding some pain. So in the last module we learned a bit about the structure of a jQuery Mobile page as we explore a mobile only web page. But as web developers we usually don't have the luxury of building a website that supports only mobile users. So in this module we'll optimize the mobile browsing experience for the ASP.NET MVC Music Store, this is a sample application you can download from the CodePlex website and it does work with a mobile web browser, but it doesn't deliver an optimal experience. So we'll be adding jQuery Mobile support to the site and Ill show you some of the trade offs to evaluate and tricks you can use to support both mobile and desktop browsers. I can say upfront that this job is a lot easier if you maintain a separation of concern in your UI layer and that's something that is very natural for an MVC application. It separates the responsibilities of the model, the view and the controller. And what I want to do when adding mobile features to a web app is to reuse as much code ad possible. With MVC that means I can reuse the models and the controllers as much as possible, perhaps even reuse some views but I do have the ability with ASP.NET MVC to have a customized view for a mobile device and we'll see just how easy that is very soon now ( Pause )
Viewports and Media Queries
If I load the existing music store application into any mobile browser, it's going to look a bit like this. This browser I am using is Opera Mobile because this browser will run on the desktop and I don't need an emulator so it's a lot faster. It also it provides the mobile behaviors like the ability to simulate touch events and the ability to switch between landscape and portrait mode, that's just an Alt R key board shortcut and it gives me really quick feedback on how the site is looking. Of course the first problem here is that the music store appears on the browser in a zoomed out fashion and that makes it impossible to see any of the navigation links unless the user starts to zoom in. We know how to fix this problem from the last module, it's as easy as adding a viewport Meta tag but where do we add this? In an MVC 4 application I can take one of two approaches. We look at one approach now and one approach in the next clip. But the first approach is to add this Meta tag to the existing layout page. And if you've done any work with MVC, you know that the layout page which is in the shared folder is the template that all of the views in this application plug into, so that defines the basic structure, the head tag, the header, the footer and where to render the body, that's all defined inside of underscore layout.CSHTML. And I could inside of the head tag here add my Meta tag saying the width is equal to the device width, the initial scale is equal to 1 and save that and come back and refresh in the browser and it's going to look a little bit different but it still not quite right. But now we'll be sending this Meta tag down to both the desktop browsers and the mobile browsers but we only expect mobile browsers to act on the tag. And now the page looks a little bit different but it's still not looking great and part of the problem here is the art work that's on the page, this promotional image is about 700 pixels wide. Now if you want to use a single code base to support both desktop and mobile browsers, you'll have to make some adjustments and concessions on how the site appears. We'll come back to that topic in just a minute but let me point out one way to solve this problem. What I want to do is use different pieces of art work with desktop and mobile and how do I do that? The answer is going to be in a media query. Because the music store application has a nice design, where all of the presentation related details like colors and padding and even the image to use like the image for that promotion, that's all defined in the CSS file. So one thing I can consider here is a media query to change that image depending on the device capabilities. A media query is an expression that you can put into a CSS file to assign style rules based on the Media query result. A media query is literally a query to see what media features are available, if the query returns true, the styles apply. If the query returns false, the styles don't apply. Media queries have nothing to do with jQuery Mobile really, they're part of the CSS 3 specification and they're heavily used in mobile applications. What I'm going to add in this CSS style sheet is a media query to say that we'll only apply this promotion rule if we're on the screen in the minimum width of the device is 640 pixels, that's something that our Opera Mobile browser wont fit in because it only has a 320 by 480 resolution. If I save that file and come back to our browser and refresh, then we've gotten rid of that promotion and the page lays out a little bit differently. It's still not optimal but I just wanted to demonstrate media queries, there's something that you need to consider for doing mobile development because mobile devices come in such a wide array of shape these days. An iPad is a mobile device but it's a lot larger than the 320 by 480 smartphone, we're emulating with Opera mobile here and you might want to have a large detailed image on the screen. The more of these decisions you can push into your style sheets, the easier it will be to use media queries to creat a responsive design that works well with different devices. Now what we're trying is one approach to mobile development. That is, I'm trying to put features and switches into the same file that I'm using for desktop devices. So I have one set of views, one set of style sheets and perhaps multiple images but for the most part I have a single code base. Probably what I would do for this promotion is come up with a different image that can fit somehow here on the screen. But what I want to do instead and explore in the next clip is a different approach. One way I completely optimize for this mobile device using a second set of views.
Layout and Navbars
One of the great features in the ASP.NET MVC 4 is the ability to switch views depending on the user agent string that the client's browser sends to the server. This is a feature available in a lot of different server side web frameworks but it's something new that you get out of the box with MVC 4. I'm going to use this feature to create a mobile optimized site. The way this works in MVC 4 is that if the browser is a mobile browser then the MVC 4 view engine will automatically look for a view with dot mobile in the name. And it finds a dot mobile view or render it, in other words if you tell the MVC framework to render the shopping cart view and it finds a shoppingcart.mobile view, it can render shoppingcart.mobile automatically if the client is on the phone. This view switching technique works for all types of views, content views, partial views and even layout views. So to optimize for a mobile, I've created a copy of the application template the underscore layout.CSHTML file in the music store and I've named the copy underscore layout.moblie.CSHTML. And as you can see in the editor here, we are structurally very similar. In fact I have changed nothing inside of the body tags between underscore layout and underscore layout.mobile. The only thing I've changed in the mobile version of the layout so far is inside of the head tag. I've included the jQuery Mobile style sheet and the jQuery Mobile script file and I've also included our own new custom style sheet called mobilesite.CSS because we'll use an entirely different style sheet for our mobile design. Now let's see what this looks like in a desktop browser and you can see there's no change here, there's not supposed to be. We're still using the regular underscore layout.CSHTML template and we're using all the same code and all the same views and style sheets that we had before. But on a mobile browser I expect to see something different, it should now use underscore layout.mobile.CSHTML. I don't have any styles define this yet, we're using just the jQuery Mobile styles that have been included and we haven't even setup the structure of the page. But given that we really haven't done anything yet, if I refresh it, it looks dramatically different, that's what I expect. And in fact I think it's a lot better because now the text is laid out vertically and it's very easy to scroll and swipe through here to see all of the content. But we can do even better that this. So let's go into this layout view and add some of our jQuery Mobile structure. So the first thing I'll do is that I know jQuery Mobile likes to work with the concept of a page, so I'm going to define a div out here that will contain everything inside the body and have a data role equal to page, the closing div tag is down here at the bottom, control K, control D to do some formatting and that should be a bit of an improvement. This header section is already perfectly defined to implement a data role of header and I'm going to take the ID equals header off of here for now. We'll talk about that in just a bit. And let's come back and refresh our browser and see that's already looking a bit different. Now there're a couple problems, we'll address them from top to bottom. First of all the text of our tittle here ASP.NET MVC Music Store, doesn't quite fit on this mobile device. And sometimes you need to make compromises for small screens. What if we just call this Mobile Music? And we can include a logo somewhere that explains that this is the ASP.NET MVC Mobile Music Store but that's already looking better. And now there's these navigational links, they're taking up a lot of vertical space and one thing I would like to do is stretch them across horizontally so that they don't take up as much space and we can have more content on the screen and things that people that want to buy. There's a couple different ways I could do that. I could do that with CSS but it turns out there's also a nav bar, a widget that's built into jQuery Mobile and it's a basic navigational widget. You can put it inside of a header and it turns out that our nav list element that we have here is already perfectly written to be part of a nav bar because the nav bar expects an unordered list inside of it with the list items that have hyper links. All we need for this one is also a containing div with the data role equal to nav bar and I'll take that closing div tag and make sure it's underneath our last UL and control K, control D to do some formatting again. Save and let's refresh one more time. And that looks better except that now I don't have quite enough space to display all of the text for the cart. The cart is trying to indicate how many items are in the card. I could perhaps get rid of that feature. I could also evaluate some of these other links to determine if they really belong here. For instance the admin link is probably used by very few people, I could get rid of that one. I could also get rid of this home link and perhaps give the user another way to get home. Let's try that approach. Let me take the anchor tag that is inside of here and cut it out. We'll get rid of that list item and instead I'm going to add another item to the header. I'm going to put the anchor tag right here but I'm going to use another data dash attribute, data dash icon to tell jQuery Mobile that this should be a home icon and to position this icon in such a way that it has no text. You can also use icon position to move things left or right. In this case what I'm going to say is, I don't want this link to actually display a new text, I just wanted to be a link that displays a home icon, so saving this and refreshing. And now we have something that's looking pretty interesting and it's looking pretty good on a mobile device. And I have a nav bar where I can click to see my shopping cart, only the shopping cart and the content that I'm interested in is always at the bottom of the page. And that simply because this genre menu rock, classical jazz and pop that is always appearing before the main content where I call render body in this template. It might be best for now just to get rid of this menu so that we're really just looking at the output of the content view. So there I can see my cart, here I can get home and I can see what's fresh off the grill or the promoted items. Now these pages can also use some work. In fact I'd like to introduce you to a couple other widgets that are in jQuery Mobile. But before we do that, I really want to focus in on what's happening when I'm clicking on these navigational links? What work is jQuery Mobile doing behind the scenes? And how does it impact how I implement my website? ( Pause )
Pages and Navigation
We now know that when we're viewing a page built with jQuery Mobile there is in the markup and in the document object model of the browser a div with the data role of page. This first page if a jQuery Mobile website is loaded into the browser with an HTTP request like any other page. Now inside that div there can be links, links in the navigation bar, in the header, in the content, anywhere on the page. And one of the most important things to understand about jQuery Mobile is that it will by default try to make all navigation happen using Ajax which includes navigation via links and also form post. And what this means is that when the user clicks a link to go to another page, jQuery Mobile intercepts that click and doesn't let the browser do a full request and refresh to display the page. Instead, jQuery Mobile request the new page using Ajax asynchronously and behind the scenes. When it receives a response, it parches out the new page out of the response and adds it to the DOM where the initial page is still living, still there. And jQuery Mobile will hide the initial page and show the new page using a smooth transition on browsers that support 3D features which Opera Mobile browser does not. The end result is a web application that feels more like a desktop application because it doesn't have to hide and redraw web pages. Instead you get spinning images or a fade in and fade out effect which is very smooth and not disruptive. The site feels more like an application. What I've done so you can see this effect a little more clearly is to have the site serve the mobile version for all browsers even desktop browsers and introduce a 500 millisecond delay in all request to an MVC controller. Now if we visit the site using Chrome desktop, we can clearly see this delay but the page transitions are smooth thanks to a fading animation. There's even a data dash attribute that you can use to change the type of transition. You can choose from pop, fade, flip and slide. Now while I'm navigating around and by the way this also works when I use the back button is-- let's open up the DOM explorer or Chrome and actually see what's inside. Here you can clearly see two pages are loaded into the DOM, the first page that became to in this application and now the page that displays the music categories. When I click on a new category, one page disappears another page loads and this behavior continues every time I click a link on any request, I can even respond from the server with the page that has multiple jQuery Mobile pages inside that single page that is multiple div element with data dash role equals page and jQuery Mobile's smart enough to switch between those without making request to the server. That's a great technique to use for static content because the content is effectively cached in the browser and the application response is fast, very fast. These page navigation features provided by jQuery Mobile are very nice but there's a few caveats to be aware off. We'll look at those next. ( Pause )
Navigation Caveats
One question you might have thought about already is this, what happens if a page fails to load? That as I click on a link and instead of returning a new page the server returns an error code. To simulate this, I'm going to have a store controller throw an exception every time we try to browse the categories. And once this is built, I'll come over and just refresh the application that make where we have everything in here and then click on the store link. And what we'll see is we get a little pop up that says "error loading page." If that isn't the behavior that you want, there's a couple ways to change it. There is a page change failed event that you can catch in Java Script and we'll look at that in a later module when we talk about the API. We'll also see another option later in this clip. Something else to be aware of is that when a page does load successfully, so not when we have an error and let me comment out this exception and rebuild so that we don't get any errors. When a page does load successfully, jQuery Mobile is parsing out a portion of the response, the page part and grafting that into the DOM. And there're several ramifications here. One is that you have to be careful with element ID values because you can end up with multiple elements having the same ID value in the DOM because they were loaded from different pages. And if we look at layoutmobile.CSHTML, we know it is the template for all views in the application. And it renders a page which includes a header, a footer and a content area which we really should give a data role of content to. And each time that we render a view, we're rendering this entire layout view and so we'll be rendering a footer with an ID of footer. And what that means, if I just save and rebuild everything. Let's come back into our application, refresh this page and then go to the store. (Pause) And now let's look in the DOM explorer. And let me expand the first page that loaded and the second page that loaded and what you're going to see is that we have multiple elements with an ID of footer in this page, that's bad. It could confuse our scripts because elements should have a unique ID value. We'd probably want to remove those IDs here and rely on CSS classes or data dash attributes if we want to interact with these particular elements or style them, just let me remove the ID equals footer and ID equals main because we don't want these appearing multiple times in the page. Another thing to be aware of is that because jQuery Mobile is parsing out the page portion of response and grafting it into the DOM, it can ignore things that you might have outside to that page area. For example, here in the layout view I also have a section defined, an MVC 4 section so that a view can render something outside of the data role equals page area that could render for instance a scrip to put at the bottom of the page. And now if I come into the index view for the store controller which is the view that's going to put out the list of categories of music. I'm going to put an alert in here and let's save everything and see how this behaves in the browser. So first let me do a hard refresh on the store page here. And what we should see pop up is the alert box. So they got to that bottom script. So if I come into this page and it's the first thing I come to, we load the entire page and that means that the script shows up here ate the bottom of the page and it executes and that raises the alert. But let's say that I first came into the home page, let me go to the home page and do a hard refresh here to make sure that's our starting point. And now come into the store and I have two links for the store here just to point out that it doesn't matter if the link is in a navigation bar or if just another link inside of your content area. If I go to that link jQuery Mobile still asynchronously loads that page in. But you'll notice that we didn't get an alert. That portion of the HTML didn't make it into the page that Java Script didn't execute. So what you need to be aware of here is that if your view is not the first view and it's getting loaded via Ajax then things that it has outside of that page area might not make it into the page. If there's a script you need that is specific to that view, don't try to load it by putting it into a section that would put into the head tag or at the bottom of the page, it has to be in that page area to make it into the DOM and to load that script. One of the easiest strategies to follow is just to make sure that everything you need is loaded with the master template in layoutmobile.CSHTML for example include all of your scripts and all of your assets there. If you need to make something else available for an individual page, like an additional script just make sure that script tag is inside that page div and not outside. Now there is one more thing to be aware of with jQuery Mobile and that is the famous document ready event that we use with jQuery Mobile and Java Script. If we want some script to execute for every page that gets loaded here then it's quite common to come in and using jQuery attach to the document and wire up a function to the ready event and what I'll do inside of the ready event is just raise an alert that says, document ready, we'll save this and see how it behaves. So let come into the browser and do a hard refresh on the home page and there you can see we get document ready. Now go into the store. ( Pause ) And if I'm expecting the ready event to fire every time a page loads and if I have features in each of these pages that depend on that behavior then I'm going to run into trouble. It turns out there's only one document ready event ready fired in the life of this app because all the other page is are loaded into a DOM that's is already in the ready state. With jQuery Mobile, if you have some script to execute every time a page gets loaded into the DOM, what you want to catch instead is the page entered event. So I'm going to go to the document and find page undersocre init to a function and again we'll just raise an alert so we can observe how this behaves, put alert page undersocre init and save this and come back into the view. There's a bunch of other interesting events that you might be interested into. We'll look some of the-- say in the later module. But for now let me just do the hard refresh and see that we get page undersocre init document ready and now when I come to the store and load additional pages, I'm still getting page undersocre init each time. So I can go to the rock section, that does a page undersocre init, go back, I get a page undersocre init when the previous page loads. And so page undersocre init is something you want to pay attention to and possibly use if you're writing scripts for the jQuery Mobile pages. And again we'll talk about some other events that happen during the page life cycle in a later module. Now finally if all of this Async behavior makes you worry or it breaks to much of your site, you can always turn it off and that's easy to do. There some options, there's a bunch of options that you can set for jQuery Mobile and one of those options is to turn off the Ajax enabled feature. ( Pause ) So I'm going to create a script tag before jQuery Mobile loads. And what I'm going to do is this time wire up an event on the document called mobile init and set that to a function that walks up to dollar dot mobile, a lot of the features of jQuery Mobile are exposed through that object. Again we'll look at a lot of this in a later module and I'm going to say Ajax enabled is false. Save this and let's come out and go to the home page again, I'll do a hard refresh and start all of the server. And what we'll see now is that I get page undersocre init and document ready and then when I go to the store, I get the bottom alert that was loaded that previously we wouldn't see because that page will get grafted into the browser. We get that bottom, we get the page undersocre init, we get the document ready, we get all these scripts firing. We're also going to see the browser flash and refresh as it redraws the whole screen. In fact if I go in and take out some of these alerting functions and we comment out those alerts, let's go into the index and comment out those bottom alert and let's just see how this site behaves with the Ajax feature disabled. Come back to the home page and I'll do a hard refresh just to make sure every thing is loaded in here. Go to the store, see rock, we're not getting any of those transitions now, to the state still works and it's just not quite as pretty. But on the flip side more of our scripts might work because this is an existing application that we're trying to modify for jQuery Mobile. It might be easier to start of for some of these Ajax features turned off and then slowly get things working. ( Pause )
Cached Pages
Something related to our last clip on page navigation is page caching. We've seen that jQuery Mobile has the ability to asynchronously load pages into the DOM and for the rest of this module; I will enable that feature again by commenting out the code that set Ajax enabled to false. In the last clip we also saw how we can have multiple page divs in the DOM at the same time. And this can lead to elements with duplicate IDs if we aren't careful. But there's another problem lurking too. jQuery Mobile will by default remove a page from the DOM if that page was loaded asynchronously with Ajax. One reason it does this is to reduce the amount of memory used by a browser because if you have a site with hundreds of pages getting loaded into the DOM you might run into a memory problem on a small device. Now there are some caching options you can use to keep pages around in the DOM. But we'll talk about those in the last module for this course. The one thing to be aware of is that by default, the initial page that loads into the browser doesn't leave the DOM. jQuery Mobile only removes pages that were loaded by Ajax. And that means that the initial page doesn't refresh any data on the screen unless you are binding page undersocre init and loading data with Ajax. To demonstrate what I'm talking about, I have a simple filter inside of this MVC application that is going to count the number of hits for each controller action inside the application. And we'll render that hit count here in the mobile layout page so it appears everywhere inside the application. And with that in place, I will run the application and we'll see that it should initially launch and say that I have been to this particular page one time, very good. And now let me get to the store and it will tell me I've been here one time. And now let me go back to home, it still saying one time, go to the store two times. Let's go to rock, that was my first time there but go back to the store three times. So you can see this counter is working everywhere except for the initial page that was loaded into the DOM. And if you watch the DOM explorer as you're navigating through an application that has Ajax enabled, you'll see why because that initial div with data role equals page that comes from this first opening home link on the application is always in the DOM, it never goes and request that from the server again. Unfortunately, there's really no good switch in jQuery Mobile that you can use to disable that feature, instead there's a bit of a work around that works in most circumstances and I'm showing you that here, it's going to work for our application. Essentially, we're hooking into the page hide event. We're using the jQuery Mobile on method to say, cache any page hide events on this document now or in the future. What we're going to do is anytime the page hide event fires, we're going to pick up the event target and remove it from the DOM. And the event target is always going to be the page. So if I save that and now let's come back in and do a hard refresh on this home page. Now we've been here two times. I go to the store. We've been here four times now. When I came back to the home page and its incremented now near three times because we kicked that page out of the cache. We kicked it out of the DOM and now each time we comeback here to jQuery Mobile has to fix that page and bring it back in. Again, for static content this might be something that you want to happen. You want that page to stay in the DOM so it's fast and responsive and you never have to go to the server to get it. We're talking about some of the caching APIs in the last module.
Dialogs
Similar to the concept of a page, jQuery Mobile can also display a modal dialog. The primary difference between a page and a dialog are that when you launch a dialog with jQuery Mobile, a dialog doesn't go into the browser history. So it will not pop up again just because the user hit the forward or back buttons on the browser. Dialogs and pages are pretty interchangeable in jQuery Mobile. You can launch any page as a dialog and to demonstrate this I have a simple view for the home controller named About. About is just going to display some text and include a back button. A back button in jQuery Mobile is any link that has a data-rel attribute with a value of back. A data-rel attribute is like the HTML rel attribute. We used it to describe the relationship between a document and the link document. And I've also have given this link a data role of button which means jQuery Mobile will style the link to look like a clickable button. I'm going to render this view as a partial view in MVC which means it's going to render without using the layout template at all. So this view will render without having the header, the footer and all the other stuff the, the underscore layoutmobile.CSHTML view adds to every other content view. If we look at it in a browser, it is literally just the text and the link that we see inside of this view and it doesn't even have any styling of behavior here because since I'm going there directly in a browser, the jQuery Mobile scripts are not loaded, it doesn't look like what its going to look like if we actually go to it from the navigation bar. Because now when I click About jQuery Mobile is going to load up that content asynchronously, put it into the DOM, the jQuery Mobile scripts and styles are applied and we have a page. I'll be at a page that looks a little bit different because I didn't give a slightly different data theme, data theme of E and also because it's rendering without putting in the header or the navigation bar or footer. Now any link that you have in a dialog will close the dialog and follow that link. In our case the link is the back button, when I click it, it just takes us back to the view that where we came from. And if I use the forward button, I can get back to that page again. Now let's change that link to launch as a dialog which is as easy as coming into our layout view and where that link is I'm going to specify another data-rel attribute, this time to say that this has a relationship of dialog jQuery Mobile when it follows this link it should bring in that the content and display it as a modal dialog. Now we'll have some different behavior. Let me refresh the browser 'til we get that new markup and click about. And there's our dialog box that pops up. I can go back, reload that dialog and it's not in my history so it doesn't matter if I move forward and backward, it's not going to pop up again, that's the primary difference between a dialog and a page. ( Pause )
Summary
In this module we looked in pages and dialogs in jQuery Mobile. And I demonstrated how jQuery Mobile implements asynchronous page navigation features for us by default which makes for smooth transitions between the pages of an application. We saw the impact to that Async navigation and how we could have multiple pages loaded in the DOM at the same time. And we know that that can create problems for an application if we aren't careful. That goes back to the opening module when I said that you need to work with the jQuery framework and understand some of the conventions it uses. If not, you can run into trouble with the scripts not executing or pages being cached forever. I did show you how to turn off the Ajax features with the Ajax enabled option and that's a good option to take if you're trying to get an existing application targeted at desktop browsers to work on mobile devices with jQuery Mobile. But if you're starting from scratch or if you've watched this course you know what to look for now and you might be able to work with the Async navigation in your application. In the next module, we're going to fix some of the ugliness that still remains in our application and pretty this up by looking at some more jQuery widgets that we can take advantages of.
jQuery Mobile Widgets
Introduction
Hi. This is Scott Allen, and we are in the process of working on the ASP.NET MVC Music Store to make the application better for mobile devices. In this module, we'll look at a few more of the available jQuery Mobile widgets and see how we can use them to improve the mobile experience for our user. We'll look at buttons, collapsibles, grids, form elements and more, and we'll continue where we left off in the last module.
Collapsibles
Now that we have a header and navigation bar in place, and we understand a bit more about page navigation with jQuery Mobile, it's time to take a look at the content in the opening page of the application and see how we can make this a little bit better. It's not very pretty right now the way an album title spent two lines and we have dots appearing in the list of the best albums here. So I want to change the markup for the home page but only for the mobile browsers and that, again, means adding a mobile version of a view. In this case, the index view of the Home controller. So I have copied the index view to Indexed.Mobile and right now the two files are identical but we're going to change the mobile view. And the widget I have decided to use for the home page is the jQuery Mobile collapsible widget. The collapsible will allow us to get more information on the screen and allow the user to see all the top promoted albums but also give them the ability to drill into an album and see more information without waiting for the page to load. So I'm going to remove the list items here and add the markup for a collapsible, control K, and control D to do some formatting. And you can see that a collapsible is just a div with a data role of collapsible and every collapsible should have a header and some content. The header, we'll put into an H3, the content in a paragraph, and now instead of building an unordered list, I'll just use a div here to contain a series of collapsible divs. Let me save that view and come into our mobile browser and refresh and you can see we have some improvement. Now, the titles don't spend two lines, some of them are cut off. We could make some tweaks here. For instance, I could override the default, H3 font size that jQuery mobile puts in place by going into my own style sheet for mobile devices and adding something that targets the H3 headers inside of an album list and makes the font size 12 pixels. Let's refresh this again, and that's looking even better still, some titles still wrap off the end but that's okay. And now, what I have is something that the user can quickly scroll through those and expand things to see more about an album. But there's still a few things here I don't like. First of all, I'm not sure why we do not have a space between fresh and off. Let me try to fix that real quick, that's going to be in the index view. Let me just put a space there. And the other thing I don't like is the space that does exist between this collapsibles. I wish there were more of a set and it turns out there's a collapsible set widget in jQuery Mobile. So if I go to this containing div and say this is actually a collapsible set and save those, we'll have a slightly different effect here. And now, all of our collapsibles combine into one set, there's no space between the two, everything fits on the screen for me, and I can still expand this to draw in and see more information about any particular album.
Grids
The next issue I'd like to address is inside this collapsible. When it expands, the text can wrap around and appear underneath the image. It would be nice to adjust the layout to a more table-like or grid-like interface where the image and text are in a two-column grid with the image on the left and the artist, price and buy link on the right. To make a grid with jQuery Mobile, you just need to apply some CSS styles. There's no grid widget or control, it's all about CSS layout. So the paragraph tag that we have here, I'm going to change it to be a div and I'm going to assign it a class of ui-grid-a. Now, there's several different grids that are available through jQuery Mobile in CSS. There's grid A, B, C, and D. That's a two-column, three-column, four-column, and five-column layout. We just need a two-column layout so I'm going to pick the A grid here. With the grid container in place, I also need to identify the columns or the blocks that are inside of that grid. That's also done with CSS classes. So I'm going to take this image, hit control K, control S to bring up the Surround With menu. And say that I want to surround that markup with a div, and that div is going to have a class of ui-block-a. So it'll be the first block that appears on the left side of the grid. We need to do the same thing with the span here. This will become, instead of a span, also be a block level element which would be a div and we'll give it a class of ui-grid-b. Save all this and refresh this on the mobile browser. And now, we have a little more of the layout that we want. To improve this just a little bit more, we could surround each piece of information with a block level element like a div. So we'll go ahead and surround the artist with a div. We'll surround the album price with a div and finally, the link to buy this. Save that and refresh one more time. And we have a much nicer appearing layout. (Inaudible) very easy to work with in JQM, it's just a little bit of structure in CSS and, of course, CSS-based layouts are very flexible that now we can move things around and if we work with a designer who knows what they're really doing, it's easy to make changes to this.
Buttons
The next issue to address in this mobile app is the Buy Now link. Links work perfectly well in mobile browsers, of course, they have to, they're fundamental to the web but they do have two downsides. One problem is that some links are hard to click. The anchor tag is an inline element and without styling, it will take up a minimum amount of space, and they could be just hard to push just right if you're using fingers on a phone screen. The other disadvantage is that they do make it look like we are in a web browser instead of an application. And as we saw in the last module, it's easy to transform a link into a button. All I need to do is come into my anchor tag and give it a data role of button. I'm also going to give it a theme so that it stands out just a little bit. So with those two changes, I can come back to the browser. Let's refresh it and open it up and voila, I have a button although the text doesn't really fit on that button. In some scenarios, if you are space-constrained, what you can do is make a mini button which is just data-mini equals true. And what the theme will do behind the scene is use a slightly smaller font for that button which just might allow it to fit into some places. Now, a mini button is shorter than a regular button but it's still going to be just as wide because by default, buttons will take up as much horizontal space as possible. In fact, if I flip this over into landscape mode, you can see the Buy Now button stretches to the edge of the screen or rather to the edge of its container which is a column inside of the grid that we built. And if that's not the behavior that I want, I can make an inline button. So I can say data-inline equals true and now the button will only take up as much horizontal space it needs and that also gives you the ability to stock buttons beside each other if there's enough space. Also, buttons can have icons. If you search the jQuery Mobile site for the word "icon," you'll find the 20 or so icons provided by jQuery Mobile out of the box. When I want to use an icon, I just need to set the data-icon attribute. I'll set that equal to check or should be a check mark. And now, we will refresh this browser and take a look. And by default, that icon position will be on the left side and just so you know there are icons for every set of themes. So if I change this theme to the e-theme, we'll see how the icon can also change color and be themed appropriately. And let's also set the data-icon position, you could set this to left top bottom. We'll also try it to the right. There's also a no text icon positioning which means literally, the button will have no text. That's actually if you remember how we placed the little Home icon here at the top of the page. But with those changes in effect, let's come back and refresh. And now, we have a yellow button, a yellow icon and the icon is on the right-hand side of that button. Finally, you can group buttons by horizontally and vertically. A horizontal grouping is somewhat similar to what the nav bar is doing here. But let me also come in here, let's get rid of the inline attribute. And let's say we wanted to stock two buttons on top of each other and we'll give them different themes just so they can appear a little bit differently. One can be E, one can be B and if I refresh this page and we look at it now, there's the two buttons on top of each other. Of course, with the icon, now, we don't have enough room for the text, so we'd have to sacrifice one of the other there. But you notice there's a space between the buttons and they look like two individual buttons. If I want to group them together, all I need is a containing element. So let's do this trick again, control K, control S to surround with some HTML which will be a div. And I'll give that surrounding div a data-role equal to controlgroup. Let's save this and let's refresh one more time. And now, you can see those buttons form a grouping of two buttons. There's no margin between the two and the inside corners aren't rounded.
Listviews
Although I demonstrated the list view in the opening module of this course, I thought we'd comeback and take a look at a few additional features the list view provides. What we're looking at now is the list of music categories, and I've placed them into a list view which looks different than what we had in the opening module. It's still rendered by a mobile view and it still starts off with an unordered list that has the data role of list view, but the inside is a bit different. First, I'm using list dividers. Dividers are a great way to categorize items in your list and maybe they don't make much sense for those list of genres because we don't have many. But if you have a list of say 100 contacts, having these list dividers would allow a mobile user to swipe through the list and quickly see where they are. You can put any content you want inside of a list divider, but here, we've essentially grouped the list items by the first letter of their name and we displayed that letter in the divider. This is as easy as creating a list item with a data role of list divider and it gets the B theme by default unless you specify something else. Another feature that we've put in here is the count bubble. A count bubble gives the user an indication of how many things they should expect to see when they select one of these list items and navigate. The count bubble is achieved just by adding a span with the class of ui-li-count and jQuery Mobile styling takes care of the rest. One thing I'll point out about this count bubble, it's the only feature we've added so far where we've had to modify code outside of a view. So far, we have been changing this application to support mobile devices and we haven't changed any controllers, any models, no database tables, no queries, no business logic. We haven't touched anything outside of a view. But to get this album counts into the list view, I created a view model so I could efficiently get the count of albums per genre. And the nice thing is the desktop view could now take advantage of this information too because this view model is built in the controller that uses a link query to get that information out from the database in an efficient manner. I wanted the controller to be responsible for building this model because if we let the view do the counting, we could have too much logic in the view and we could also have a very inefficient page that uses too many database queries. If you're familiar with the famous select N plus 1 problem with ORMs, that's what I'd be talking about. What we have here works well in its performance.
Finger Friendly Forms
jQuery Mobile provides a complete set of what they call finger-friendly form elements. These are your standard texts inputs, radio buttons, and check boxes and it's all the standard HTML you're accustomed to writing. But jQuery Mobile comes in to enhance those inputs and form inputs to make them look stylish and to make them finger-friendly. For example, this is the view where a customer can provide the shipping information when they order music. Apparently, this was written with MVC3 before we had MP3 downloads. But it's going to render the usual input type equals text and input type equals submit elements enhanced so that they have rounded corners in a very Web 2.0 look. Just like any other input, I can go in to here and type into this, when I click submit at the bottom, it's going to post that to the server, all the values are processed the same. And you could do the things that you would usually expect with form elements. For example, you can add a disabled attribute and an input will be disabled. The user can't select it, they can't type into it. And you could also do things specific to jQuery Mobile. For example, you can use data-many equals true and the input will be styled to be slightly smaller, slightly shorter that is, you can save in vertical space with data-many. You can also save data-role equals none and jQuery Mobile will not enhance the form elements. That's what's happening here with the PromoCode input. The way that you do this with an HTML helper in ASP.NET MVC is passing the anonymous object with additional attributes that you want to set on the HTML, and MVC will extract those properties and set the values on the HTML. The trick with data-attributes is that because the "dash" is an illegal character and a C-sharp property name, we have to use an underscore. So data underscore role, MVC will convert the underscores to dashes and render data-role equals none. You can see my promo input as a regular boring text input now, it doesn't have the smooth, rounded corners that we aimed for. Now, because the MVC Music Store doesn't quite make use of all the possible form inputs, I'm going to temporarily remove that bit of markup and replace it with a different form that has pretty much everything that you can use in jQuery Mobile. And let's start at the top and talk about some of these. Let me also then save this view of course and do a hard refresh on the browser so hopefully this will pop out. So the first form input on that screen is the input type equals text, it has the rounded corners. The one thing I want to point out is that all of the form inputs in this view are wrapped inside a div with a data role of fieldcontain. Fieldcontain is something that tries to give you a responsive design and align the label in a way that it's going to look good. So, on a device like this that is very space-constrained, the label will typically always appear above a particular input. But if you get on to bigger devices, it can lay things out in a different manner. To demonstrate that, let me bring over a larger emulator. This one has a lot more space vertically and horizontally. It's a 540 by 960 device. And if we toggle this thing into landscape mode, what you'll see is the text input label aligned itself to the left and it's beside that input. That's making use of the available real state here, that's responsive design, that's something that could be done with media queries. And that just happens for free when you wrap things inside of a div with a data role of fieldcontain. And jQuery Mobile does expect you to have a label for every input to make this accessible. But if you're trying to save space, one thing you can do is add a class to this label which is ui-hidden-accessible, that's still going to make that label available to accessibility technologies like screen readers. And then you could use something like an HTML5 placeholder attribute to put some watermark text there. So we'll add a placeholder text input. And now, I'll be coming in and refresh this page. And our text input appears inside of the input, so I'm going to start typing and I put something inside of there that replaces the placeholder text. And speaking of HTML5 attributes, you can also use the HTML5 input types like e-mail, URL, telephone, time and date, and if the browser supports anything special for them, then you'll get the advantages of things like popping up the keyboard that lets you put in an e-mail address a little bit easier. They all work with jQuery Mobile. So the second input on the screen is our Textarea. This one is sort of nice because it will grow. The more information that I put inside of there, the more lines that I put inside of there, so, there's no need for an internal scrollbar there. We have a search input, input type equals search. When I type in here, I should be able to click the X to clear it out if I want, and that's a special feature of the search input. Next, we can see the flip switch, this is something where you toggle between two states and you can call them whatever you want on and off, true and false. You can set the initial state with a data-attribute. But the user can slide that with their finger to be toggled between two states. A different type of slider happens when you use input type equals range. Any input type equals range automatically gets a slightly different look in the other sliders and the inputs that we have. Now, we have something that we can drag around to increment and decrement a number. So inside of that input type equals range, you can specify a minimum value, you can specify a maximum value, and you can use data-highlight equals true to specify that as that thing slides to the right, it should fill things up and highlight the area that is being filled up on the left side. That is where the blue it comes from here. And, of course, we could change the data theme if we wanted change that color too. There is also a step increment that you could specify so I could say step equals two, otherwise, the default is one. Now, if we refresh this page and slide down here, every time I move a little bit, it's going to jump by two instead of one. And that particular widget is something that you can get on a lot of browsers that support the HTML5 input type equals range. What jQuery Mobile will do to enhance that is also add the input here where a user can type in the plus and minus dashes and that's in addition to being able to slide this thing around. Next, we have a set of checkboxes. You'll notice that not only are these inside of a fieldcontain, but they're also inside of a controlgroup. And we talked about controlgroups earlier with buttons. With checkboxes, it's the same premise. Instead of having individual checkboxes with margin in between each checkbox that as if all these checkboxes were inside on a single widget, it has a four rounded corners and they're stocked up together nicely. All of the colors and themes are provided by jQuery Mobile when it enhances the standard checkbox. So this is another case where you're just using standard HTML input type equals checkbox. And what jQuery Mobile will do is come along and make this a little more touch-friendly because I can hit anywhere inside of here with a finger and it's going to select or unselect that checkbox. You can also group checkboxes inside of a controlgroup horizontally. So here's a set of checkboxes with data type equals horizontal inside of a data role of controlgroup. And the way this renders is like this under font styling. And this is working out nicely because I only have single letter to put there inside of each checkbox. And we can do same thing with radio buttons. So this is a vertical grouping of radio buttons. Of course, with radio buttons, I can only have one selected at a time. And, of course, the checkboxes can also be laid out horizontally. Data type equals horizontal, that gives us the ability to select this layout view. They will wrap when there's not enough horizontal space so you do have to be careful and think about the size of the device where you're going to use a horizontal grouping. And finally, we have a couple instances of select controls. Interestingly enough, these don't quite render quite right on app or mobile. They should have some rounded corners and that's not quite coming through so that could be a bug in the browser. That could be a slight bug jQuery Mobile. And finally down here at the bottom, we have two buttons. They are stocked inside of a grid, that's why they're appearing next to each other. There's cancel and there's submit, they're themed slightly differently so they have different colors. And if we flip that over into landscape mode, it looks a little bit better. So again, another case where you have to be very cautious about how much horizontal space you really have on some of these devices. And again, the programming model on the server is no different from what you do today. Even though these inputs are all enhanced by jQuery Mobile, they submit values just like the regular HTML elements. In many cases, these are still just HMTL elements behind the scenes. So there's no server-side difference in the programming model. Client-side, you might have to do some different things in specific scenarios, but we'll talk about some of that in the next module when we look at the Script API.
Summary
In this module, we looked at more jQuery Mobile widgets. We saw some more features of the button and list view widgets and learned how to do things like have list dividers and use the data dash menu mode. We also looked at all of the form elements that jQuery Mobile will enhance, and it enhances everything from a simple input type equals text to sliders and checkboxes making them all finger-friendly and increasing the usability of your sight for mobile devices. In the next module, we'll be writing some script code to interact with these widgets as we take a look at the jQuery Mobile programming API.
jQuery Mobile APIs
Introduction
Hi, this is Scott Allen. In this module, we're going to look at the API of jQuery Mobile. We'll cover some of the events, defaults, selectors and methods that you can use and we'll look at some specific scenarios where you need to write some script. A lot of this will be familiar if you've done some jQuery programming in the past. But there are some twists with jQuery mobile. We'll start off by reviewing some of the defaults. ( Pause )
Defaults and Data Attributes
We've actually changed the default settings once in this course already when we said, AJAX unable to falls and temporarily turned off asynchronous page loads. But I want to show some of the other defaults settings you can change. These global default settings must be set before the jQuery mobile script loads because they impact the initialization of the framework. So we need to bind to the mobileinit event before we load the jQuery mobile script and inside of this event handler, I'm showing you two new options. A page load error message which you can customize and we're also changing the default page transition. The default is to fade a new page into view when it's loaded asynchronously. Now we've seen that we want pages to flip. So let's try this on, on a desktop browser first. I have to say configure to serve mobile pages even to a desktop browser right now. And we'll see if I get into another page, we get this flip transition. And if I get into About page, I currently have that setup float exception immediately. We can see we get the new error message. Let's also try this in a mobile browser. And if I get to the About page, we get the same error message. You'll notice the flip doesn't work. We still have a fade effect. What's happening is jQuery mobile will fall back to the fade effect if the browser doesn't support 3D transforms and that's what happened here. So, we still have a transition and jQuery mobile tries its best to do what we want but it does have to work within the limitations of the browser sometimes. Let me all so pull up the documentation for jQuery mobile because you'll need for your programming against the API and you probably wonder what some of the other defaults are. The documentation is on the jQuery mobile website, just go to the Docs section. And if you want to see things about, let say the default, we'll go to Configuring defaults. And if we scroll down here, we can see some of the other options that we can configure globally during that mobileinit event. For instance, if you don't like the CSS class that's being used when a button is in active state, you can set that with active button class and there's a whole slew of defaults here that you can toggle including the pageLoadErrorMessage which we just did and the pageLoadErrorMessageTheme which we'll able to change the color of that little pop up that says, "oops." And while I'm in here, let me also show you the data dash reference. So, if I comeback to the API section in the documentation, there's a section called data attribute reference. Throughout this course, we've been using data dash attributes to set roles and make elements inline and more. And you might have wondered what all the available data dash attributes are. Here's a page that will tell you every single data dash attribute the jQuery mobile looks for. It will also tell you what widgets you can use with what attribute. So here, I can see that both button and checkbox have a mini form I can set with data dash mini. I also want to call out the enhancement attributes which are down here just a little further because you can use this almost everywhere. If I say data dash enhance equals false, jQuery mobile will not enhance a piece of markup. So, for example it will not turn a text input into a fancy input with rounded corners. Also, I can turn off the asynchronies page loading globally with the AJAX enabled default setting, we did that during a mobileinit event in a previous module. But if there 's just one page or one form that I don't want to load asynchronously or have it asynchronously post, I can put data dash ajax equals false. I can put that as an attribute on a hyperlink, on a button, on a form and jQuery mobile with not intercept events to make asynchronous request. This could be important if you want to do a file upload for example. A form with an input type equals file, you'll need to use data dash ajax equals false to make that work because file upload cannot be asynchronous, at least not without using some other plug ins and technologies. ( Pause )
Swipe and Orientation Events
jQuery mobile adds a number of events that you can handle and these events generally fall into two categories. There are events that are five when things happen in the environment, like the user swipes the screen with their finger and there's also a page life cycle events. To demonstrate some of this event, I've added some script that will bind to the swipeleft event on a document. And when that happens we'll just open an alert box and tell the user that they swipe to the left. And what we'll see is that-- this works any mobile browser. So I have to just click the mouse button, move to the left, leave up the mouse button and I get the swipeleft message. It works on a mobile browser but what might surprise you is it is also works in a desktop browser. See, you can wire up, swipeleft, swiperight, or just the plain swipe event on different elements, not just on a document, and then respond to these events. A swipe might mean that the user wants to scroll something or navigates somewhere, you can call back to the server and refresh data to the screen, anything is possible. We can also use an event to find out when the user changes the orientation on the device from portrait to landscape. We can do this by going to the window. I'm going to bind the orientationchange event and wire that up to a function that takes an event parameter. And inside of here we will just alert event.orientation. And so, information about that changes past in to the event handler. Let's try that in a mobile browser. I'm going to do a hard refresh first. And then I'll hit alt R to change the orientation, now I'm in landscape mode. Hit alt R, now I'm in portrait mode. So, that's an event you might catch when you want to redraw some of the screen or perform some other activity that changes how the page are structured. A lot of times you can deal with orientation events just using media queries. If you want to change the layout of the page, just have media queries that check the orientation of the device and apply different style roles and these things are taken of for you automatically. And again what might surprise you, instead if we open up a desktop browser and let me do a hard refresh. And we start changing the width of this, we just change into landscape mode, portrait mode, it doesn't work so well, it's the alert box because I cannot actually get the window open far enough but it does change between landscape and portrait depending on the size of the browser window.
Page Events
What we just looked at are some of the events that will fire when things happen in the world around us. But there are also framework events to tell us when pages are loading, hiding, and being created. We've used one of these events before when we did some work in the pageinit event. There are 12 different events thant can fire during a page life cycle, and these are all events you can subscribe to and do work in script code if you need to modify how the framework behaves or add something to your page. For example, you can handle the pageload event which will fire when the page has been retrieved from the server and loaded into the DOM. You have parameters that will give you the URL, use to load the page, a data, the XML HTTP request object, use to make the request, there's a lot of information available and this is all documented in the pages on the documentation site I showed you earlier. Just look for the events section under the API header. Another event you might be interested in is pageloadfailed. If you do not like the default response of jQuery mobile which is to show that little pop up about an error, you can handle this event and redirect user to another URL or creating more persistent error message to make sure the user didn't missed the error message. The other two events I'll point out are pagecreate which happens when the pages is created in the DOM but before jQuery mobile runs and starts enhancing the page and then there's pageinit which happens after jQuery mobile has gone through and change an ordered list into list views and selects into slide toggles. So, the pages there, it's been initialized and all of the finger friendly controls are in place. As a quick demo of wire up, a few of these events all at once, and what we'll do is when anyone of them fire will alert on event.type. Type will contain the name of the event that is fired. Let's try this in our mobile browser. I'll do a hard refresh to make sure the script is in here and you can see we get a page create method. Remember create is before jQuery mobile does its magic then we get pageint. So, now everything in the DOM should have all the jQuery mobile classes and things like that attached to it. And we have pageshow on that is actually presented in the browser window. And pagechange also fires to let us know that there's a new page being displayed. Now, let me navigate over to the store, we'll get a pagecreate, a pageinit, a pageload, a pagehide for the home page that's not going away, a pageshow to let us know that this new page is here, and a pagechange just to let us know that something has changed. ( Pause )
Dealing With Dynamic Updates
What I want to do next is put some of these events together and demonstrates some things that you should be aware of with jQuery mobile. So let's imagine that here underneath of the header, we want to create an unordered list and we'll give that unordered list an id of eventList. And during this demonstration by the way, I'm going to introduce some bugs, that I run into all the time with jQuery mobile, some mistakes that developers make. And I'll demonstrate this so you can see what happens then we'll comeback and fix them. But we have an unordered list with an id of evenList and the idea is that, as certain events changed, I want to add list items to this eventList that contain what event just happened. And we'll give this-- the unordered list a data role of listview to see how it behaves as we're dynamically adding data to this. So, one of the scenarios I want you to think about when we're doing this demonstration is, what happens if inside of our page someone clicks on a button and it call to the server using jQuery, using AJAX, and they pull back some JSON. I'm using jQuery templates to initiates some new things and paste them into the DOM, that sort of the scenario that I want you think about here. When an event happens, we're going to dynamically create list items and add them to this unordered list and see how it behaves and see some of the things we have to do to get this working. We'll have a little more complexity in our script. So, what I'm going to do is create a module in JavaScript by creating a function, that's going to execute all by itself, and inside of this function, we can still bind the various swipe events. A swipe event and the orientation event but instead of alerting, now what I want to do when those things happen is add something to that unordered list. So let me also create a function here, addToList as a function or you give me a message and this function will just turn out into a list item and append it. So we'll have to say, let's create a new list item with jQuery, let's say, it's text equal to whatever message you pass in to me and then we're going to append that to eventList which is a variable that I want to have initialized when the page is been up. And so doing a swipe left, I can say, addToList, swipe left. Doing orientationchange, I can say addToList, event.orientation. And we're going to need a little bit of initialization code here. We don't want to grab eventList right away with jQuery selector. We need to wait until the page initializes. So, of course, that's why I can say document, please bind the pageinit event to a function and inside of that function, we can say, eventList equals go out and find the thing with an id of eventList. So, let save that script and see how it behaves in a mobile browser. I'm going to do a hard refresh on the home page. And let's try an orientation change, there's landscape, there's portrait, swipe to the left, swipe to the left, and some things seemed to be working here, we're obviously getting those events, we're obviously creating list items and adding them to that unordered list, but this doesn't look like the list view or custom to with jQuery mobile. Something is not being enhanced here properly. We're going to comeback to that problem a little bit later but now, I actually I want to concentrate on different problems. These events do seem to be working and since I put that unordered list here in the layout mobile page, it should-- that should be available on every page that we go to. So, if I go to the store and I swipe left, and I swipe left, and now nothing seems to be happening. What about the orientation? That didn't seem to work either, let me go to home, and oh, it appears that, I was updating that same event list all the time. So, even on the store page when I was swiping left, it was coming back and adding items to that unordered list that was over here on the home page. And this is something that I see jQuery mobile developers run into. Remember that initial page, it's into the DOM and it stays there. Unless we have some script to kick it out of the DOM, it's going to stay in there, it's very stateful. And what happens is, we caught the pageinit event and we got ourselves pointed to that particular event list that it's on the home page and there's really a couple of different problems there. When I load that store page, we're loading another page asynchronously, that's using the same layout mobile template and that means if I open the document explorer here, I would see two unordered list with an id of eventList, inside to the same HTML body, and that's a bit of a problem. Even though this pageinit is firing, each time a page gets initialized, when we just put in an alert to let you see that we are going out and selecting the eventList on that other page loads. And now I comeback to home and do a hard refresh. We have a pageinit there, I go to the store, we have a pageinit there but that particular pageinit, it's still going to find the event list that was on the home page, because it's the first one in the DOM. So, you might think there's an easy solution to this which is to come down and instead of using an id here, let's just give this unordered list a class of eventList. And then during pageinit we can go out and select anything with the class of eventList but if you're thinking ahead a little bit, you might realize what flow is going to happen next. Let me go home and do a hard refresh and actually let's get rid of the alert. For pageinit now, we know that fires, let me do a hard refresh, let me do a swipeleft, swipeleft, we got two swipelefts there. Let's go over to the store, swipeleft, swipeleft-- it seems to be working better now. I got those two events there but we comeback to the home, and we got-- another way the two swipes on the home page but also the two swipes from the store page because our selector now that's going out and finding the unordered list, it's finding every unordered list with the class of eventList and there's two of those now in the page. There's the one that was initially loaded, the home page, and there's the store page. If I comeback to the store now, it gets reloaded so that event list gets cleared out but remember that home page is still in the DOM. So one way to fix this problem would be to use that little bit of script code that we used earlier to make sure we kick the home page out of the DOM when it gets hidden. Another way to fix this would be to just turn off the AJAX enabled feature in the global options like we did earlier in this course. But let's see if there's another way to fix this problem. What I really what to do is that, when the page changes whether it's from home to store or store to home, I want to hook up the unordered list that is in that page. And so one way to that is instead of handling pageinit, I'm going to handle pagechange because pagechange gets two parameters. It gets an event that represents your typical event with the target. It also gets a second parameter that has some information inside of it like the fromPage and toPage. fromPage is where you came from, toPage is where you're going. And so, if I say that the eventList, when we select that, that it should stay and scope of that toPage, then we should only get that one ordered list. So, if you're not familiar with that syntax in jQuery that saying, find anything with the class of eventList but limit it to this context. So, ui.toPage is going to point to the div with a data role of page, that represents the page that just got brought in and displayed and that's the only event list that I want to find. So, let's try this again. I'm going to save this. Let's try a hard refresh again and let me do a swipeleft on the home page, that looks good, move over to the store, swipeleft here, that's looking good, comeback to home and it didn't pick up those other two that I did on the store page. Well, I can do one here to get three swipelefts, comeback to the store and of course, it's been reloaded. Now, it's down to zero but it-- now at least my swipes here on the store page are not also putting themselves in the event list for the home page. So, I'd like to think this demonstration so far. So there's a couple problems that jQuery mobile developers typically run into. One is-- sometimes handling the wrong event. For instance, if we were to try to handle document.ready here, with the event list, also it doesn't work because it's been known now, there's only one document, ready event and it fires when the first page loads in. Another problem jQuery mobile developers typically run into is dealing with the stateful pages and having a page that hangs around and you end up with multiple elements that have the same id in the DOM at the same time. That's another problem to watch out for. And then the third problem again, because you have multiple pages in the DOM at the same time, sometimes your selector and jQuery can return more than you thought it should and you just need to scope the selector sometimes by using something like the toPage or the fromPage. But we still have one additional problem here which is that my unordered list-- the list items inside of it, they don't look like a list items that are in the list view that we've been working with before. They should be styled and enhanced by jQuery mobile and the problem here is that jQuery mobile does it styling and enhancing in between pagecreate and pageinit. So if I add something dynamically to a list view, well, jQuery mobile doesn't know that, so it's just another list item that's in that unordered list, it's not going to have any of the classes or behavior that jQuery mobile uses to enhance those list items. And the way I'm going to have to fix that problem is by looking at some methods that I can invoke with jQuery mobile widgets and jQuery mobile in general.
Methods
jQuery mobile provides a number of methods that you can invoke on widgets and also on the dollar sign dot mobile object itself and we're going to need some of these methods if we're going to get our list view working. If you're familiar with jQuery UI, the methods for JQuery mobile widget are similar. For example, there's a refresh method that we can invoke on the list view and this is something that you need to do if you dynamically add list items to the list view. You'll need to invoke refresh afterwards. The syntax for this, I will do this immediately after appending a list item, is I need to go up to the eventList, invoke the listview method on it and then pass in refresh as a sting to say this is, ultimately the function that I want to call, refresh this list view. And if I do this, and save, and they come out to our mobile browser and refresh, then it should be able to swipeleft and now this looks like the list view that I expect with jQuery mobile. Refresh gives the framework a chance to go in and apply the correct spells (phonetic) and wire up the right events for this list view to behave the way we expect. But what if I was creating the entire list dynamically? In other words, instead of having a ul here, we didn't have a list and we'll just create one on the file if it doesn't exist. So again, the scenario to think about is you've just called the server, you retrieved some data from the server, now you want to put something on the page and have it in a jQuery widget, how's that going to work? What I'm going to do is, we'll first look for the eventList in the toPage because it just might exist already but if eventList.length is zero, in other words, it didn't find an eventList and what we'll do is, we'll say, eventList equals create an event list for (inaudible) and I think I'll pass in toPage so it knows the proper page to place that eventList in. Now we need a function, var createEventList, let's say function to takes a page as a parameter. And inside of here, we'll use some jQuery to create that unordered list. So, I can say the list is, use jQuery to create an unordered list, we'll still give it a class of eventList, we'll give it a data role of listview. And ultimately if we're going to return that list but we also need to figure out where to put that list, where on the page does it go. So I'm going to use some special selector syntax. The place where we want to put this is after the header in the given toPage that was handed to us. So, we need to be able to find the header and here some special selector syntax that you can use with jQuery mobile. Colon jqmData will allow you to walk up and inspect a jQuery mobile data dash attributes. For instance, I can say, role equals header and that should be able to find the header in that given page. I could also select the content area, footer area, I could select by any data dash attribute just by removing the data part of it. So, if we found the header, we can say after the header, put this list into the DOM, and then we should have a list that we can work with. So let me save this, and we'll try refreshing it in the browser and see if this works correctly and we'll try a swipeleft and a swipeleft and all of a sudden we've lost our list view again. Even though we're calling evenList.listview, refresh, every time we add a new list item, and then the problem is now, is that some unordered list was never initialized correctly. The unordered list was added to the DOM with a data role of list view. A jQuery mobile is not actively monitoring every insertion into the DOM and then enhancing things. So what we have to do is go out and tell jQuery mobile that this is supposed to be a list view. So this is just list.listview. Just like you were doing jQuery UI, there's method for every widget, that's the method we're invoking. And now let's go out and refresh this browser again, and do a swipeleft. So now, we're dynamically creating a list view, dynamically adding items into it, this should work even when we go to the store page. We'll try landscape and portrait and the swipe and comeback home and everything seems to be working perfectly now. So, when you are dynamically creating content, remember you'll need to initialize your widgets and you might need to refresh your widget if you're adding things into them or changing properties or updating certain attributes on them. In addition to methods on the widgets, there's also a method on the dollar sign dot mobile object itself. You can see the full list from the documentation but the two most important ones are loadPage and changePage. loadPage will asynchronously grab a page and load it into the DOM without displaying it, changePage will asynchronously load a page if it's not already there, and then display it. So a quick demonstration of this, let's imagine if the user swipes to the right, always want to go to the store page where they can browse the categories. So, I can say, document, that's bind to swiperight, and that's going to be attached to a function but it's going to do some navigation. So we'll say, $.mobile.changePage and we need to pass in URL of the page that we want to go to. So I'm going to use some, ASP.NET MVC Helpers to say, I want the URL for the action, the index action of the store controller, that's going to have be unquote and we'll use single quotes to keep things a little bit clear here. And let me save that, and see if it works. We'll come out to our browser, we'll do a refresh of the page. I'll do swipe to the right, and we end up on the store page. Comeback home again, swipe to the right, and we're on the store page, so that seems to work well. And that is what changePage can do for you programmatically, changing what page you're on instead of the user clicking a link to navigate. ( Pause )
AJAX, JSON, and One Last Trap
Finally, since we've been talking about asynchronous request grabbing JSON, and updating the screen. I put together a little example, where the store index views that show the categories of music that builds a UI almost entirely from JSON data. We'll start with the view itself. The only HTML emits is the h3 header and an empty ul element with an id of genreList. You might wonder if it's safe to use an id here but this is not the layout template master, this is an individual view to load a single page into the DOM. So, as long as none of the other individual views have the custom id, we should be safe. The reason id values were a problem in the previous demonstrations was because we were rendering those elements from the master review which all this individual views plug into, thus, every page was rendering elements with those ids and that guarantees a collision. Here, this is the only view that's going to render a ul and it's the only view that's going to render an element. Any kind of element with an id of genreList. We're also going to include some script. And as soon as the script gets on the browser it will wire itself to the pageinit event. There's a set of bug here, but we'll get back to that. When the init even fires, it's going to be have a URL that it can use to call, GetGenreData from the store controller. So, let take a look at that real quick. This particular action method, queries all of the music categories out of the database and builds a summary that is a model object that we used in the last module to carry genre information and album counts to our view. This is nearly the same object as before, but now I'm also adding a URL property to the object so we can send the client, the URL to get the details for this genre and that one last thing that we need to do on the client. And we're returning an array of this summary objects and serializing them into JSON. So back on the client, we will use jQuery's getJSON method to call that action and retrieve the list of genres, then when the call is successful, we'll update the unordered list element with the new information. Update list when there's a template into the list which we'll talk about the temple in just a bit. But then it also invokes the listview method on that list to make sure jQuery mobile can enhance the list into a list view. So, the render method is a part of JS render, and JS render in one of many client-side templating libraries for JavaScript. Another popular one is jQuery templates. So I've included the JS render, JavaScript library into the layout view and then this is the template. It's essentially a view in JavaScript. It expects in a way of objects with URL, name, and number of albums, and the syntax with the double curly braces is telling the template where to insert this property values for each object that it finds. The template will construct the all the HTML we need and then the update list function will graph the result of the template into the list. Let's try this in the browser, I'll do a refresh to make sure we have the latest, and go to the store and everything works, it doesn't look any different but using JSON and some templates, we can now update to state that, anytime, perhaps have a refresh button here, or just a timer that periodically refreshes those album counts. If it changing rapidly that something the user might want to see, this is how you can use JSON and jQuery mobile together to create very responsive apps, but there is a set of problem here. If I follow the length to look at alternatives albums and then I comeback, my list view is destroyed, if you spotted this bug already, pat yourself on the back because it's a tough one to spot. ( Pause ) Previously, we were binding to the pageinit event on the document just to log the event and catch the event for every page. And unfortunately that the same thing we're doing here, we're catching the pageinit event for every page that gets initialized which isn't what we want to do. When I navigated to that list of albums, pageinit fired again, the AJAX calls is firing off and initializing the list view multiple times and that causes things to break. Ideally, when you wire up the pageinit event for a page specific initialization logic like we have here, they wired up using a page identifier, so you only kept the event for your specific page. Unfortunately, in this case, a div with a data dash role equals page is in the master template. And that something I have to think about it in my MVC application. If I want toPage div to be defined in the layout template or in the individual view because if I move that into this individual view, it becomes easier to give it an id and write scripting logic that associated with that page. Also, I need to be careful when I have a script embedded in a view like this, because it's easy to have it loaded multiple times than wire up the event multiple times which is also happening here. There are two easy solutions to this problem. One is to set AJAX enabled defaults like we did earlier in the course that always clears up some of these stateful problems. Another easy solution is to use the jQuery one method. Instead of buying, one says to wire up this event but only handled it once and then unsubscribed and let it die. So, we wire up the event, for the next time pageinit fires which will be for us, for this page it's getting loading and then it goes away and we don't have to worry about other pages that raise page initialize, because this handle it will be gone. So, if I save that and comeback into our browser and let's go to the home page and refresh. We should be better off now, it go to the store and look at list of albums. I comeback, list view still there, got a Blues, Ballads is working again.
Summary
In this module, we look at APIs, events, methods, default settings, and some of the things to watch out for with jQuery mobile, and that brings us to the end of the course. Along the way we turned the desktop only application into an app that supports both desktop and mobile and we only needed to touch a few views in one controller. I hope you enjoy this course and I hope you found it educational.
Subscribe to:
Post Comments (Atom)
No comments:
Post a Comment