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:
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()
và 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ị: