Showing posts with label jquery. Show all posts
Showing posts with label jquery. Show all posts

Using jQuery/jQuery mobile in Worklight 5

In the Using JQuery Mobile in WorkLight application entry i talked about how to use JQuery Mobile in the Worklight application, that was using Worklight 4.2.1 In the Worklight 5.0 version one big change is that the JQuery Mobile framework comes out of the box with Worklight, and unlike before we dont have to manually include the jQuery framework on the page and we wont have to use the following code

<script>
  var jq = jQuery.noConflict();
</script>
Instead if you take a closer look at the JavaScript file generated by the Worklight studio you will notice that it binds jQuery global variable at window.$ and also assigns it to WLJQ global variable like this

window.$ = WLJQ;
function wlCommonInit(){
 // Common initialization code goes here
}
But problem is that it does not assign the global jQuery variable to window.jQuery variable, so when you want to use jQUery mobile you have two options either you can include your own version of jQuery in addition to the one that comes OTB or you can modify the code like this

window.$ = WLJQ;
window.jQuery = WLJQ;
function wlCommonInit(){
 // Common initialization code goes here
}

Using jQUery .load(), ajax wrapper method

In the Posting data to REST service in PhoneGap/Cordova i talked about how to use jQuery to make a HTTP POST call to a REST service. In that case i used $.post() method, But jQuery also provides one wrapper method load() that makes making AJAX call even simpler. I wanted to try that so i changed the sample to use the load(). The load() method works on jQuery selector, so it first makes AJAX request and then takes the response of AJAX method and inserts it in all the elements returns by the selector. In my case first i did create a HTTP form and when i click on it, i want to collect all the values entered by the user on the form and use them to make HTTP POST call and once the result it returned i want to insert it into html div with id equal to result. This single line of code does everything $('div#result').load('http://192.168.1.101:9000/ManageContact/rest/contact', $("#insertContact :input")); First i am selecting a div with id equal to result by using $('div#result') selector, the http://192.168.1.101:9000/ManageContact/rest/contact parameter is the URL to which the XHR request should be made. Second paramater $("#insertContact :input") says that select the form insertContact and return all the inputs on it in array format and submit them to the URL.

<!DOCTYPE html>
<html>
<head>
<title>Manage Contact</title>
<script src="http://code.jquery.com/jquery-1.6.4.min.js"></script>
<script type="text/javascript" charset="utf-8">
  $(document).ready(function(){
    $("#submit").click(insertContact);
    function insertContact(){
      console.log("Entering insertContact()");
      $('div#result').load('http://192.168.1.101:9000/ManageContact/rest/contact',
   $("#insertContact :input")); 
      return false;
    }
  });
</script>
</head>
<body>
  <form id="insertContact">
    <table>
      <tr>
        <td>Contact Id</td>
        <td><input type="text" name="contactId" /></td>
      </tr>
      <tr>
        <td>First Name</td>
        <td><input type="text" name="firstName" /></td>
      </tr>
      <tr>
        <td>Last Name</td>
        <td><input type="text" name="lastName" /></td>
      </tr>
      <tr>
        <td>Email</td>
        <td><input type="text" name="email" /></td>
      </tr>
      <tr>
        <td><input type="submit" id="submit" name="submit" 
  value="Submit" /></td>
      </tr>
    </table>
  </form>
  
  <div id='result'></div>
 
</body>
</html>
The load() method looks at the second parameter to figure out if the request should be made using HTTP GET or POST. If i wanted to submit the form using HTTP GET i should have used $('div#result').load('http://192.168.1.101:9000/ManageContact/rest/contact', $("#insertContact").serialize());

Adding support for back button in jQuery Mobile page in PhoneGap/Cordova application

When your building PhoneGap/Cordova application you might have noticed that it does not get browser back button. But if your using JQuery mobile you can take care of this situation by adding data-add-back-btn attribute at page level, I did that and this is screen show of how my page looks This is the index.html file that i used for my application.

<!DOCTYPE html>
<html>
<head>
<title>Device Properties Example</title>

<script type="text/javascript" charset="utf-8" src="cordova-1.7.0.js"></script>
<link rel="stylesheet"
  href="http://code.jquery.com/mobile/1.1.0/jquery.mobile-1.1.0.min.css" />
<script src="http://code.jquery.com/jquery-1.6.4.min.js"></script>
<script
  src="http://code.jquery.com/mobile/1.1.0/jquery.mobile-1.1.0.min.js"></script>

<script type="text/javascript" charset="utf-8">

