Selasa, 12 Februari 2013

Modifikasi Typeahead Bootstrap dan Ajax

Jika kamu belum tahu apa itu Bootstrap bisa dilihat disini. Jika kamu sudah tahu Bootstrap tapi belum tahu tentang Typeahead bisa dilihat disini. Secara singkat Typeahead adalah input yang memunculkan pilihan saat kita mengetikkan sesuatu yang mirip dengan input yang kita masukkan. Contoh: waktu kamu mengetikkan nama di Facebook pasti muncul pilihan pada input tersebut daftar teman-temanmu yang memiliki nama yang mirip dengan input yang kamu masukkan.
Bootstrap memiliki Typeahead yang sebenarnya sudah cukup keren, tapi kita akan memodifikasi Typeahead ini agar bisa menampilkan foto dan menerima sumber data dari Ajax sehingga mirip dengan input di Facebook. Jika kamu penasaran bentuk asli dari Typeahead kita akan buat kerangka dasarnya dulu.

<!doctype html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <title>Typeahead</title>
  <link rel="stylesheet" href="bootstrap/css/bootstrap.min.css" media="all">
  <link rel="stylesheet" href="bootstrap/css/bootstrap-responsive.min.css" media="all">
  <script src="jquery/jquery-1.9.0.min.js"></script>
  <script src="bootstrap/bootstrap.min.js"></script>
</head>
<body>
  <div class="container">
    <div class="row">
      <div class="span8 offset2">
        <h3>Bootstrap Typeahead</h3>
        <hr>
        <form class="form-horizontal">
          <div class="control-group">
            <label for="artis" class="control-label">Pilih Artis</label>
            <div class="controls">
              <input type="text" name="artis" id="artis" class="input-xlarge" data-provide="typeahead" data-source='["Maria Selena", "Gita Gutawa", "Sandra Dewi", "Citra Kirana"]'>
            </div>
          </div>
          <div class="form-actions">
            <button class="btn btn-primary">SIMPAN</button> &nbsp;
            <button class="btn">BATAL</button>
          </div>
        </form>
      </div>
    </div>
  </div>
</body>
</html>

Untuk path css dan javascriptnya ubah sesuai dengan letak file Bootstrap dan jQuery yang kamu punyai. Untuk mengaplikasi typeahead pada sebuah input sangatlah mudah, bahkan cuma menambahkan atribut data-provide="typeahead" dan data-source yang beruba array string maka typeahead sudah bisa berjalan seperti pada gambar ini:



Lumayan keren kan?, tapi sekarang kita akan memodifikasinya sehingga bisa menampilkan foto bersama dengan namanya. Bootstrap Typeahead memiliki API yang bisa kita ubah agar kita dapat mengubah tampilan defaultnya yaitu menggunakan highlighter dan updater. Berikut ini kode Javascriptnya yang bisa kamu masukkan dimanapun (sebaiknya sebelum tutup tag BODY).
/* 
 * load data dengan ajax dimana data yang diperoleh memiliki format:
 * [{ nama: 'nama artist', foto: 'url foto' }]
 *
 * jika tidak menggunakan ajax langsung masukkan di source
 */
$.ajax({
  url: 'respond.php?artis',
  dataType: 'json',
  success: function(d) {
    var data = d;
    
    /*
     * ini deklarasi typeaheadnya yang sudah dimodifikasi
     */
    $('#artis').typeahead({
      source: function(query, process) {
        objects = [];
        // iterasi data
        $.each(data, function(i, object) {
          objects.push(object.foto + '#' + object.nama);
        });
        process(objects);
      },
      items: 4, // jumlah list maksimal yang muncul
      highlighter: function(item) {
        var s = item.split('#'),
          regex = new RegExp('(' + this.query + ')', 'i'),
          nama = s[1].replace(regex, "<strong>$1</strong>");
        html = '<div class="typeahead">';
        html += '<div class="typeahead-media"><img src="img/' + s[0] + '" class="pull-left">';
        html += '<div class="media-body">';
        html += '<p class="media-heading">' + nama + '</p>';
        html += '</div>';
        html += '</div>';
        return html;
      },
      updater: function(item) {
        var s = item.split('#');
        return s[1];
      },
      matcher: function(item) {
        var s = item.split('#');
        return s[1].toLowerCase().indexOf(this.query.toLowerCase()) != -1
      }
    });
  }
});

Dan hasil yang kita peroleh seperti gambar ini:



Sebaiknya ukuran file gambarnya sama dan berbentuk bujursangkar. Karena tampilannya masih acak-acakan kita butuh sedikit CSS untuk merapikannya. Tambahkan kode CSS ini di dalam tag HEAD:
ul.typeahead.dropdown-menu {
  min-width: 370px; }
ul.typeahead li a {
  padding: 4px; }
