Building Cross-Platform Apps Using jQuery Mobile

2011. 5. 3. 10:12Java/Spring Framework

JQuery Mobile Site 주소 : http://jquerymobile.com/

--> 실제 이 곳에 들어가서 문서를 참고하시면서 만들어 보는 것이 가장 좋을 것 같네요
공부는 교과서 중심으로 해야 조치요~ ㅎㅎ





MSDN 블로그에 올라온 jQuery Mobile 강좌
원문 : http://msdn.microsoft.com/en-us/scriptjunkie/hh144955.aspx
▼ 아래 글은 윈문을 그대로 싹 복사 해 온거라서.. 문제가 되면.. 자삭할 께용

jQuery Mobile introduces a cross-platform and cross-device framework for developing mobile applications. It supports a wide variety of mobile browsers and delivers a unified user interface to the devices. It has simplified working with mobile browsers by abstracting away inconsistences between the vendors. Just as jQuery changed the way we wrote JavaScript , jQuery Mobile will change the way we build mobile web applications.

I recently used jQuery Mobile to build an application and was stunned at how smoothly the development process went. The framework is straight forward and well documented. While it’s still in alpha, you will find that it’s very stable and responsive.

As a web developer, jQuery Mobile is instantly rewarding because there isn’t much of a learning curve. Whatever your preferred stack is for developing web applications is – that’s your stack for developing jQuery Mobile applications. The sample application below is built using only browser capabilities (HTML/CSS/JavaScript), so you will find your editor-of-choice is perfect for following along.

Page Structure and Components

We are going to build a to-do application. The requirements are: 1) it works great on most mobile devices, 2) we can create new to-dos and assign them a priority and 3) we can remove to-dos from the list when we’ve completed them.

For this application there is only going to be one physical page, index.html. It’s going to reference the necessary JavaScript and CSS, and provide the main structure of the page.

  1. <!DOCTYPE html>
  2. <html>
  3. <head>
  4. <title>Mobile To-Do</title>
  5. <link rel="stylesheet" href="http://code.jquery.com/mobile/1.0a4.1/jquery.mobile-1.0a4.1.min.css" />
  6. <script src="http://code.jquery.com/jquery-1.5.2.min.js"></script>
  7. <script src="http://code.jquery.com/mobile/1.0a4.1/jquery.mobile-1.0a4.1.min.js"></script>
  8. </head>
  9. <body>
  10. <div data-role="page">
  11. <div data-role="header">
  12. <h1>Mobile To-Do List</h1>
  13. </div>
  14. <div data-role="content">
  15. Page content will go here
  16. </div>
  17. </div>
  18. </body>
  19. </html>

Aside from referencing the latest version of jQuery, we also reference jquery.mobile-1.0a4.1.min.css and jquery.mobile-1.0a4.1.min.js via CDN. This CSS and JavaScript file is where the jQuery Mobile magic lives.

In the body tag we provided the basic page anatomy for application: a header with our title in it and a section for page content. You will notice that sections of markup have a data-role attribute. This attribute instructs the framework on how to render that element and sometimes it’s content. Elements with a data-role attribute applied are often referred to as components.

If we point a browser to our page we find that these steps have gotten us well on our way to creating a cross-platform mobile application.

The jQuery Mobile framework has used the data-role attributes we supplied to apply styles, add attributes, and, in some cases, even restructure elements. If we examine the DOM once the page has been rendered by the browser, we find that the jQuery Mobile framework has been busy.

  1. <body class="ui-mobile-viewport">
  2. <div data-role="page" class="ui-page ui-body-c ui-page-active">
  3. <div data-role="header" class="ui-bar-a ui-header" role="banner">
  4. <h1 class="ui-title" tabindex="0" role="heading" aria-level="1">Mobile To-Do List</h1>
  5. </div>
  6. <div data-role="content" class="ui-content" role="main">
  7. Page content will go here
  8. </div>
  9. </div>
  10. </body>

It’s worth remembering that the jQuery Mobile framework modifies the DOM significantly as it often comes into play often when writing selectors for business logic.

We need to provide a list of existing to-dos and display the list when the user enters the application. jQuery Mobile provides a lot components for us to work with: toolbars, dialogs, buttons, select menus, etc. For the purpose of our list we are going to choose the list view component. Inside of our content component we add the list view markup.

  1. <ul id="todoList" data-role="listview">
  2. <li>Sample Item #1</li>
  3. <li>The Second Item</li>
  4. <li>3rd and Final Item</li>
  5. </ul>

We specified listview for our data-role attribute which will ensure that the framework treats the element and its content as a list view component. We also gave the component and id attribute as we will be referring to it later in our JavaScript code. Lastly, we added a few <li> elements to give us an idea of what the individual to-do items will look like.

It’s a good start but we are going to have different priorities for our to-dos; plus, we need a way to remove items. The framework makes dividing items and displaying a delete icon easy.

To create dividers, we add three new list items for our priorities and specify divider as their data-role. To add delete icons to the to-do items, we specify delete as their data-icon and wrap the inner value in an anchor tag.

  1. <li data-role="list-divider">Important</li>
  2. <li data-icon="delete"><a>Sample Item #1</a></li>
  3. <li data-role="list-divider">Normal</li>
  4. <li data-icon="delete"><a>The Second Item</a></li>
  5. <li data-role="list-divider">Low</li>
  6. <li data-icon="delete"><a>3rd and Final Item</a></li>

We have a nice mock of what our rendered list of to-dos will look like. Let’s move on to adding a new page for creating to-do items. One option for our new page is to add a physical html file for it. That method works great if you need to render the page dynamically with server-side processing but that’s not necessary for this demo. Since our page is going to be static, we can define a new logical page inside our existing index.html file.

  1. <div id="mainPage" data-role="page">
  2. <!—- omitted code -->
  3. </div>
  4. <div id="addPage" data-role="page">
  5. <div data-role="header">
  6. <h1>New To-Do</h1>
  7. </div>
  8. <div data-role="content">
  9. </div>
  10. </div>

We’ve added a new page component as a sibling element to our existing page component. We gave our existing page component an id attribute of mainPage and new the new page component the id of addPage. Let’s go ahead and create buttons to link these two logical pages using the ids we created for them.

  1. <div id="mainPage" data-role="page">
  2. <div data-role="header">
  3. <h1>Mobile To-Do List</h1>
  4. <a href="#addPage" data-role="button" data-icon="plus">New</a>
  5. </div>
  6. <!—- omitted code -->
  7. </div>
  8. <div id="addPage" data-role="page">
  9. <div data-role="header">
  10. <a href="#mainPage" data-role="button" data-icon="back">Back</a>
  11. <h1>New To-Do</h1>
  12. <a href="#mainPage" id="createButton" data-role="button" data-icon="plus">Create</a>
  13. </div>
  14. <div data-role="content">
  15. </div>
  16. </div>

We have used a new data-role value button on anchor elements which will instruct the framework to render them as button components in our header. We also used the data-icon attribute to specify which icon we would like displayed on the buttons. The framework handles linking the buttons to the logical pages using the href attribute. The result is navigation between our two logical pages with a sliding transition animation.

Let’s fill out our create page. We need to allow the user to specify a description and priority for the to-do. An input element will be perfect for the description and a select element will work nicely for specifying a single priority.

  1. <div data-role="content">
  2. <div data-role="fieldcontain">
  3. <label for="todoDescription">Description:</label>
  4. <input type="text" name="todoDescription" id="todoDescription" value="" />
  5. </div>
  6. <div data-role="fieldcontain">
  7. <label for="todoUrgency" class="select">Urgency:</label>
  8. <select name="todoUrgency" id="todoUrgency" data-native-menu="false">
  9. <option value="2">Important</option>
  10. <option value="1" selected="selected">Normal</option>
  11. <option value="0">Low</option>
  12. </select>
  13. </div>
  14. </div>

We didn’t specify a data-role for the input, select, and label elements as we did for the list view component. This is because the framework doesn’t need a hint on how to render these components since they are defined explicitly by the tag’s name. However we did wrap the new components in a fieldcontain component. While this component isn’t required, it provides some nice rendering benefits: 1) A single line will be rendered at the bottom of the set providing some visual separation of fields and 2) The orientation of label in respect to the control will be rendered optimally based on horizontal screen resolution of the device.

We also set the select component’s data-native-menu to false. This will cause the framework to render a theme-friendly menu of the list items instead of displaying the browser’s default drop down.

Tie It Together with JavaScript

We now have our two screens completely laid out. Next, we are going to reference some JavaScript files that will make our development a bit easier. We are going to use json2.js for serialization of JavaScript objects, jquery.cookie.js for working with cookies, and jquery.tmpl.js for binding html templates to objects. Save each of those in your scripts folder and reference them. Finally, create and reference a to-do.js file in your scripts folder which is where all our code will go.

  1. <head>
  2. <title>Mobile To-Do</title>
  3. <link rel="stylesheet" href="http://code.jquery.com/mobile/1.0a2/jquery.mobile-1.0a2.min.css" />
  4. <script src="http://code.jquery.com/jquery-1.4.4.min.js"></script>
  5. <script src="http://code.jquery.com/mobile/1.0a2/jquery.mobile-1.0a2.min.js"></script>
  6. <script src="scripts/json2.js"></script>
  7. <script src="scripts/jquery.cookie.js"></script>
  8. <script src="scripts/jquery.tmpl.js"></script>
  9. <script src="scripts/to-do.js"></script>
  10. </head>

Since we are working in browsers and not depending on a server-side storage or data, we are going to store the to-do data in cookies. We are going write a simple JavaScript class to encapsulate loading, saving, and reading the to-do data. This class will also encapsulate the serialization and deserialization of to-do data into JSON for storage in the cookie.

  1. $(document).ready(function() {
  2. var Model = function() {
  3. var items = [],
  4. load = function() {
  5. items = JSON.parse($.cookie("model") || "[]");
  6. };
  7. this.PRIORITY = {
  8. IMPORTANT: 2,
  9. NORMAL: 1,
  10. LOW: 0
  11. };
  12. this.save = function() {
  13. $.cookie("model", JSON.stringify(items), { expires: 365 }));
  14. };
  15. this.create = function(text, priority) {
  16. var result = { "text": text, "priority": priority };
  17. items.push(result);
  18. this.save();
  19. return result;
  20. };
  21. this.remove = function(item) {
  22. items.splice($(items).index(item), 1);
  23. this.save();
  24. };
  25. this.clear = function() {
  26. items = [];
  27. this.save();
  28. };
  29. this.getItems = function() {
  30. var result = [];
  31. for (var index = 0, max = items.length; index != max; index++) {
  32. result.push(items[index])
  33. }
  34. return result;
  35. };
  36. load();
  37. };
  38. });

We won’t dive into a deep explanation of how our Model class is working. You can dig into it for a greater understanding or just accept that it can load(), save(), remove() and getItems().

As with most jQuery code, we wrapped our code in the $(document).ready() event closure. Future snippets will omit this detail but should be contained in the closure as well.

Let’s create an instance of our Model class to work with along with an array to hold a reference to our list headers.

  1. var model = new Model(),
  2. listHeaders = [];

The model variable points to an instance of our Model class. The listHeaders array will contain each of the list items in our list view that are dividers, in our case the three that represent priority. Let’s add a custom data-priority attribute to each of the dividers to specify which priority the divider represents. These values will correspond to the constants we setup in the Model class: IMPORTANT = 2, NORMAL = 1 and LOW = 0.

  1. <li data-priority="2" data-role="list-divider">Important</li>
  2. <li data-priority="1" data-role="list-divider">Normal</li>
  3. <li data-priority="0" data-role="list-divider">Low</li>

Next, we create a self-invoking function to fill the listHeaders array with dividers.

  1. (function getListHeaders() {
  2. $("#todoList > li[data-priority]").each(function() {
  3. listHeaders[parseInt($(this).attr("data-priority"))] = $(this);
  4. });
  5. })();

We now that we have an easy way to reference our dividers by priority. Let’s create a function that will take a to-do object, bind it to an html template and insert the result under the appropriate priority divider. From our Model class, we can see that to-do objects simply have a text string and a priority integer.

  1. var renderItemElement = function(item) {
  2. return $.tmpl("<li data-icon=\"delete\" class=\"item\"><a>${text}</a></li>", item)
  3. .data("item", item)
  4. .insertAfter(listHeaders[item.priority]);
  5. };

We have laid the ground work to be able to create, save, and render to-do items. Now we can tie it all together in the create button’s click event.

  1. $("#createButton").click(function() {
  2. var priority = parseInt($("#todoUrgency").val()),
  3. item = model.create($("#todoDescription").val(), priority);
  4. renderItemElement(item);
  5. $("#todoList").listview("refresh");
  6. $("#todoUrgency").val(model.PRIORITY.NORMAL.toString()).trigger("change");
  7. $("#todoDescription").val("");
  8. });

We get the priority from the todoUrgency select component and the text from the todoDescription input component. We create a new to-do object using the model’s create function. This function also handles saving the object in the cookie. We call renderItemElement and pass the new object which handles binding and inserting the to-do into the DOM. Since we have dynamically added an item to the list view component, we call the refresh command to re-render the component. The final two lines of code reset the todoUrgency and todoDescription components to their original states.

Now that we can add items to our list, let’s implement functionality for removing them. We’ve already used the data-icon attribute to instruct the framework to render a delete icon on each list item. We just need to catch a list item’s click and remove the item from both the model and list view.

  1. $("#todoList").delegate("li.item", "click", function() {
  2. model.remove($(this).data("item"));
  3. $(this).slideUp(function() {
  4. $(this).remove();
  5. });
  6. });

Instead of attaching a handler to each item, we delegate the list item’s click event to the todoList. First we remove the object from the model. The remove method also handles saving the change to our cookie. Next we use a slide up animation for the visual removal of the item. Finally, once the animation has completed, we remove the element from the DOM.

As a final step, we need to render this initial view of all the items when the user first opens the application. For this we will create another self-invoking function.

  1. (function renderExistingItems() {
  2. $(model.getItems()).each(function() {
  3. renderItemElement(this);
  4. });
  5. $("#todoList").listview("refresh");
  6. })();

We use the model’s getItems function to get a list of existing items, which we loop through and render using the renderItemElement function. Once again, we call refresh on the listview component since the items were dynamically appended.

Conclusion

The demand for mobile applications is growing in both the consumer and business sectors. The demand for an application to support multiple devices is growing as a result of the device market becoming more divided. jQuery Mobile introduces a unique opportunity for applications to support multiple devices without developers needing to learn multiple platforms. Best of all as web developers we are working in the platform we love – the web.

This introduction only scratched the surface of what jQuery Mobile has to offer. For example the framework handles navigation to external pages seamlessly with AJAX. It provides a rich theming system for customizing the look and feel of your pages. And as mentioned before, there is a long list of useful components for rapidly delivering user interfaces.

Working example: http://examples.nickriggs.com/todo/

Source code: http://gallery.msdn.microsoft.com/scriptjunkie/jQuery-Mobile-Todo-b93169a9/file/139/1/todo-mobile.zip