Tạo đồ thị và bảng xếp hạng thật dễ dàng trong Rails với Chartkick (Phần 1)

chart.png

Chúng ta làm việc với dữ liệu được thay đổi hàng ngày. Từ nhìn nhận của tôi, một trong những cách tiện dụng để thể hiện dữ liệu số là sử dụng đồ thị. Nhiều người thích sử dụng đồ thị, bởi vì chúng khá màu sắc và dễ hiểu – tương phản với các bảng biểu tĩnh.

Có rất nhiều giải pháp để thể hiện các biểu đồ trong web apps, nhưng hôm nay chúng ta thảo luận về một giải pháp xây dựng cho Rails, nó sẽ thể hiện biểu đồ một cách dễ dàng. Giải pháp này được gọi là Chartkick và nó được xây dựng bởi Andrew Kane. Chartkich có thể làm việc với Google Charts, Highcharts, and Chart.js. Nó có rẩt nhiều lựa chọn để tuỳ chỉnh và cũng có một vài các thư viện hỗ trợ như groupdate, hightop, và active_median.

Trong bài này, chúng ta thảo luận cách để dùng Chartkick vào một Rails app, thể hiện các biểu đồ khác nhau, tuỳ chỉnh chúng, làm chúng load đồng bộ, và cách để nâng cao khả năng code của bạn với groupdate và hightop gems.

Chuẩn bị Application

Bắt đầu bằng cách tạo ra một Rails application:

$ rails new HappyGrapher -T

Trong mục này, tôi sẽ sử dụng Rails 5, nhưng các ví dụ được cung cấp có thể làm việc tốt với Rails 4 và 3.

Giả sử ứng dụng của chúng ta theo dõi "sporters" (vận động viên) và các “competitions” (cuộc thi) mà họ tham gia. Dưới đây là thông tin về tất cả các bảng sẽ yêu cầu:

sporters

  • name (string)
  • age (integer) – trong ví dụ này giả thiết tuổi trong phạm vi 18-50
  • country_id (integer) – một foreign key để dự tính sự liên quan giữa sporters and countries

countries

  • title (string) – chúng ta sẽ thêm ngẫu nhiên 50 nước vào trong database

competitions

  • title (string)

competition_results

Đây là bảng trung gian thể hiện quan hệ nhiều - nhiều giữa 2 bảng sporters and competitions.

  • sporter_id (integer) – một foreign key
  • competition_id (integer) – một foreign key
  • place (integer)– nơi các vận động viên ở. chúng ta giả sử rằng cột này có giá trị từ 1 đến 6

Thiết lập và chạy migrations cần thiết:

$ rails g model Country name:string
$ rails g model Sporter name:string age:integer country:references
$ rails g model Competition title:string
$ rails g model CompetitionResult sporter:references competition:references place:integer
$ rake db:migrate

Tạo ra một controller, một view và root route:

statistics_controller.rb

class StatisticsController < ApplicationController
  def index
  end
end

views/statistics/index.html.erb

<h1>Statistics</h1>

config/routes.rb

[...]
root 'statistics#index'
[...]

Khá tốt, nhưng để biểu đồ rõ ràng chúng ta sẽ cần thêm một vài mẫu dữ liệu, vậy hãy bắt đầu ngay bây giờ.

Tải lên dữ liệu mẫu

Để đẩy nhanh quá trình tải dữ liệu mẫu, tôi sẽ sử dụng 2 gem: faker cho phép bạn tạo ra các đoạn text thay đổi từ tên, email,.. và countries làm đơn giản hoá việc tìm nạp các thông tin về các quốc gia có sẵn. Thêm các gem này vào Gemfile:

Gemfile

[...]
gem 'countries'
gem 'faker'
[...]

Cài đặt:

$ bundle install

Mở file seeds.rb và paste code thêm 50 country:

db/seeds.rb

ISO3166::Country.all.shuffle.first(50).each do |country|
  Country.create({name: country.name})
end

Đoạn này sẽ thêm toàn bộ các country ngẫu nhiên vì vậy đừng bị bất ngờ nếu có các tên như Antarctica or Sao Tome trong bảng của bạn.

Bây giờ chúng ta cũng cần một nhóm các vận động viên:

db/seeds.rb

100.times {
    Sporter.create({
      name: Faker::Name.name,
      age: rand(18..50),
      country_id: rand(1..50)
    })
}

Ở đây chúng ta sử dụng Faker để tạo ra các tên đơn giản.

Tiếp theo là competitions. Tôi không thể tìm thấy bất kì danh sách có sẵn để sử dụng, vì vậy chúng ta sẽ tự gõ tên các môn thể thao.

db/seeds.rb

%w(tennis parachuting badminton archery chess boxing racing golf running skiing walking cycling surfing swimming skeleton).each {|c| Competition.create({title: c}) }

Và cuối cùng l các kết quả của cuộc thi:

db/seeds.rb

Competition.all.each do |competition|
  sporters = Sporter.all.shuffle.first(6)
  (1..6).each do |place|
    CompetitionResult.create({
                       sporter_id: sporters.pop.id,
                       competition_id: competition.id,
                       place: place,
                       created_at: rand(5.years.ago..Time.now)
                    })
  end