.typeahead-media img {
    width: 28px;
  margin-right: 5px; }

Dan inilah hasil akhirnya:



Gak jelek2 amat kan :D. Semoga bermanfaat.

Jika kamu penasaran dan ingin tahu seperti apa file php yang memproses request sehingga menghasilkan bentuk yang diinginkan maka ini contoh kerangka kodenya:

<?php
if (isset($_GET['artis'])) {
  $query = mysql_query("SELECT `nama`, `foto` FROM `artis`");
  $hasil = array();
  while ($d = mysql_fetch_array($query, MYSQL_ASSOC)) {
    $hasil[] = array(
      'nama' => $d['nama'],
      'foto' => $d['foto']
    );
  }
  echo json_encode($hasil, JSON_HEX_TAG | JSON_HEX_APOS | JSON_HEX_QUOT | JSON_HEX_AMP);
}

Senin, 11 Februari 2013

Pagination dengan CodeIgniter, jQuery dan Bootstrap

Karena ada beberapa teman yang kesulitan mengaplikasikan pagination di CodeIgniter dengan jQuery, maka aku coba membuat sesuatu yang menurutku simple dan mudah dipahami (*semoga :). Kali ini aku akan menggunakan Twitter Bootstrap untuk CSS-nya dan jQuery untuk meload data. Kerangka dasar dokumennya adalah seperti ini:
<html lang="en">
<head>
  <meta charset="UTF-8">
  <title>Pagination</title>
  <link rel="stylesheet" href="/third_party/bootstrap/css/bootstrap.min.css" />
  <link rel="stylesheet" href="/third_party/bootstrap/css/bootstrap-responsive.min.css" />
  <script src="/third_party/jquery/jquery-1.8.2.js"></script>
  <script>
    // masukkan kode javascript berikutnya disini
  </script>
</head>
<body>
  <div class="container">
    <div class="row">
      <div class="span12">
        <h1>Pagination Dokumen</h1>
        <hr>
        <form class="form-inline">
          <input type="text" name="query" id="query" class="input-large"> &nbsp; 
          <button class="btn" onclick="return Document.search()"><i class="icon-search"></i> Cari</button>
        </form>
        <hr>
        <table class="table table-bordered table-condensed table-striped">
          <thead>
            <tr>
              <th>Judul</th>
              <th>Pengarang</th>
              <th>Tahun</th>
            </tr>
          </thead>
          <tbody id="document-data">
            <tr>
              <td></td>
              <td></td>
              <td></td>
            </tr>
          </tbody>
        </table>
        <hr>
        <div class="pagination pagination-centered pagination-medium" id="pagination">
          <ul>
            <li><a href="">&laquo;</a></li>
            <li><a href="">1</a></li>
            <li><a href="">&raquo;</a></li>
          </ul>
        </div>
      </div>
    </div>
  </div>
</body>
</html>



Pada dokumen diatas kita memiliki sebuah tabel yang akan menampilkan data dokumen dengan kolom Judul, Pengarang, dan Tahun. Diatas tabel terdapat form untuk memasukkan kata kunci pencarian. Dibawah tabel terdapat template pagination yang nanti akan diubah setelah data diload oleh Ajax. Perhatikan ada beberapa id dari elemen yang berperan yaitu:
  • query. id untuk input kata kunci pencarian
  • document-data. id di tbody tabel data
  • pagination. id untuk bagian pagination dibawah tabel
Jika diperhatikan, di tombol Cari ada atribut onclick="return Document.search()". Nah sekarang kita tambahkan fungsi Javascriptnya untuk menangani tampilan data dan pagination di dokumen ini. Kode Javascript ini bisa kamu letakkan di dalam tag HEAD setelah jquery (di bagian yang aku tandai sebelumnya).
var Document = {
  param: {
    dataperpage: 3, // jumlah data per halaman
    query: '',
    curpage: 0,
    numpage: 0
  },
  url: '/test/document',
  search: function() {
    this.param.query = $('#query').val();
    this.param.curpage = 0;
    this.loadData();
    return false;
  },
  setPage: function(n) {
    this.param.curpage = n;
    this.loadData();
    return false;
  },
  prevPage: function() {
    if (this.param.curpage > 0) {
      this.param.curpage--;
      this.loadData();
    }
    return false;
  },
  nextPage: function() {
    if (this.param.curpage < this.param.numpage) {
      this.param.curpage++;
      this.loadData();
    }
    return false;
  },
  loadData: function() {
    $.ajax({
      url: Document.url,
      type: 'POST',
      dataType: 'json',
      data: jQuery.param(Document.param),
      success: function(d) {
        $('#pagination').html(d.pagination);
        Document.param.numpage = d.numpage;
        var t = '', dt = {};
        for (var i = 0; i < d.data.length; i++) {
          dt = d.data[i];
          t += '<tr><td>' + dt.judul +'</td>' + 
             '<td>' + dt.pengarang + '</td>' + 
             '<td>' + dt.tahun + '</td></tr>';
        }
        $('#document-data').html(t); // id dari tbody tabel data
      }
    });
  }
}

