Syntax Highlighter

Wednesday, May 22, 2013

Superheroic dynamic pagination with AngularJs

Data binding, custom directives, reusable components, dependency injection, you name it! AngularJs has all you need in order to boost front-end productivity. I've been using it for quite a while now and recently I came by an interesting problem: dynamic pagination. I'm not sure if you understand what I mean by "dynamic". I wanted to implement an admin page containing a paginated table holding all the site's signed-up users There would be a searchbar as well, and the number of pages and filtering should occur dynamically as the user types in the query.
Using data binders and clever event handling this can be easily accomplished by angular and the result is incredibly satisfying. Let me show you the code. First add these stylesheets:
<link href="http://netdna.bootstrapcdn.com/twitter-bootstrap/2.1.1/css/bootstrap.no-icons.min.css" rel="stylesheet">
<link href="http://netdna.bootstrapcdn.com/font-awesome/2.0/css/font-awesome.css" rel="stylesheet">
Now the table view. It binds any change to the text input to the function $scope.search(), adds all the pagedItems[currentPage] array to the table's body and sets up the pagination logic in the table's foot:
        <div ng-controller="ctrlRead">
            <div class="input-append">
                <input type="text" ng-model="query" ng-change="search()" class="input-large search-query" placeholder="Search">
             <span class="add-on"><i class="icon-search"></i></span>
            </div>
            <table class="table table-striped table-condensed table-hover">
                <thead>
                    <tr>
                        <th class="id">Id&nbsp;</th>
                        <th class="name">Name&nbsp;</th>
                        <th class="description">Description&nbsp;</th>
                        <th class="field3">Field 3&nbsp;</th>
                        <th class="field4">Field 4&nbsp;</th>
                        <th class="field5">Field 5&nbsp;</th>
                    </tr>
                </thead>
                <tfoot>
                    <td colspan="6">
                        <div class="pagination pull-right">
                            <ul>
                                <li ng-class="{disabled: currentPage == 0}">
                                    <a href ng-click="prevPage()">« Prev</a>
                                </li>
                                <li ng-repeat="n in range(pagedItems.length)"
                                    ng-class="{active: n == currentPage}"
                                ng-click="setPage()">
                                    <a href ng-bind="n + 1">1</a>
                                </li>
                                <li ng-class="{disabled: currentPage == pagedItems.length - 1}">
                                    <a href ng-click="nextPage()">Next »</a>
                                </li>
                            </ul>
                        </div>
                    </td>
                </tfoot>
                <tbody>
                    <tr ng-repeat="item in pagedItems[currentPage] | orderBy:sortingOrder:reverse">
                        <td>{{item.id}}</td>
                        <td>{{item.name}}</td>
                        <td>{{item.description}}</td>
                        <td>{{item.field3}}</td>
                        <td>{{item.field4}}</td>
                        <td>{{item.field5}}</td>
                    </tr>
                </tbody>
            </table>
        </div>
We need only build the filteredItems array after each call to $scope.search() accordingly. This can be done by passing the classic needle/haystack function as $filter's matching function and then split the result based on the number of elements per page. But talk is cheap, I know:
//classical way of finding a substring (needle) in a given string (haystack)
    var searchMatch = function (haystack, needle) {
        if (!needle) {
            return true;
        }
        return haystack.toLowerCase().indexOf(needle.toLowerCase()) !== -1;
    };

    // filter the items following the search string
    $scope.search = function () {
        $scope.filteredItems = $filter('filter')($scope.items, function (item) {
            for(var attr in item) {
                if(item.hasOwnProperty(attr))
                    if (searchMatch(item[attr], $scope.query))
                        return true;
            }
            return false;
        });
        // take care of the sorting order
        if ($scope.sortingOrder !== '') {
            $scope.filteredItems = $filter('orderBy')($scope.filteredItems, $scope.sortingOrder, $scope.reverse);
        }
        $scope.currentPage = 0;
        // now group by pages
        $scope.groupToPages();
    };
    
    // divide elements by page
    $scope.groupToPages = function () {
        $scope.pagedItems = [];
        
        for (var i = 0; i < $scope.filteredItems.length; i++) {
            if (i % $scope.itemsPerPage === 0) {
                $scope.pagedItems[Math.floor(i / $scope.itemsPerPage)] = [ $scope.filteredItems[i] ];
            } else {
                $scope.pagedItems[Math.floor(i / $scope.itemsPerPage)].push($scope.filteredItems[i]);
            }
        }
    };
Finally, just add the paging logic for the table's foot and call $scope.search() so we show everything after the page loads.
    $scope.range = function (end) {
        var ret = [];
        for(var i = 0; i < end; i++) {
            ret.push(i);
        }
        return ret;
    };
    
    $scope.prevPage = function () {
        if ($scope.currentPage > 0) {
            $scope.currentPage--;
        }
    };
    
    $scope.nextPage = function () {
        if ($scope.currentPage < $scope.pagedItems.length - 1) {
            $scope.currentPage++;
        }
    };
    
    $scope.setPage = function () {
        $scope.currentPage = this.n;
    };

    // create filtered items for the first time
    $scope.search();
Just add this code to our view's controller and we're done! Check out this fiddle with our working code and some mock data! Now, of course this is only useful when all the necessary data can be fetched from the server quickly, so that the search is consistent. If you cannot provide Angular with all the necessary data, perhaps you should consider infinite scrolling instead. If that's the case, check this out. Have fun!
ftw.

Sunday, October 28, 2012

Connecting your Android App to a remote database

       For Android devs to be: in most mobile applications, an external server and database is useful, so I'll show a simple way of connecting an Android app to a backend script written in PHP.

       Well, this can be done using the Apache HttpClient library, which can be found at  http://hc.apache.org/ . Once you add this library to your project's classpath, you'll be able to handle HTTP Requests from Java in a very neat and simple way. Let's use it to write a simple project, in which the user will be able to upload files and perform queries on the server's database.

       In order to make it simple, let's create two basic classes: User and Server, and write them so we can use them later on in the simplest and most intuitive way possible:

           
public class User {
  
    private int id;
 
    private String username;
 
    private String password;
 
    private File image;
 
    private String fileURL;

 public User(String username, String password, File image){
  
    this.username  = username;
    this.password = password;
    this.image = image;
  
 }
 
 
 public String getUserName(){
    return username;
 }
 
 public String getPassword(){
    return password;
 }

 public File getImage() {
    return image;
 }

 public String getFileURL() {
    return fileURL;
 }


 public int getId() {
    return id;
 }

}

public class Server {
 
  private String serverURL;
  private HashMap<ServerScripts,String> serverScripts;
  
  public static enum ServerScripts{
   SQL_QUERY_MAKER, IMAGE_UPLOAD;
  }
  
  public Server(String SUrl){
     setServerURL(SUrl);
     buildScriptReference();
  }
  
  private void buildScriptReference(){
   
   serverScripts = new HashMap<ServerScrpts,String>();
   serverScripts.put(ServerScripts.SQL_QUERY_MAKER,getServerURL() + "/PHP/SQLQueryMakerScript.php");
   serverScripts.put(ServerScripts.IMAGE_UPLOAD, getServerURL() + "/PHP/imageUploadScript.php");
   
  }

  public HashMap<ServerScrpts,String> getServerScripts() {
   return serverScripts;
  }

  public String getServerURL() {
   return serverURL;
  }

  public void setServerURL(String serverURL) {
   this.serverURL = serverURL;
  }
  
  
}
Next we create a class that will be responsible for handling the HTTP Requests so we can add/remove Users to/from the server. So let's focus on the client side, creating a ConnectionHandler class. We'll provide it with a DefaultHttpClient object (so we can use the Apache library), the server with which we wish to communicate and three methods:
  • void uploadFile(File file, String fileId) - Uploads the image to the server, asking it to react to the Request through the script imageUploadScript.php
  • public BufferedInputStream downloadFile(String fileUrl) - Gets an user's image based on the userID given.
  • String performSQLQuery(String sqlCommand) - Queries the server's database, asking it to run the specified SQL query through the script SQLqueryMakerScript.php. The String returned will be JSON encoded, so we can easily manipulate the results.
Now the code:

public class ConnectionHandler {

 private HttpClient httpClient;
 private Server server;
 
 
 public ConnectionHandler(String serverURL){
  
  httpClient = new DefaultHttpClient();
  server = new Server(serverURL);
  
 }
 
 
 public BufferedInputStream downloadFile(String fileUrl) throws IOException{
  

        //since we don't need the server to respond using any
        //particular script, we can use the standard java.net methods
  InputStream is = null;
  URL myFileUrl = null;
  try {
   myFileUrl = new URL(fileUrl);
  } catch (MalformedURLException e1) {
   // TODO Auto-generated catch block
   e1.printStackTrace();
  }
  HttpURLConnection conn;
  conn = (HttpURLConnection) myFileUrl.openConnection();
  conn.setReadTimeout(10000);
  conn.setRequestMethod("POST");
  conn.setDoInput(true); 
  conn.connect();
   
  is = conn.getInputStream();
  BufferedInputStream bis = new BufferedInputStream(is);
   
  return bis;
   
 }
 
 public void uploadFile(File file, String fileId) throws Exception{
  
  HttpPost post = new HttpPost(server.getServerScripts().get(ServerScripts.IMAGE_UPLOAD));

  MultipartEntity  body = new MultipartEntity();
  
  body.addPart("imagem", new FileBody(file));
  body.addPart("fileId", new StringBody(fileId));
  
  post.setEntity(body);
  
  String response = EntityUtils.toString(httpClient.execute(post).getEntity(), "UTF-8");
  System.out.println(response);
 }
 
        

 public String performSQLQuery(String sqlCommand){
 
            
  String result = null;
  InputStream is = null;
  
  try{
   HttpPost httppost = new HttpPost(server.getServerScripts().get(ServerScripts.SQL_QUERY_MAKER));
   
   ArrayList<NameValuePair> nameValuePairs = new ArrayList<NameValuePair>();
   
   nameValuePairs.add(new BasicNameValuePair("SQLCommand", sqlCommand));
   
   UrlEncodedFormEntity body = new UrlEncodedFormEntity(nameValuePairs);
   
   httppost.setEntity(body);
   
   HttpResponse response = httpClient.execute(httppost);
   HttpEntity entity = response.getEntity();
   is = entity.getContent();
   
  } catch(Exception e){
    e.printStackTrace();
  }
  try{
   BufferedReader reader = new BufferedReader(new InputStreamReader(is,"UTF-8"));
   StringBuilder str = new StringBuilder();
   String line= new String();
   
   while((line = reader.readLine())!=null){
    str.append(line + "\n");
   }
   
   is.close();
   
   result = str.toString();
  }catch(Exception e){
   e.printStackTrace();
  }
  
  return result;
 }
 
 public String getServerUrl(){
  return server.getServerURL();
 }
 
 

 
}

All right, now we're all set. Let's create a user in a local WAMP server, which is an easy to use Apache instance set up to work with PHP and MySQL. It can be downloaded at http://www.wampserver.com/en/ . There are also equivalents for Mac OS X and Linux distros, namely MAMP and LAMP. The OS doesn't really matter, as long as we have an Apache server running PHP and connected to a MySQL db. So let's keep going:
 

public class ConnectionTester{

      public static void main(String[] args){
           File file = new File("filePath");

           ConnectionHandler conn = new ConnectionHandler("http://localhost");
           User user = new User("Eric","abc123", file);

           createNewUser(user.getUserName(), 
                  user.getPassword(), user.getImage(), conn);


      }

      private static void createNewUser(String userName, String password, File image, ConnectionHandler connHandler){
   
   JSONArray jsonArray = null;
   
   int idLastRow=0;
   
   try {
              //this query will just make sure we add the user to the end of
              //the table.
    String jsonResponse= connHandler.performSQLQuery("select * from users order by id desc limit 0,1");
    jsonArray = new JSONArray(jsonResponse);
    idLastRow = jsonArray.getJSONObject(0).getInt("id");
   } catch (JSONException e) {
    // TODO Auto-generated catch block
    e.printStackTrace();
   }
   
   int userId = idLastRow + 1;
   
   
   String sqlCommand = "INSERT INTO USERS VALUES (" + userId + ", '" + userName + 
   "', '" + password + "' , '" + "http://localhost/images/" + userId + ".jpg' )";
   
   connHandler.performSQLQuery(sqlCommand);
   
   try {
    connHandler.uploadFile(image, userId + "");
   } catch (Exception e) {
    // TODO Auto-generated catch block
    e.printStackTrace();
   }
   
 }



}
Test it and you'll see it adds the file to your server's "imagens" directory and creates a new row on your SQL user's table, as long as you put these PHP scripts on your WAMP's www/PHP folder:

imageUploadScript.php:
<?php 
 $ext = pathinfo($_FILES['imagem']['name'], PATHINFO_EXTENSION);
 $path = $_SERVER['DOCUMENT_ROOT'] . "/images/" . $_POST["fileId"] . "." . $ext;
 if(move_uploaded_file($_FILES["imagem"]['tmp_name'], $path)) {
  echo "The file ".  basename( $_FILES["imagem"]['name']). 
  " has been uploaded";
 } else{
  echo "There was an error uploading the file, please try again!";
 }
?>



SQLqueryMakerScript.php:
<?php
 $con = mysql_connect("localhost","username","password");
 
 if(!$con){
  die('Could not connect: ' . mysql_error());
 }
 
 mysql_select_db("squeak",$con);
 
 
 $result = mysql_query($_POST["SQLCommand"]);
 $output=null;
 
 while($row = mysql_fetch_assoc($result)){
   $output[]=$row;
 }
 
 $jsonresponse = json_encode($output);
 echo $jsonresponse;
 
 mysql_close($con);
?>
Finally, to use this on your Android app, just implement these classes on your project and set the server url to your Apache server, if you allow external TCP connections. This method of sending HTTP requests from Java can be used to connect to any website, you can read more about it at http://hc.apache.org/httpclient-legacy/tutorial.html .  Hopefully this post got you started with mobile client-server applications. Good luck!

Thursday, October 25, 2012

Because sharing is worth it!

       It lives! This blog was created so I had a place to share my findings on computer programming and other stuff. I hope you find my content relevant and I don't bore you to death. So if you're into programming, computer science, web development and technology in general, welcome aboard! Let's get started.