end

Lưu ý rằng chúng ta ghi đè cột created_at  giả định rằng cuộc thi tại một nơi từ vài năm hoặc vài tháng trước

Thể hiện một biểu đồ đơn giản

Mọi thứ đã sẵn sàng để bắt đầu thực hiện chức năng chi – các biểu đồ. Thêm gem chartkick vào Gemfile rồi bundle install:

Gemfile

[...]
gem 'chartkick'
[...]

Chartkick hỗ trợ cả Google Charts, Highcharts và Chart.js (từ version 2.0 nó được mặc định gắn vào). Ở ví dụ này chúng ta sẽ dùng Highcharts, nhưng cài đặt để chạy Google Charts cũng khá đơn giản.

Đầu tiên, tải bản mới nhất của Highcharts và đặt nó bên trong mục  javascripts. Tiếp theo thêm:

javascripts/application.js

[...]
//= require highcharts
//= require chartkick
[...]

Giờ ta có thể thể hiện biểu đồ. Để bắt đầu, hãy thể hiện một thanh biểu đồ minh hoạ tuổi của các vận động viên:

Load tất cả sporters:

statistics_controller.rb

[...]
def index
  @sporters = Sporter.all
end
[...]

View:

views/statistics/index.html.erb

<%= bar_chart @sporters.group(:age).count %>

Chúng ta nhóm các vận động viên theo tuổi và tính số lượng các mục trong mỗi nhóm.

Bạn có lẽ đang tự hỏi làm thế nào để cài đặt các thiết lập cho các biểu đồ như đặt tên, điều chỉnh chiều rộng, cao, và các thứ khác. Điều này cũng khá đơn giản - các thiết lập được đặt trong options của bar_chart.

Tạo thêm một StatisticsHelper vag thêm code:

statistics_helper.rb


module StatisticsHelper
  def sporters_by_age
    bar_chart @sporters.group(:age).count, height: '500px', library: {
      title: {text: 'Sporters by age', x: -20},
      yAxis: {
         allowDecimals: false,
         title: {
             text: 'Ages count'
         }
      },
      xAxis: {
         title: {
             text: 'Age'
         }
      }
    }
  end
end

:library  bao gồm các thiết lập library-specific. Ở đây chúng ta đang ngăn chặn các số thập phân từ việc xuất hiện trên trục Y (chắc chắn, chúng ta không thể có 2.5 người ở tuổi 20) và đưa nó một cái tên. Trục X cũng đã được định nghĩa tên. Phía trên nữa, tạo ra tiêu đề cho biểu đồ (nó sẽ xuất hiện tại phía dưới mặc định). Highcharts sẽ load các option khả dụng.

Sử dụng Hightop

Nếu bạn chỉ muốn thể hiện độ tuổi “phổ b” nhất của các vận động viên, sử dụng gem hightop – nhỏ thôi nhưng hữu ích được thiết kế để giải quyết các việc này.

Thêm vào Gemfile và chạy bundle install:

Gemfile

[...]
gem 'hightop'
[...]

Bây giờ bạn có thể lấy ví dụ: 10 tuổi thông dụng nhất.

views/statistics/index.html.erb

<%= bar_chart @sporters.top(:age, 10) %>

Biểu đồ không đồng bộ

Nếu database của bạn có nhiều dữ liệu muốn thể hiện trên một biểu đồ, page này sẽ tải khá chậm. Vì vậy, tốt hơn là để thể hiện biểu đồ của bạn một cách không đồng bộ. Chartkick cũng hỗ trợ chức năng này. Toàn bộ việc bạn phải làm là tạo ra một route riêng biệt và một action controller, sau đó sử dụng route này bên trong một view. Chú ý rằng nó requiers jQuery hoặc Zepto.js.

Tạo một route:

config/routes.rb

[...]
resources :charts, only: [] do
  collection do
    get 'sporters_by_age'
  end
end
[...]

và một controller:

charts_controller.rb

class ChartsController < AppliontrollercationController
  def sporters_by_age
    result = Sporter.group(:age).count
    render json: [{name: 'Count', data: result}]
  end
end

Sửa lại helper của bạn để lấy ra route vừa được tạo mới:

statistics_helper.rb

[...]
def sporters_by_age
  bar_chart sporters_by_age_charts_path, height: '500px', library: {
    [...]
  }
end
[...]

Bây giờ biểu đồ của bạn sẽ được load không đồng bộ cho phép người dùng duyệt các nội dung khác nhau trên page. Trường hợp thay đổi @sporters là không cần thêm nữa, nên bạn có thể xoá nó từ action index của StatisticsController.

Kết luận

Trong bài này chúng ta đã thảo luận về Chartkick- một gem tốt để biểu diễn các biểu đồ đơn giản. Ở phần tiếp theo, chúng ta sẽ tìm hiểu các loại biểu đồ khác nhau mà Chartkick có nhé! Thank you!

Tài liệu dịch: https://www.sitepoint.com/make-easy-graphs-and-charts-on-rails-with-chartkick/


All Rights Reserved