$(document).ready(function() {
  Document.search();
});
Ada bagian penting di property param dalam object Document diatas yaitu dataperpage yang berisi jumlah data perhalaman yang akan ditampilkan di tabel. Kamu bisa mengubah-ubah sesuai keinginanmu. Lalu bagian url juga harus disesuaikan dengan route yang akan kita buat di CodeIgniternya. Agar javascript meload data untuk pertamakalinya maka kita panggil Document.search() ketika dokumen siap melakui event $(document).ready(). Jangan simpan dulu file dokumen tadi karena setelah kita buat Controller baru kita bisa tahu mau disimpan dimana dokumen ini.
Sekarang kita buka file application/config/routes.php di CodeIgniter dan masukkan baris berikut:
$route['test'] = "test";
$route['test/document'] = "test/pagination";
Kita mesti punya Test controller sekarang. buat Controller baru dan beri nama Test yang isinya seperti ini:
<?php if ( ! defined('BASEPATH')) exit('No direct script access allowed');
class Test extends CI_Controller {
  public function index() {
    $this->load->view('test/pagination.php');
  }
  
  public function pagination() {
    $this->load->database();
    $this->load->model('test_model');
    $this->test_model->search_document();
  }
}
Perhatikan di method index kita meload View test/pagination.php. Nah, file view ini adalah file dokumen yang berisi HTML dan Javascript yang telah kita buat sebelumnya. Berarti file dokumen tadi simpan di dalam folder application/views/test dan diberi nama pagination.php. Kalau kamu punya struktur view dan controller sendiri maka bagian controller bisa diubah sesuai dengan struktur yang kamu punya yang penting dokumennya ketemu.
Selanjutnya di controller tadi kita lihat bahwa controller meload model test_model, berarti sekarang kita buat modelnya:
<?php
class Test_model extends CI_Model {
  public function search_document() {
    $input = array('dataperpage', 'query', 'curpage');
    foreach ($input as $val)
      $$val = $this->input->post($val);
    
    $query = $this->db->escape_like_str($query);
    $where = "`JUDUL_DOKUMEN` LIKE '%{$query}%' OR `PENGARANG_DOKUMEN` LIKE '%{$query}%'";
    
    $query = $this->db->query("SELECT COUNT(`ID_DOKUMEN`) AS `HASIL` FROM `tb_dokumen` WHERE $where");
    $total = $query->row()->HASIL;
    $npage = ceil($total / $dataperpage);
    
    $start = $curpage * $dataperpage;
    $end = $start + $dataperpage;
    $query = $this->db->query("SELECT `JUDUL_DOKUMEN`, `PENGARANG_DOKUMEN`, `TAHUN_PENERBITAN_DOKUMEN` FROM `tb_dokumen` WHERE $where LIMIT $start, $dataperpage");
    $hasil = array(
      'data' => array(),
      'pagination' => '',
      'numpage' => $npage - 1,
    );
    if ($query->num_rows() > 0) {
      foreach ($query->result() as $row) {
        $hasil['data'][] = array(
          'judul' => $row->JUDUL_DOKUMEN,
          'pengarang' => $row->PENGARANG_DOKUMEN,
          'tahun' => $row->TAHUN_PENERBITAN_DOKUMEN
        );
      }
    }
    
    $hasil['pagination'] .= '<ul>
      <li class="'. ($curpage == 0 ? 'disabled' : '') .'" onclick="return Document.prevPage()"><a href>&laquo;</li>';
    for ($i = 1; $i <= $npage; $i++) {
      $hasil['pagination'] .= '<li class="' . ($curpage == ($i - 1) ? 'active' : '') . '" onclick="return Document.setPage(' . ($i - 1) .')"><a href>' . $i . '</a></li>';
    }
    $hasil['pagination'] .= '<li class="' . ($curpage == $npage - 1 ? 'disabled' : '') . '" onclick="return Document.nextPage()"><a href>&raquo;</a></li>
      </ul>';
    
    echo json_encode($hasil, JSON_HEX_TAG | JSON_HEX_APOS | JSON_HEX_QUOT | JSON_HEX_AMP);
  }
}
Kita akan mereturn hasil berupa JSON yang akan diterima oleh Javascript. Jika kamu memiliki database yang berbeda maka ubah saja bagian model diatas dan edit sesuai keinginanmu. Dan berikut ini hasil akhirnya:



Semoga bermanfaat dan mudah dipahami :D