</script>
</head>
<body>
  <div data-role="page" id="page1" data-add-back-btn="true" 
  data-back-btn-text="Previous" data-title="First Page">
    <div data-theme="a" data-role="header">
      <h3>First Page</h3>
    </div>
    <div data-role="content">
      <a href="#page2">Go to page 2</a>
    </div>
    <div data-theme="a" data-role="footer">
      <h3>Copyright stuff</h3>
    </div>
  </div>
  
  <div data-role="page" id="page2" data-add-back-btn="true" 
  data-back-btn-text="Previous" data-title="Second Page">
    <div data-theme="a" data-role="header">
      <h3>Second Page</h3>
    </div>
    <div data-role="content">
      <a href="#page3">Go to page 3</a>
    </div>
    <div data-theme="a" data-role="footer">
      <h3>Copyright stuff</h3>
    </div>
  </div>  
    <div data-role="page" id="page3" data-add-back-btn="true" 
 data-back-btn-text="Previous" data-title="Third Page">
    <div data-theme="a" data-role="header">
      <h3>Third page</h3>
    </div>
    <div data-role="content">
      <h3>Hello from page 3</h3>
    </div>
    <div data-theme="a" data-role="footer">
      <h3>Copyright stuff</h3>
    </div>
  </div>  
</body>
</html>
My index.html page has 3 JQM pages inside it, each one of them has 2 attributes first is data-add-back-btn which says that you want Jquery Mobile to manage back button when their is page in the history, you enable it by setting its value to true. The data-back-btn-text attribute is used to set title of the back button, in my case i am setting it to Previous

Posting data to REST service in PhoneGap/Cordova

In the Consuming REST service from PhoneGap/Cordova entry i talked about how you can consume a REST service in PhoneGap/Cordova application using JQuery. In that application i was making a HTTP GET request to get data from REST service, i wanted to figure out how to make a HTTP POST call to REST service to create a new contact. You can download the index.html for my application from here This is screen shot of how my form looks like
This is screen shot of success message that i get if the contact insertion is successful

I followed these steps to build the CordovaManageContact application
  1. I followed the instructions in Getting Started with Android to build application that points to index.html inside the application, i tried it once to make sure that it works
  2. Then i changed the index.html file for my application to look like this
    
    <!DOCTYPE html>
    <html>
    <head>
    <title>Manage Contact</title>
    <script type="text/javascript" 
    charset="utf-8" src="cordova-1.7.0.js"></script>
    <script src="http://code.jquery.com/jquery-1.6.4.min.js"></script>
    <script type="text/javascript" charset="utf-8">
      $(document).ready(function(){
        $("#submit").click(insertContact);
      });
      function insertContact(){
        console.log("Entering insertContact()");
        $.post("http://192.168.1.101:9000/ManageContact/rest/contact",
        $("#insertContact :input").serializeArray(), 
        function(json){
          if(json== null || json == 'undefined')
            alert("Insert failed");
          else
            alert("Insert successful");
        });
        return false;
      }
    </script>
    </head>
    <body>
      <h3>Insert Contact</h3>
      <form id="insertContact">
        <table>
          <tr>
            <td>Contact Id</td>
            <td><input type="text" name="contactId" /></td>
          </tr>
          <tr>
            <td>First Name</td>
            <td><input type="text" name="firstName" /></td>
          </tr>
          <tr>
            <td>Last Name</td>
            <td><input type="text" name="lastName" /></td>
          </tr>
          <tr>
            <td>Email</td>
            <td><input type="text" name="email" /></td>
          </tr>
          <tr>
            <td><input type="submit" 
      id="submit" name="submit" value="Submit" /></td>
          </tr>
        </table>
      </form>
    </body>
    </html>
    
    When the user clicks on submit button control goes to insertContact() method., In this method i am using jQuery $.post() call to submit the form to http://192.168.1.101:9000/ManageContact/rest/contact URL. I am using jQuery to collect all the values entered by the user into form and encode them by calling $("#insertContact :input").serializeArray() method. After the post request control goes to the anonymous function which is third parameter of the $.post() method. In that method i am checking if i got response if yes that means insert was successful if not that means insert failed, that is because my REST service is structured not to send anything back in case of insert failure.

Consuming REST service from PhoneGap/Cordova

