Hiển Thị Danh Sách Record

Lấy Về Danh Sách Record

Tính năng đầu tiên chúng ta sẽ thực hiện đó là hiển thị danh sách các record được lưu trong bảng records trong CSDL. Để làm việc này bạn cần thêm một index action bên trong RecordsController xử lý công việc lấy dữ liệu từ database như sau:

# app/controllers/records_controller.rb

class RecordsController < ApplicationController
  def index
    @records = Record.all
  end
end

Tiếp theo, chúng ta cần tạo một view tương ứng với action trên để hiển thị dữ liệu trả về từ controller. Tạo một file index.html.erb trong thư mục apps/view/records. File này sẽ đóng vai trò là cầu nối giữa ứng dụng Rails và các component của Reacts. Bạn sẽ hiểu rõ hơn về điều này khi bắt đầu thêm React component ở phần tiếp theo đây.

Bây giờ trong file vừa được tạo ở trên bạn thêm đoạn code sau:

<%# app/views/records/index.html.erb %>

<%= react_component 'Records', { data: @records } %>

Đoạn code trên sử dụng helper method có tên là react_component. Method này được cung cấp bởi gem react-rails mà chúng ta đã thêm vào ở các bài học trước. Chức năng của method này là để tạo ra mã HTML với format cho trước để mount một React component với tên và dữ liệu tương ứng. Trong đoạn code trên component được mount sẽ có tên là Records và dữ liệu ban đầu được truyền vào chính là biến records trả về từ action index trong RecordsController của Rails.

Bạn có thể quay trở lại trình duyệt và truy cập vào địa chỉ localhost:3000/records để kiểm tra. Mặc dù vẫn chưa có dữ liệu nào hiển thị trên trình duyệt do chúng ta chưa tạo React component Records, tuy nhiên nếu bạn View Source (sử dụng tổ hợp phím tắt Ctrl + U trên Windows hoặc Linux hoặc Cmd + Alt + U trên Mac OS X) thì bạn sẽ thấy mã HTML hiển thị như sau:

<div data-react-class="Records" data-react-props="{...}">
</div>

Records Component

Tiếp theo, trong thư mục javascripts/components chúng ta sẽ tạo một file CoffeeScript với tên là records.js.coffee với nội dung như sau:

  # app/assets/javascripts/components/records.js.coffee

  @Records = React.createClass
    render: ->
      React.DOM.div
        className: 'records'
        React.DOM.h2
          className: 'title'
          'Records'

Code JavaScript:

(function() {
  this.Records = React.createClass({
    render: function() {
      return React.DOM.div({
        className: 'records'
      }, React.DOM.h2({
        className: 'title'
      }, 'Records'));
    }
  });

}).call(this);

Mỗi một component của React yêu cầu cần phải có một method render() để thực hiện việc hiển thị nội dung của component. Trong đoạn code trên method render() sẽ tạo ra một phần tử DOM div chứa bên trong một phần tử con h2.

Lưu ý: Nếu bạn muốn sử dụng cú pháp của JSX đoạn code trong render() lúc này sẽ như sau:

  render: ->
    `<div className="records">
      <h2 className="title"> Records </h2>
    </div>`

Bây giờ nếu bạn refresh lại trình duyệt bạn sẽ thấy nội dung như sau:

Hiển thị React component

Thiết Lập State và Property

Tuy nhiên React component của chúng ta tới thời điểm này vẫn khá tĩnh. Công việc tiếp theo chúng ta cần làm đó là hiển thị danh sách các record từ biến record của Rails. Để làm điều này chúng ta sẽ thêm hai method là getInitialState()getDefaultProps() vào trong Records component như sau:

  # app/assets/javascripts/components/records.js.coffee

  @Records = React.createClass
    getInitialState: ->
      records: @props.data
    getDefaultProps: ->
      records: []
    render: ->
      ...

Code JavaScript:

(function() {
  this.Records = React.createClass({
    getInitialState: function() {
      return {
        records: this.props.data
      };
    },
    getDefaultProps: function() {
      return {
        records: []
      };
    },
    render: function() {
      return React.DOM.div({
        className: 'records'
      }, React.DOM.h2({
        className: 'title'
      }, 'Records'));
    }
  });

}).call(this);

