Xây dựng ứng dụng tìm kiếm đơn giản với Rails và Typeahead.js

Giới thiệu chung

Trong bài viết này mình sẽ viết một ứng dụng tìm kiếm đơn giản sử dụng Rails và thư viện Typeahead.js

Chúng ta có thể thấy search là một phần rất phổ biến trong các trang web hiện nay, hầu như các trang web đều hỗ trợ tính năng này. Nhưng thực tế để tạo ra thành phần search tương đối khó và tốn khả nhiều thời gian để làm, chưa nói đến việc tạo ra giao diện trải nghiệm người dùng (UX). Thời gian gần đây mình tìm được một thư viện rất hay để xử lý vấn đề này, đó chính là 1 thư viện javascript: TypeaheadJS.

Thư viện typeahead.js: https://twitter.github.io/typeahead.js/

Thư viện typeahead bao gồm 2 thành phần chính là: Bloodhound(suggestion engine) và Typeahead (UI view). Suggestion engine là công cụ gợi ý giúp trả về các giá trị gợi ý với từng string query. UI view giúp render các suggestion và tương tác với DOM. Sự kết hợp giữa 2 component này tạo ra một công cụ hoàn hảo cho việc gợi ý khi nhập dữ liệu.

Mình không dài dòng nữa mà bắt đầu code luôn nhỉ ?

Mình có code một module nho nhỏ cho bài viết này. Bài toán là có 1 danh sách CLB yêu thích đọc sách ở các trường cấp 3 và đại học, mình cần làm 1 ô search, khi nhập từng ký tự vào thì phía dưới sẽ gợi ý ra 1 vài kết quả phù hợp.

Công việc đầu tiên là code giao diện. Mình sẽ đề cập đến bài khác về CSS trong các bài viết sau (mình rất thích code các componnet với CSS và Javascript thuần túy thay vì dùng các thư viện).

Let 's code

Bước 1: Code Rails: model, controller, api

Khởi tạo ứng dụng

rails new simple_search

Tạo model club với các trường name, slug, fanpage

rails g model club name:string slug:string fanpage:string  

Tạo controller clubs

rails g controller clubs 

Thêm vào file routes.rb

resources :clubs, path: "cau-lac-bo", only: [:index, :show]

Trong file clubs_controller mình sẽ định nghĩa action index.

class ClubsController < ApplicationController
  def index
    @clubs = Club.all
    respond_to do |format|
      format.html
      format.json{ render json: @clubs }
    end
  end
end

Vậy là mình định nghĩa đủ phần controller cho chương trình search đơn giản này. Chỉ cần tạo action để render ra view và render ra api là danh sách các club trong trường hợp request là json.

Về phần view Tạo file views/clubs/index.html.erb. Mình đã code sẵn giao diện cho phần này. Vì bài viết này mình chỉ tập trung vào Rails và Typeahead Sau đó thêm

<div class="search-area">
    <input class="typeahead" type="text" name="" value="" placeholder="Nhập tên CLB ... ">
  </div>

Trong đó class="typeahead" được dùng để cho thư viện typeahead sử dụng trong bước 2

Bước 2: Sử dụng thư viện Typeahead

Các bạn vào trang https://twitter.github.io/typeahead.js/ và download bản mới nhất file "typeahead.bundle.js". Trong thời điểm viết bài viết này là v0.11.1 Và lưu vào thư mục /assets/javascripts/ Sau đó mình require nó trong file application.js, ta thêm

//= require typeahead.bundle

Vậy là mình đó có thư viện Typeahead để dùng. Thư viện này bao gồm 2 thành phần chính là: Bloodhound, Typeahead mình đã nói ở phần giới thiệu

Bước tiếp theo là bước quan trọng cũng là bước khó nhất, mình làm theo document của của typeahead https://twitter.github.io/typeahead.js/examples/ từng bước từng bước một, nó giải thích rất rõ và dễ hiểu. Phần này mình cũng định viết lại từng bước từng bước 1 nhưng mình tin rằng phần document đã làm rất tốt việc đó. Mình không thiết kế lại chiếc bánh xe nữa. 😄

<script type="text/javascript">
$(document).ready(function(){
  var clubs = new Bloodhound({
    datumTokenizer: Bloodhound.tokenizers.obj.nonword('slug'),
    queryTokenizer: Bloodhound.tokenizers.whitespace,
    identify: function(obj) { return obj.slug; },
    prefetch: '../cau-lac-bo.json',

    remote: {
      url: '../cau-lac-bo/%QUERY.json',
      wildcard: '%QUERY'
    }
  });
  function clubsWithDefaults(q, sync) {
    console.log(clubs.index.datums);
    if (q === '') {
      sync(clubs.get(Object.keys(clubs.index.datums)));
    }

    else {
      clubs.search(q, sync);
    }
  }
  $('.search-area .typeahead').typeahead({
    minLength: 0,
    highlight: true
  },
  {
    name: 'clubs',
    display: 'name',
    source: clubsWithDefaults,
    templates: {
      empty: [
        '<div class="empty-message">',
          'Không có kết quả nào phù hợp.  Làm ơn tìm kiếm bằng tiếng việt không dấu',
        '</div>'
      ].join('\n'),
      suggestion: Handlebars.compile('<a href="{{facebook}}" class="club-link">{{name}}</a>')
    }
  });
});

function handle_filter_club(event){
  var filter_link = './cau-lac-bo'
  event.preventDefault();
  if($('#club-type :selected').val()){
    filter_link = filter_link + '?type=' + $('#club-type :selected').val();
  }
  if($('#location :selected').val()){
    filter_link = filter_link + '?location=' + $('#location :selected').val();
  }
  window.location.href = filter_link;
}
</script>


Và chúng ta chỉ cần thêm vào database dữ liệu là chúng ta đã có 1 ứng dụng search rất đơn giản mà cực kỹ tối ưu. Mình thường dùng thêm 1 gem để làm phần admin cho các ứng dụng Rails nhỏ. Đó là https://github.com/thoughtbot/administrate một gem mình cũng rất thích.

Tổng kết

Vây là mình vừa giới thiệu với các bạn ứng dụng search đơn giản với Rails và Typeahead. Mình thấy rằng sử dụng Typeahead trong các Project là rất tiện lợi và tối ưu. Nhưng trong một số Project thì nó đòi hỏi cần tương tác nhiều hơn và customize nhiều thứ. Nhất là các ứng dụng với lượng dữ liệu lớn. Mình mong rằng sẽ có cơ hội viết 1 bài về chủ đề này. Cám ơn các bạn đã xem bài viết của mình. Chúc các bạn làm việc hiệu quả. Happy coding 😄 😄


All Rights Reserved