In the Using JPA in REST web application deployed in Jetty entry i blogged about how to build a ManageContact JPA service which will allow me to perform CRUD operations on CONTACT table using a REST service. I wanted to figure out how i can consume this service from PhoneGap/Cordova application so i built this sample application which will call http://localhost:9000/ManageContact/rest/contact rest service, read the contact list returned in XML format, parse it and display contact list to the user, you can download the sample application from here This screen shot displays the list of contacts that i got from the REST service I followed these steps to build the CordovaManageContact application
  1. I followed the instructions in Getting Started with Android to build application that points to index.html inside the application, i tried it once to make sure that it works
  2. Then i changed the index.html file for my application to look like this
    
    <!DOCTYPE html>
    <html>
    <head>
    <title>Device Properties Example</title>
    <script type="text/javascript" charset="utf-8" 
    src="cordova-1.7.0.js"></script>
    <script src="js/jquery.js"></script>
    
    <script type="text/javascript" charset="utf-8">
      $(document).ready(function(){
        $("#getContactBtn").click(getContactList);
      });
      function onDeviceReady() {
        console.log("Entering index.html.onDeviceReady");
        getContactList();
        console.log("Exiting index.html.onDeviceReady");
      }
      function getContactList(){ 
        console.log("Entering getContactList()");
        $.ajax({
          url : "http://192.168.1.101:9000/ManageContact/rest/contact",
          dataType:"xml",
          cache: false,
          error:function (xhr, ajaxOptions, thrownError){
            debugger;
                    alert(xhr.statusText);
                    alert(thrownError);
                },
          success : function(xml) {
            console.log("Entering getContactList.success()");
          
            $(xml).find("contact").each(function() {
              var html = '<li>' + $(this).find("firstName").text() 
              + ' ' + $(this).find("lastName").text() +'</li>';
              $('#contactList').append(html);
            });
            console.log("Exiting getContactList.success()");
          } 
        });
        console.log("Exiting getContactList()");
      }
    </script>
    </head>
    <body>
      <h3>Contact List</h3>
      <button id="getContactBtn">Get Contact</button>
      <ul id="contactList"></ul>
    </body>
    </html>
    
    When i click on the Get Contact button the getContactList() method gets called, it uses the jquery $.ajax() method to make a call and once the result is returned, it uses logic in the success method to parse the xml and get all the contact records and adds each one of them as list item in the contactList list. I have the REST service running on my machine along with the Android emulator which has the PhoneGap application but when i tried to access the service at http://localhost/ManageContact/rest/contact,http://127.0.0.1/ManageContact/rest/contact it did not work. I tried to map the ip address 192.168.1.101 to demohost.com in my host file but Android did not understand that mapping either. I had to use ipconfig command to figure out the ip address of the machine and then use it.

Using JQuery Mobile in PhoneGap/Cordova application

JQuery Mobile is one of the most popular Mobile UI frameworks, i wanted to figure out how to use it in Cordova application so i built a sample application that uses JQuery Mobile and also the Cordova device API to display device related properties in JQuery Mobile UI. You can download the sample application from here. I followed these steps to build this application
  1. I followed the instructions in Getting Started with Android to build application that points to index.html inside the application, i tried it once to make sure that it works
  2. I did download the jquery.mobile-1.1.0.zip from JQuery Mobile Download page
  3. I did unzip the jquery.mobile-1.1.0.zip in c:\software folder
  4. Next i copied the css, docs and js folder from C:\software\jquery.mobile-1.1.0\demos into assets/www folder of my web application.
  5. If your just starting with JQuery mobile or playing around then you can even directly point to the CDN URL of JQuery Mobile without actually downloading it in your application, in that case you can skip step 2 and 3. You can simply add these lines in your HTML page
    
    <link rel="stylesheet"
     href="http://code.jquery.com/mobile/1.1.0/jquery.mobile-1.1.0.min.css" />
    <script src="http://code.jquery.com/jquery-1.6.4.min.js"></script>
    <script
     src="http://code.jquery.com/mobile/1.1.0/jquery.mobile-1.1.0.min.js"></script>
    
  6. Next change your index.html page to look like this
    
    <!DOCTYPE html>
    <html>
    <head>
    <title>Device Properties Example</title>
    <script type="text/javascript" charset="utf-8" src="cordova-1.7.0.js"></script>
    <!--  
    <link rel="stylesheet"
     href="http://code.jquery.com/mobile/1.1.0/jquery.mobile-1.1.0.min.css" />
    <script src="http://code.jquery.com/jquery-1.6.4.min.js"></script>
    <script
     src="http://code.jquery.com/mobile/1.1.0/jquery.mobile-1.1.0.min.js"></script>
    -->
    <link rel="stylesheet"  href="css/themes/default/jquery.mobile-1.1.0.css" />
    <script src="js/jquery.js"></script>
    <script src="js/jquery.mobile-1.1.0.js"></script>
    <script type="text/javascript" charset="utf-8">
     document.addEventListener("deviceready", onDeviceReady, false);
     function onDeviceReady() {
      console.log("Entering index.html.onDeviceReady");
      //var element = document.getElementById('deviceProperties');
      var html = "";
      html = html + "<li>" + 'Device Name: ' + device.name + "</li>";
      html = html + "<li>" + 'Device Cordova: ' + device.cordova + "</li>";
      html = html + "<li>" + 'Device Platform: ' + device.platform + "</li>";
      html = html + "<li>" + 'Device UUID: ' + device.uuid + "</li>";
      console.log(html);
    
      $("#deviceProperties").html(html);
      $("#deviceProperties").listview('refresh');
      console.log("Exiting index.html.onDeviceReady");
     } 
    </script>
    </head>
    <body>
    
     <div data-role="page" id="page1">
      <div data-theme="a" data-role="header">
       <h3>Hello JQuery Mobile</h3>
      </div>
      <div data-role="content">
       Device Properties
       <ul data-role="listview" data-inset="true" 
        id="deviceProperties">
    
       </ul>
      </div>
      <div data-theme="a" data-role="footer">
       <h3>Copyright stuff</h3>
      </div>
     </div>
     
    </body>
    </html>
    
    My application has one JQuery Mobile page whose content is defined using div with id equal to page1. The div with data-role equal to content defines the content of the page. By default it only has empty ul element, i am using onDeviceReady() JavaScript function to read device property and add them to the list.