ReactJS dựa vào vào properties để thực hiện việc giao tiếp giữa các component với nhau và state để xác định xem có cần re-render (hiển thị lại) lại một component hay không. Ở đoạn code trên chúng ta đã xác lập state ban đầu cho component là dữ liệu lấy từ data property (hay giá trị truyền vào cho thuộc tính data-react-props của phần tử HTML của component) . Ngoài ra method getDefaultProps() ở component sẽ thiết lập các giá trị mặc định cho property records của component là một mảng rỗng.

Tạo Helper Method

Bây giờ trước khi cập nhật phương thức render() của component để hiển thị các record được lưu trên DB thì chúng ta sẽ tạo một helper function dùng để tạo định dạng hiển thị cho số tiền. Điều này sẽ giúp chúng dễ dàng đáp ứng với trường hợp có yêu cầu thay đổi về format hiển thị của số lượng tiền (ví dụ như vị trí dấu chấm, dấu phảy, ký hiệu tiền tệ...). Tạo một file có tên là utils.js.coffee trong app/assets/javascripts với nội dung như sau:

  # app/assets/javascripts/utils.js.coffee

  @amountFormat = (amount) ->
    '$ ' + Number(amount).toLocaleString()

Code JavaScript:

(function() {
  this.amountFormat = function(amount) {
    return '$ ' + Number(amount).toLocaleString();
  };

}).call(this);

Nested Component

Tiếp theo chúng ta sẽ tạo một Record component đóng vai trò là một component con của Records component và được dùng để hiển thị tương ứng từng record trong danh sách.

Trong thư mục app/assets/javascripts/components tạo một file record.js.coffee với nội dung sau:

  # app/assets/javascripts/components/record.js.coffee

  @Record = React.createClass
    render: ->
      React.DOM.tr null,
        React.DOM.td null, @props.record.date
        React.DOM.td null, @props.record.title
        React.DOM.td null, amountFormat(@props.record.amount)

Code JavaScript:

(function() {
  this.Record = React.createClass({
    render: function() {
      return React.DOM.tr(null, React.DOM.td(null, this.props.record.date), React.DOM.td(null, this.props.record.title), React.DOM.td(null, amountFormat(this.props.record.amount)));
    }
  });

}).call(this);

Record component ở trên sẽ hiển thị từng dòng dữ liệu tương ứng với record được lưu trên database.

Cập Nhật Records Component

Bây giờ cập nhật method render trong Records thành như sau:

  # app/assets/javascripts/components/records.js.coffee

  @Records = React.createClass
    ...
    render: ->
      React.DOM.div
        className: 'records'
        React.DOM.h2
          className: 'title'
          'Records'
        React.DOM.table
          className: 'table table-bordered'
          React.DOM.thead null,
            React.DOM.tr null,
              React.DOM.th null, 'Date'
              React.DOM.th null, 'Title'
              React.DOM.th null, 'Amount'
          React.DOM.tbody null,
            for record in @state.records
              React.createElement Record, key: record.id, record: record

Code JavaScript:

  ...
    render: function() {
      var record;
      return React.DOM.div({
        className: 'records'
      }, React.DOM.h2({
        className: 'title'
      }, 'Records'), React.DOM.table({
        className: 'table table-bordered'
      }, React.DOM.thead(null, React.DOM.tr(null, React.DOM.th(null, 'Date'), React.DOM.th(null, 'Title'), React.DOM.th(null, 'Amount'))), React.DOM.tbody(null, (function() {
        var i, len, ref, results;
        ref = this.state.records;
        results = [];
        for (i = 0, len = ref.length; i < len; i++) {
          record = ref[i];
          results.push(React.createElement(Record, {
            key: record.id,
            record: record
          }));
        }
        return results;
      }).call(this))));
    }
  ...

Đoạn code trên cập nhật lại cách hiển thị của Records component bằng việc sử dụng table trong đó bên trong tbody với mỗi một record sẽ tạo một element từ chính Record component (hay component con). Ở đây bạn cũng chú ý thuộc tính đặc biệt key được sử dụng để React có thể theo dõi những thay đổi diễn ra của component con để cập nhật component cha.

Bây giờ nếu bạn tải lại trang bạn sẽ thấy danh sách các record được hiển thị:

Hiển thị các record

Content must not be empty

Related Tutorial