Sunday, June 30, 2013

Saying Hello To jQuery Mobile

You find a lot of debate on native mobile applications vs applications running on web views. For me the main priority was to get an application done in the minimum time whilst trying to achieve the smoothness of a native app.

Looking through many content on the web, i ended up with two main options as follows;


This was actually a pretty well thought out framework which was if i am not mistaken, not free initially but made available for free since recent times. Sencha Touch 2 is said to deliver applications that resemble a native application in the Web view space. I was inclined to go ahead with Sencha Touch 2 initially, yet since it was based mostly on JavaScript and since my JavaScript knowledge is a bit rusty and given the time constraints, i had to give up that idea since the learning curve was too steep.

Are we not all fans of jQuery? Well i sure am. Loved the jQuery core API and the time it saved me having to deal with nitty gritty JavaScript details. And then they go and develop a framework for the mobile space. As the syntax was not alien for me as it was when i was looking at some Sencha Touch 2 code, i digged a bit deeper to find out the pros and cons of the framework. I actually ended up using JQM on top of PhoneGap.

All WebView based applications depend on the native browser's support in order to deliver animations to resemble a native application. One drawback in terms of JQM was that my application was experiencing a jittery feel on Android 2.3 (GingerBread). The issue there being that the native browser which comes with GingerBread had less animation support. All animations work fine on higher versions other than 2.3 and lower. This was a drawback i had to succumb to given my time constraints on developing a native application. 

So in this post i will give a glimpse of common problems i ran into whilst using JQM and how to overcome those issues.


  • How do you bind to events on a given page
Coming from some experience on the jQuery core library, i jumped into using the document ready method which was what i was using when building web applications and trying to bind DOM events after the document was loaded. 
Yet this is not how things work on the JQM world. Since all pages are made up of div elements in the JQM space, the pages are loaded using ajax requests which enables the framework to resemble a native app which would not have been possible if the pages refreshed on each page load. Following is a code snippet which shows how one should bind events to a particular page in the world of JQM.



 
<div id="firstPage" data-role="page">
 <div data-role="content">
   <h1>Content</h1>
   
   <a id="showalert" data-role="button">Click Me</a>
 </div>
</div>


<script type="text/javascript">
$( "#firstPage" ).on( "pageinit", function( event, ui ) {

$('#showalert').click(function(){
 alert('i was clicked');
});

});
</script>

You will see that the page bindings happen using the on() method which is the successor for the predecessor methods such as live(), bind(). Here we have two options, we can either use the pageinit option or the pageshow option. I would not delve into the details of the two since it is self-explanatory.


  • How to programmatically move to another page
By default, page transitions happen through the usage of href links to the page ID of the page you want to go to. But on certain situations, you need the ability to do page transitions based on say a button click. This is how you would achieve this with JQM;



<div id="firstPage" data-role="page">
  <div data-role="header">Hi!! </div>
  <div data-role="content">
  <a id="gotoPage" data-role="button">Go to second page</a>
  </div>
  
  <div data-role="footer">The End</div>
 </div> 
 
<div id="secondPage" data-role="page">
 <div data-role="content">
   <h1>Second page</h1>
   
   
 </div>
</div>


<script type="text/javascript">
$( "#firstPage" ).on( "pageinit", function( event, ui ) {

$('#gotoPage').click(function(){
  $.mobile.changePage( "#secondPage", { transition: "flip"} );
});

});
</script>

Here what we are doing is transitioning from the first page to the second page on a button click. The usage of $.mobile.changePage allows us to accomplish our goal. The second option passed into it is optional where we could define various configuration parameters. I have defined an animation for the page transition when it occurs.


  • Dynamically loading content to a List View
You would often want to populate data to a list view by perhaps reading data from an external system. It would not work by simply creating the HTML content and appending it to the unordered list element. Following is how this can be achieved;



<div id="firstPage" data-role="page">
  <div data-role="header">Hi!! </div>
  <div data-role="content">
  <ul id="myList" data-role="listview" data-filter="true"
    data-filter-reveal="true"
    data-filter-placeholder="Search List..." data-inset="true">

   </ul>
  </div>
  
  <div data-role="footer">The End</div>
 </div> 



<script type="text/javascript">
$( "#firstPage" ).on( "pageinit", function( event, ui ) {

var dataArr = ["one","two","three"];
var content = '';
for(i=0;i<dataArr.length;i++){
content+='<li>'+dataArr[i]+'</li>';
}

 $('#myList').html(content);
 /**
 The following two method calls will refresh the List view with the updated data after page initialization
 **/
 $("#myList").listview("refresh");
 $("#myList").trigger( "updatelayout");

});
</script>