This is screen shot of application once its loaded

Chaning pages dynamically using JQuery Mobile - use different .htmls

In the Changing pages dynamically in JQuery Mobile Worklight application i blogged about how you can use the JQuery Mobile JavaScript framework to create multi-page web application. In that example i built a web application which has only single HTML file that contains HTML fragments for different pages and i used following JavaScript to change page dynamically

function switchToPage2(){
  jq.mobile.changePage ("#page2");
}
One of the reader raised a very good question that it might not be good idea to have all the pages in same html and can we break the html's into different pages and answer is yes. I wanted to try that so first i did create a listcontact.html in the same directory as that of ManageContact.hml that has markup of only listcontact.html like this

<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8" />
<meta name="viewport"
  content="width=device-width, initial-scale=1.0, 
  maximum-scale=1.0, minimum-scale=1.0, user-scalable=0" />
<title>ManageContact</title>
<link rel="shortcut icon" href="images/favicon.png" />
<link rel="apple-touch-icon" href="images/apple-touch-icon.png" />
<link rel="stylesheet" href="css/reset.css" />
<link rel="stylesheet" href="css/ManageContact.css" />
<link rel="stylesheet" href="css/jquery.mobile-1.0.min.css" />
<script src="js/jquery-1.7.1.min.js"></script>
<script>
  var jq = jQuery.noConflict();
</script>
<script src="js/jquery.mobile-1.0.min.js"></script>
</head>
<body onload="WL.Client.init({})" id="content" style="display: none">
  <div data-role="page" id="home">
    <div data-theme="a" data-role="header">
      <h3>Contact List</h3>
    </div>
    <div data-role="content" id="pagePort">
      <ul data-role="listview" data-inset="true" data-filter="true"
        id="displayContact">
      </ul>
       <a data-role="button"
            data-transition="fade" href="#home"
            id="searchContact"> Home </a>
    </div>
    <div data-theme="a" data-role="footer">
      <h3>Copyright stuff</h3>
    </div>
  </div>
  <script src="js/ManageContact.js"></script>
  <script src="js/messages.js"></script>
  <script src="js/auth.js"></script>
</body>
The listcontact.html has the same structure as ManageContact.html (The worklight server does add more resources to the ManageContact.html as part of build process). Then i had to use following JavaScript to switch page

function switchToPage2(){
  jq.mobile.changePage ("listcontact.html");
}
and when i click on switchPage it does make forward control to listcontact.html, that works. But since we refreshed the whole page we loose the JavaScript context.

Changing pages dynamically in JQuery Mobile Worklight application

In the Creating multi page application with JQuery Mobile entry i talked about how to build a multi-page application with JQuery Mobile, in that i talked about how to switch page using static href's but most of time you would want to change page name dynamically after executing some JavaScript, so i changed my html like this

<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8" />
<meta name="viewport"
  content="width=device-width, initial-scale=1.0, maximum-scale=1.0,
  minimum-scale=1.0, user-scalable=0" />
<title>HelloWorkLightPhoneGap</title>
<link rel="shortcut icon" href="images/favicon.png" />
<link rel="apple-touch-icon" href="images/apple-touch-icon.png" />
<link rel="stylesheet" href="css/reset.css" />
<link rel="stylesheet" href="css/HelloWorkLightPhoneGap.css" />
<link rel="stylesheet" href="css/jquery.mobile-1.0.min.css" />
<script src="js/jquery-1.7.1.min.js"></script>
<script>
  var jq = jQuery.noConflict();
</script>
<script src="js/jquery.mobile-1.0.min.js"></script>
<script type="text/javascript"
  src="http://maps.google.com/maps/api/js?sensor=false"></script>
</head>
<body onload="WL.Client.init({})" id="content" style="display: none">
  <div data-role="page" id="page1">
    <div data-theme="a" data-role="header">
      <h3>First Page</h3>
    </div>
    <div data-role="content" id="pagePort">
      <a href="javascript:switchToPage2()">Go to second page</a>
    </div>
    <div data-theme="a" data-role="footer">
      <h3>Copyright stuff</h3>
    </div>
  </div>

  <div data-role="page" id="page2" >
    <div data-theme="a" data-role="header">
      <h3>Second Page</h3>
    </div>
    <div data-role="content" id="pagePort">
      <a href="javascript:switchToPage1()">Go to third page</a>
    </div>
    <div data-theme="a" data-role="footer">
      <h3>Copyright stuff</h3>
    </div>
  </div>
   <script src="js/HelloWorkLightPhoneGap.js"></script>
  <script src="js/messages.js"></script>
  <script src="js/auth.js"></script>
</body>
</html>
In this the anchor for change page is changed to point to JavaScript function that looks like this, you can use the jq.mobile.changePage("<pageid>")

function switchToPage2(){
  jq.mobile.changePage ("#page2");
}

function switchToPage1(){
  jq.mobile.changePage ("#page1");
}

Creating multi page application with JQuery Mobile

JQuery Mobile makes creating multi-page application very easy, I think its much better than using Worklight for creating multi-page application Take a look at this simple html page that i built which has 3 pages page1, page2 and page3, every page has link to the next page.

<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8" />
<meta name="viewport"
  content="width=device-width, initial-scale=1.0, maximum-scale=1.0,
  minimum-scale=1.0, user-scalable=0" />
<title>HelloWorkLightPhoneGap</title>
<link rel="shortcut icon" href="images/favicon.png" />
<link rel="apple-touch-icon" href="images/apple-touch-icon.png" />
<link rel="stylesheet" href="css/reset.css" />
<link rel="stylesheet" href="css/HelloWorkLightPhoneGap.css" />
<link rel="stylesheet" href="css/jquery.mobile-1.0.min.css" />
<script src="js/jquery-1.7.1.min.js"></script>
<script>
  var jq = jQuery.noConflict();
</script>
<script src="js/jquery.mobile-1.0.min.js"></script>
<script type="text/javascript"
  src="http://maps.google.com/maps/api/js?sensor=false"></script>
</head>
<body onload="WL.Client.init({})" id="content" style="display: none">
  <div data-role="page" id="page1">
    <div data-theme="a" data-role="header">
      <h3>First Page</h3>
    </div>
    <div data-role="content" id="pagePort">
      <a href="#page2">Go to second page</a>
    </div>
    <div data-theme="a" data-role="footer">
      <h3>Copyright stuff</h3>
    </div>
  </div>

  <div data-role="page" id="page2" >
    <div data-theme="a" data-role="header">
      <h3>Second Page</h3>
    </div>
    <div data-role="content" id="pagePort">
      <a href="#page3">Go to third page</a>
    </div>
    <div data-theme="a" data-role="footer">
      <h3>Copyright stuff</h3>
    </div>
  </div>
  
  <div data-role="page" id="page3">
    <div data-theme="a" data-role="header">
      <h3>Third page</h3>
    </div>
    <div data-role="content" id="pagePort">
      <a href="#page1">Go to first page</a>
    </div>
    <div data-theme="a" data-role="footer">
      <h3>Copyright stuff</h3>
    </div>
  </div>

  <script src="js/HelloWorkLightPhoneGap.js"></script>
  <script src="js/messages.js"></script>
  <script src="js/auth.js"></script>
</body>
</html>
When you want to move to the next page you use the link with value of href equal to the id of the next page