I have used a Javascript array here, but your typical use-case will fetch the data from an external source. The two calls to the listview after appending the html content to the list is where the update of the list takes place. Otherwise you will just get an empty list with no data on it.


  • Revealing list view data only on search
Here my requirement was to reveal the data within the list view only when the user types characters into the search bar. Something similar to a soft search functionality.  How i achieved this is shown from the following code snippet;





<div id="firstPage" data-role="page">
  <div data-role="header">Hi!! </div>
  <div data-role="content">
  <ul id="myList" data-role="listview" data-filter="true"
    data-filter-reveal="true"
    data-filter-placeholder="Search List..." data-inset="true" style="display:none;">

   </ul>
  </div>
  
  <div data-role="footer">The End</div>
 </div> 



<script type="text/javascript">
          $( document ).on( "pageinit", "#firstPage", function() {
   
    $( "#myList" ).on( "listviewbeforefilter", function ( e, data ) {
               
                                $input = $( data.input );
        value = $input.val();
                                if(value.length>0){
                                                loadData();
                                                $('#myList').css("display","block");
                                              
                                }
                                else if(value.length==0){
                                                
                                                $('#myList').css("display","none");
                                }
                               
    });
 

function loadData(){

var dataArr = ["one","two","three"];
var content = '';
for(i=0;i<dataArr.length;i++){
content+='<li>'+dataArr[i]+'</li>';
}

 $('#myList').html(content);
 /**
 The following two method calls will refresh the List view with the updated data after page initialization
 **/
 $("#myList").listview("refresh");
 $("#myList").trigger( "updatelayout");

}
</script>

Using the  listviewbeforefilter i would check if any data is input by the user and only in that instance would i load the data to the list view. Afterwards with a little bit of css i would show or hide the list view based on the input length entered by the user.


  • Passing data from one page to another
Another requirement for me was to pass data from one page to another while transitioning to that page. Since there is no concept of POST or GET in terms of JQM, i had to search how this can be accomplished. The following code snippet shows how this can be achieved;



<div id="firstPage" data-role="page">
  <div data-role="header">Hi!! </div>
  <div data-role="content">
   <a href="#secondPage?name=Tom&phone=9411225597" data-role="button">Check Name</a>
  </div>
  
  <div data-role="footer">The End</div>
 </div> 

<div id="secondPage" data-role="page">
 <div data-role="content">
  <h1 id="txtName"> </h1>
  <h1 id="txtPhone"> </h1>
 </div>
</div>

<script type="text/javascript">
     $( "#secondPage" ).on( "pageshow", function( event, ui ) {
                                 
                                 
      var query = $(this).data("absUrl").split("?")[1];
                                 
        if(typeof query != 'undefined'){
            var params = query.split("&");
                               
            $('#txtName').html(params[0].replace("name=",""));
   $('#txtPhone').html(params[1].replace("phone=",""));
  }
    });
</script>

I am passing the data using the href  tag by appending the parameters as i would do in a typical GET request. The key here is how to capture the data once the data is passed into the second page. We first need to retrieve the URL using the absUrl attribute and thereafter its a simple split and replace of the parameters embedded within the URL. This is the only way i found by which one page can transfer data to another on page transition.


That's about it people. The points where i got stuck are the ones i wanted to highlight here so that anyone else who might face similar issues will benefit from this post if they ever came across it in time :).. I'm sure i will face many more obstacles along the way whereby i will follow this post up with those as well.

Thank you for reading and have a blessed day ahead.

And as usual comments and criticisms are always welcome along with your views on JQM as a framework for the mobile platform. So what do you think?


3 comments:

  1. Couple of pointers..There is indeed a way to pass data between pages thats less messier..
    $.mobile.changePage('url', {
    data : { json1 : value1,json2 : value2 }});

    and you can extract the parameters from the url the way you have done it..or using regex(my golden hammer), i found this nifty function on the web..props to whoever wrote it..
    function getParameterByName(event, name) {
    var match = RegExp('[?&]' + name + '=([^&]*)').exec(event.currentTarget.baseURI);
    return match && decodeURIComponent(match[1].replace(/\+/g, ' '));
    };

    ReplyDelete
  2. also you bind event listeners the same way you bind page events i.e using the on() method..i.e $('#something').on('tap',function(e){console.log(e.target);});and you usually don't trap the click event,,its slower(intended delay to detect double clicks), use tap and touchend and the like..

    ReplyDelete
  3. Thx Ruchira. Those are some useful pointers you have given.

    ReplyDelete