Invoking REST service that returns JSON from worklight

In the Invoking REST service from WorkLight i built a simple WorkLight application that makes REST call but the REST service in that case returns XML, To me if your building a REST service for access from Browser it would be much better idea to return JSON instead of XML that avoids one conversion. So with that goal in mind i wanted to change my application and i followed these steps for that
  • First thing that i had to do was change the REST service so that it returns JSON and also i made JSON as the default return type for service, you can get more information and download the sample application by using entry
  • Next i changed the ContactRESTService-impl.js, the worklight REST adapter that i am using for making the actual call,
    
    function getContactList() {
      var input = {
          method : 'get',
          returnedContentType : 'json',
          path : '/ManageContact/rest/contact'
      };
      return WL.Server.invokeHttp(input);
    }
    
    function searchContact(lastName){
      var input = {
            method : 'get',
            returnedContentType : 'json',
            path : '/ManageContact/rest/contact/search?lastName='+lastName
        };
        return WL.Server.invokeHttp(input);
    }
    
    Only change in this file is to let the WorkLight adapter know that i am expecting json as response type instead of xml
  • After that i had to change the way i am parsing the response and using it to display results to the user, the reason being now the structure of the response is different so the callback that reads the response and displays the result has to know about how to handle the changed structure.
    
    function loadContactSuccess(result) {
      console.log("Inside loadContactSuccess " + result);
      var html = '';
      try {
        if (result.status == 200) {
          var contactList = result.invocationResult.contact; 
          var i = 0;
          for (i = 0; i < contactList.length; i++) {
            var currentContact = contactList[i];
            html = html + '<li><a href="javascript:showContactDetail('
                + currentContact.contactId + ')">'
                + currentContact.firstName + ' '
                + currentContact.lastName + '</a></li>';
          }
        }
        jq("#displayContact").html(html);
        jq("#displayContact").listview('refresh');
        busyIndicator.hide();
      } catch (e) {
        busyIndicator.hide();
        displayError(e.toString());
      }
    }
    
    The highlighted code displays how i am getting the actual contact list from the response. This is how the response of the REST service looks like when i hit it directly
    This is how the response that WorkLight adapter is returning to the JavaScript client looks like

Creating multi page application in WorkLight

WorkLight allows you to create multi-page application, i wanted to try this feature, so i changed the Contact Search application that i created in Using BusyIndicator common control entry, so that, when executes a search for contact, it returns list of contact names, user can click on one of the contact name to go to the Contact Details page and that page has button that allows user to go back to Summary page. This is how my summary page looks like
When you click on the arrow button next to any user name it takes you to the Details page for that contact which looks like this
You can click on the Summary button on the Details page to go back to the Contact Summary page. I followed these steps to create this sample application
  1. First i changed the main or landing page of my app to mark the div where the new page should be inserted, i did that by setting value of id element to pagePort like this
    
    !DOCTYPE html>
    <html>
    <head>
    ....
    </head>
    <body onload="WL.Client.init({showLogger:true})" id="content"
      style="display: none">
    
      <div data-role="page" id="page1">
        <div data-theme="a" data-role="header">
          <h3>Contact DB App</h3>
        </div>
        <div data-role="content" id="pagePort" >
          <div data-role="fieldcontain">
            <fieldset data-role="controlgroup">
              <label for="textinput1"> First Name </label> <input
                id="contactName" placeholder="" value="" type="text" />
            </fieldset>
          </div>
          <a data-role="button" data-transition="fade"
            href="javascript:getContact()" id="searchContact"> Search
            Contact </a>
          <ul data-role="listview" data-inset="true" data-filter="true"
            id="displayContact">
          </ul>
        </div>
        <div data-theme="a" data-role="footer">
          <h3>Copyright stuff</h3>
        </div>
      </div>
      <script src="js/HelloDatabase.js"></script>
      <script src="js/messages.js"></script>
      <script src="js/auth.js"></script>
    </body>
    </html>
    
  2. Then i did create this new contactDetail.html page in the same directory as that of my first/landing page
    
    <div data-role="content" id="mainContent">
      <div data-role="fieldcontain">
        <fieldset data-role="controlgroup">
          <label for="firstName"> First Name </label> 
          <input id="firstName" placeholder="" value="" type="text" readonly="readonly" />
          <label for="lastName"> Last Name </label> 
          <input id="lastName" placeholder="" value="" type="text" readonly="readonly"/>
          <label for="email"> Email </label> 
          <input id="email" placeholder="" value="" type="text" readonly="readonly"/>
          
          <a data-role="button" data-transition="fade"
            href="javascript:showSummary()" id="searchContact"> Summary </a>
        </fieldset>
      </div>
    </div>
    
    The Details page shows the form with contact details and it has a Summary button that allows user to go back to the summary page, when you click on that button it will pass control to showSummary() method
  3. I had to make changes to my main JavaScript function to make it look like this
    
    var busyIndicator;
    function wlCommonInit() {
      busyIndicator = new WL.BusyIndicator('page1');
    }
    
    function getContact() {
      console.log("Entering getContact() REST service based version");
      var contactName = $('contactName').getValue();
      var invocationData = {
        adapter : "ContactWSService",
        procedure : "searchContact",
        parameters : [ contactName ]
      }
      var options = {
        onSuccess : loadContactSuccess,
        onFailure : loadContactFailure
      }
      busyIndicator.show();
      WL.Client.invokeProcedure(invocationData, options);
    }
    
    function loadContactSuccess(result) {
      console.log("Inside loadContactSuccess " + result);
      var html = '';
      try {
        if (result.status == 200) {
          var contactList = result.invocationResult.Envelope.Body.searchContactResponse.contactList;
          var i = 0;
          for (i = 0; i < contactList.length; i++) {
            var currentContact = contactList[i];
            html = html + '<li><a href="javascript:showContactDetail('
                + currentContact.contactId + ')">'
                + currentContact.firstName + ' '
                + currentContact.lastName + '</a></li>';
          }
        }
        jq("#displayContact").html(html);
        jq("#displayContact").listview('refresh');
        busyIndicator.hide();
      } catch (e) {
        busyIndicator.hide();
      }
    }
    function showContactDetail(contactId) {
      console.log("Show Contact Detail is clicked " + contactId);
      WL.Page.load("contactDetail.html", {
        onComplete : function() {
          console.log("After fragment is loadded ");
          jq('#mainContent').trigger("create");
          getContactDetails(contactId);
        },
        onUnload : function() {
          console.log("After fragment is unloadded ");
        }
      });
    }
    
    function showSummary() {
      WL.Page.load("contactSummary.html", {
        onComplete : function() {
          console.log("After fragment is loadded ");
          jq('#mainContent').trigger("create");
          
        },
        onUnload : function() {
          console.log("After fragment is unloadded ");
        }
      }); 
    }
    
    function getContactDetails(contactId) {
      console.log("Entering getContactDetails()");
      var invocationData = {
        adapter : "ContactWSService",
        procedure : "getContact",
        parameters : [ contactId ]
      }
      var options = {
        onSuccess : getContactDetailsSuccess,
        onFailure : getContactDetailsFailure
      }
      busyIndicator.show();
      WL.Client.invokeProcedure(invocationData, options);
    }
    
    function getContactDetailsSuccess(result) {
      console.log("Entering getContactDetailsSuccess");
      try {
        if (result.status == 200) {
          var displayContact = result.invocationResult.Envelope.Body.getContactResponse.contact;
          $('firstName').value=displayContact.firstName;
          $('lastName').value=displayContact.lastName;
          $('email').value=displayContact.email;
        }
        busyIndicator.hide();
      } catch (e) {
        busyIndicator.hide();
      }
    }
    function getContactDetailsFailure(result) {
      console.log("Entering getContactDetailsFailure");
    }
    
    I had to make quite a few changes in my JavaScript they are as follows
    • loadContactSuccess: The loadContactSuccess method gets called when you execute search and it gets back the result, this method generates one row each for the result. I changed this method so that when it was generating the list i did attach getContactDetails(contactId) method to each row
    • showContactDetail: The showContactDetail method will get called when user clicks on any of the user name, when that happens i am calling WL.Page.load("contactDetails.html" method which replaces the markup inside the page with contactDetail.html, this method also calls the getContactDetails which calls SOAP service with contactId to get details of the contact.
    • showSummary: The showSummary method will be called when user clicks on the Summary button on the details page, it again calls WL.Page.load("contactSummary.html" to replace the markup in the current page with the contact details

Forcing jQuery Mobile to re-evaluate styles/theme on dynamically inserted content

Today i was trying to build a JQuery Mobile application that has multiple pages. When user clicks on the page switch button i take the markup on the new page and insert it in the old page. One problem that i noticed is when you insert HTML into a page and that HTML has jQuery UI widgets then those widgets do not get evaluated, instead jQuery Displays them without any styles. You can solve this problem by calling the $('changedDiv').trigger("create") method with value of the div equal to id where the new markup got inserted


<div id='changedDiv'>
//Insert jQuery widgets here
</div>

Using JQuery Mobile in WorkLight application

In the Connecting to Database from WorkLight i blogged about how to build WorkLight application that talks to database, that application did not had good looking UI, so i made changes to use JQuery Mobile that makes building good looking UI much simpler. This is how my application looks like now
These are the steps that i followed
  1. Create a simple WorkLight application that invokes a SQL query you can use the steps mentioned in Connecting to Database from WorkLight
  2. First download the Worklight Starter Application for JQuery Mobile from Worklight Getting started page
  3. Expand the WorklightStarter_jQueryMobile.zip some where on your local disk
  4. Copy jquery.mobile*.js and jquery-*.js from the WorklightStarter_jQueryMobile.zip to the js folder of your application, Also copy jquery.mobile.*.css into css folder of your application
  5. Change the html page of your application to include the JQuery related css and js, this is how the html page looks for me
    
    <!DOCTYPE html>
    <html>
    <head>
    <meta charset="utf-8" />
    <meta name="viewport"
      content="width=device-width, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0, user-scalable=0" />
    <title>HelloDatabase</title>
    <link rel="shortcut icon" href="images/favicon.png" />
    <link rel="apple-touch-icon" href="images/apple-touch-icon.png" />
    <link rel="stylesheet" href="css/reset.css" />
    <link rel="stylesheet" href="css/HelloDatabase.css" />
    <link rel="stylesheet" href="css/jquery.mobile-1.0.min.css" />
    
    <script src="js/jquery-1.7.1.min.js"></script>
    <script>
      var jq = jQuery.noConflict();
    </script>
    
    <script src="js/jquery.mobile-1.0.min.js"></script>
    </head>
    <body onload="WL.Client.init({})" id="content" style="display: none">
     
      <div data-role="page" id="page1">
        <div data-theme="a" data-role="header">
          <h3>Contact DB App</h3>
        </div>
        <div data-role="content">
        
          <div data-role="fieldcontain">
                        <fieldset data-role="controlgroup">
                            <label for="textinput1">
                               First Name
                            </label>
                            <input id="contactName" placeholder="" value="" type="text" />
                        </fieldset>
                    </div>
                    <a data-role="button" data-transition="fade" href="javascript:getContact()" id="searchContact">
                        Search Contact
                    </a>
          <ul data-role="listview" data-inset="true" data-filter="true" id="displayContact">
            
          </ul>
    
        </div>
        <div data-theme="a" data-role="footer">
          <h3>Copyright stuff</h3>
        </div>
      </div>
     
    
      <script src="js/HelloDatabase.js"></script>
      <script src="js/messages.js"></script>
      <script src="js/auth.js"></script>
    </body>
    </html>
    
    In the head section first you should include jquery related css and js and also call var jq = jQuery.noConflict(); so that the jQuery $ character does not conflict with $ used by the prototype javascript framework used by worklight. Next use the jquery mobile template in the body section that divided the page into head, content and footer section.
  6. Add following code to the javascript file for your application
    
    function wlCommonInit(){
      // Common initialization code goes here
    }
    
    
    function getContact(){
      console.log("Entering getContact()");
    
      var contactName = '%'+$('contactName').getValue()+'%';
    
      var invocationData = {
          adapter:"mySQLAdapter",
          procedure:"searchContactByFirstName",
          parameters:[contactName]
      }
      var options ={
          onSuccess:loadContactSuccess,
          onFailure:loadContactFailure
      }
      
      WL.Client.invokeProcedure(invocationData, options);
    }
    
    function loadContactSuccess(result){
      console.log("Inside loadContactSuccess " + result);
      var html = '';
      if(result.invocationResult.isSuccessful){
        var contactList = result.invocationResult.resultSet;
        var i = 0;
        for(i =0 ; i < contactList.length ; i++){
          var currentContact = contactList[i];
          html =  html + '<li><a href="#">'+currentContact.FIRSTNAME +' ' 
       +currentContact.LASTNAME +'</a></li>';
        }
      }
      jq("#displayContact").html(html);
      jq("#displayContact").listview('refresh');
    }
    
    function loadContactFailure(result){
      console.log("Inside loadContactError " + result);
    }
    
    As you can can see i am using jq("#displayContact")> instead of regular $("#displayContact") this is the nonconflict version of JQuery that you must use in the WorkLight, but your still allowed to use the prototype based syntax for looking up objects as well like i did in $('contactName').getValue()