ReactJs với Ruby on Rails 5 (Phần 3)
Bài đăng này đã không được cập nhật trong 3 năm
Phần này mình sẽ làm 1 trang hoàn chỉnh với CRUD nhé
Mình sẽ hướng dẫn phânf trước nên phần này mình cho các bạn xem source code(cũng dễ hiểu)
model user
#db/migrate/20170808085251_create_users.rb
class CreateUsers < ActiveRecord::Migration[5.0]
def change
create_table :users do |t|
t.string :name
t.integer :age
t.string :email
t.text :address
t.timestamps
end
end
end
routes
# config/routes.rb
Rails.application.routes.draw do
resources :users
namespace :api do
namespace :v1 do
resources :users
end
end
end
controllers
# app/controllers/api/v1/users_controller.rb
class Api::V1::UsersController < ApplicationController
before_action :load_user, only: [:update, :destroy]
def index
@users = if params[:data].blank?
User.all
else
User.search_by User.new(params[:data].as_json)
end
render json: @users
end
def create
@user = User.new user_params
if @user.save
render json: @user
else
render nothing: true
end
end
def update
if @user.update_attributes user_params
render json: @user
else
render nothing: true
end
end
def destroy
if @user.destroy
else
render nothing: true
end
end
private
def user_params
params.require(:user).permit :id, :name, :age, :email, :address
end
def load_user
@user = User.find_by id: params[:id]
end
end
components
Ở đây, mình tạo 1 class cha là UserTemplate, sau đó render các template con
// app/assets/javascripts/components/user_template.jsx
class UserTemplate extends React.Component {
constructor() {
super();
this.state = {users: []};
}
componentDidMount() {
this.getDataUserFromApi();
}
// dùng để lấy tất cả data của users
getDataUserFromApi() {
var self = this;
$.ajax({
url: '/api/v1/users',
success: function(data) {
self.setState({users: data});
},
error: function(xhr, status, error) {
alert('Cannot get data from API: ', error);
}
});
}
// delete record
handleDeleteRecord(user) {
var data = user;
const {users} = this.state;
const update_state = (state) => this.setState(state);
$.ajax({
method: 'DELETE',
url: '/api/v1/users/' + user.id,
success: function() {
let index = users.findIndex(function(e) {return e.id == data.id});
users.splice(index, 1);
update_state({users: users});
},
error: function(xhr, status, error) {
alert('Cannot delete requested record: ', error);
}
});
}
// validdate record
validRecord(object) {
if((object.name == undefined && object.email == undefined && object.age == undefined && object.address == undefined) ||
(object.name == "" && object.email == "" && object.age == "" && object.address == "")) {
return false;
} else {
return true;
}
}
// update record
handleUpdateRecord(old_user, user) {
var data_old = old_user;
const {users} = this.state;
const update_state = (state) => this.setState(state);
if(this.validRecord(user)){
$.ajax({
method: 'PUT',
url: '/api/v1/users/' + data_old.id,
data: {user: user},
success: function(data) {
let index = users.findIndex(function(e) {return e.id == data_old.id});
users.splice(index, 1, data);
update_state({users: users});
},
error: function(xhr, status, error) {
alert('Cannot update requested record: ', error);
}
});
}
}
// add record
handleAddRecord(user) {
const {users} = this.state;
const update_state = (state) => this.setState(state);
if(this.validRecord(user)) {
$.ajax({
url: '/api/v1/users',
method: 'POST',
data: {user: user},
success: function(data) {
users.push(data);
update_state({users: users});
},
error: function(xhr, status, error) {
alert('Cannot add a new record: ', error);
}
});
}
}
// search data
handleSearch(data) {
const {users} = this.state;
const update_state = (state) => this.setState(state);
$.ajax({
url: '/api/v1/users',
data: {data: data},
success: function(data) {
update_state({users: data})
},
error: function(xhr, status, error) {
alert('Search error: ', status, xhr, error);
}
});
}
render() {
return(
<div className="container">
<h2>REACTJS DEMO</h2>
<br />
<div className="row">
<div className="col-md-12">
<h3>New User</h3>
</div>
<div className="col-md-12">
<div className="panel panel-default">
<div className="panel-body">
<NewUser handleAddRecord={this.handleAddRecord.bind(this)} />
</div>
</div>
</div>
<br />
<div className="col-md-12">
<h3>Search</h3>
</div>
<div className="col-md-12">
<div className="panel panel-default">
<div className="panel-body">
<SearchUser handleSearch={this.handleSearch.bind(this)} />
</div>
</div>
</div>
<br />
<div className="col-md-12">
<UserTable users={this.state.users}
handleDeleteRecord={this.handleDeleteRecord.bind(this)}
handleUpdateRecord={this.handleUpdateRecord.bind(this)} />
</div>
</div>
</div>
)
}
}
Table user
// app/assets/javascripts/components/user_table.jsx
class UserTable extends React.Component {
constructor() {
super();
}
handleDeleteRecord(user) {
this.props.handleDeleteRecord(user);
}
handleUpdateRecord(old_user, user) {
this.props.handleUpdateRecord(old_user, user);
}
render() {
var users = [];
this.props.users.forEach(function(user) {
users.push(<User user={user} key={'user' + user.id}
handleDeleteRecord={this.handleDeleteRecord.bind(this, user)}
handleUpdateRecord={this.handleUpdateRecord.bind(this)} />);
}.bind(this));
return(
<table className="table table-striped">
<thead>
<tr>
<th className="col-md-2"><center>Name</center></th>
<th className="col-md-1"><center>Age</center></th>
<th className="col-md-2"><center>Email</center></th>
<th className="col-md-3"><center>Address</center></th>
<th className="col-md-2"><center>Action</center></th>
</tr>
</thead>
<tbody>
{users}
</tbody>
</table>
)
}
}
user
// app/assets/javascripts/components/user.jsx
class User extends React.Component {
constructor() {
super();
this.state = {edit: false};
this.updateInput = this.updateInput.bind(this);
}
propTypes: {
name: React.PropTypes.string,
age: React.PropTypes.integer,
email: React.PropTypes.string,
address: React.PropTypes.string
}
updateInput(name, value) {
this.setState({[name]: value});
}
handleUpdateRecord() {
this.props.handleUpdateRecord(this.props.user, this.getDataRecord());
this.setState({edit: false});
}
getDataRecord() {
user_data = {
name: this.state["name"],
age: this.state["age"],
email: this.state["email"],
address: this.state["address"]
};
return user_data;
}
handleToggle(e) {
e.preventDefault();
this.setState({edit: !this.state.edit});
}
renderRecord() {
var user = this.props.user;
return(
<tr>
<td><center>{user.name}</center></td>
<td><center>{user.age}</center></td>
<td><center>{user.email}</center></td>
<td><center>{user.address}</center></td>
<td>
<center>
<a className="btn btn-danger btn-xs" onClick={this.props.handleDeleteRecord}>delete</a>
 
<a className="btn btn-warning btn-xs" onClick={this.handleToggle.bind(this)}>edit</a>
</center>
</td>
</tr>
);
}
renderForm() {
return(
<tr>
<td>
<input name="name" defaultValue={this.props.user.name} className="form-control" type="text"
onChange={(e) => this.updateInput(e.target.name, e.target.value)}/>
</td>
<td>
<input name="age" defaultValue={this.props.user.age} className="form-control" type="number"
min="0" onChange={(e) => this.updateInput(e.target.name, e.target.value)}/>
</td>
<td>
<input name="email" defaultValue={this.props.user.email} className="form-control" type="email"
onChange={(e) => this.updateInput(e.target.name, e.target.value)}/>
</td>
<td>
<input name="address" defaultValue={this.props.user.address} className="form-control" type="text"
onChange={(e) => this.updateInput(e.target.name, e.target.value)}/>
</td>
<td>
<center>
<a className="btn btn-success btn-xs" onClick={this.handleUpdateRecord.bind(this)}>Save</a>
 
<a className="btn btn-default btn-xs" onClick={this.handleToggle.bind(this)}>Cancel</a>
</center>
</td>
</tr>
);
}
render() {
if (this.state.edit) {
return(this.renderForm());
} else {
return(this.renderRecord());
}
}
}
search user
// app/assets/javascripts/components/search_user.jsx
class SearchUser extends React.Component {
constructor() {
super();
this.defaultRecord();
}
componentWillUpdate(nextProps, nextState) {
if(this.state != nextState) {
this.handleSearch(nextState);
}
}
updateInput(name, value) {
this.setState({[name]: value});
}
handleSearch(data) {
this.props.handleSearch(data);
}
getDataRecord() {
user_data = {
name: this.state["name"],
age: this.state["age"],
email: this.state["email"],
address: this.state["address"]
};
return user_data;
}
defaultRecord() {
this.state = {name: "", age: "", email: "", address: ""}
}
render() {
return(
<form className="form-inline">
<div className="form-group">
<input name="name" value={this.state.name} className="form-control" type="text"
placeholder="Enter name" onChange={(e) => {this.updateInput(e.target.name, e.target.value)}} />
 
<input name="age" value={this.state.age} className="form-control" type="number" min="0"
placeholder="Enter age" onChange={(e) => this.updateInput(e.target.name, e.target.value)} />
 
<input name="email" value={this.state.email} className="form-control" type="email"
placeholder="Enter email" onChange={(e) => this.updateInput(e.target.name, e.target.value)} />
 
<input name="address" value={this.state.address} className="form-control" type="text"
placeholder="Enter address" onChange={(e) => this.updateInput(e.target.name, e.target.value)} />
</div>
</form>
)
}
}
new user
// app/assets/javascripts/components/new_user.jsx
class NewUser extends React.Component {
constructor() {
super();
this.defaultRecord();
this.updateInput = this.updateInput.bind(this);
}
propTypes: {
name: React.PropTypes.string,
event_date: React.PropTypes.string,
place: React.PropTypes.string,
description: React.PropTypes.string
}
updateInput(name, value) {
this.setState({[name]: value});
}
handleAddRecord() {
user_data = {
name: this.state["name"],
age: this.state["age"],
email: this.state["email"],
address: this.state["address"]
};
this.props.handleAddRecord(user_data);
this.defaultRecord();
}
defaultRecord() {
this.state = {name: "", age: "", email: "", address: ""}
}
render() {
return(
<form className="form-inline">
<div className="form-group">
<input name="name" value={this.state.name} className="form-control" type="text"
placeholder="Enter name" onChange={(e) => this.updateInput(e.target.name, e.target.value)} />
 
<input name="age" value={this.state.age} className="form-control" type="number" min="0"
placeholder="Enter age" onChange={(e) => this.updateInput(e.target.name, e.target.value)} />
 
<input name="email" value={this.state.email} className="form-control" type="email"
placeholder="Enter email" onChange={(e) => this.updateInput(e.target.name, e.target.value)} />
 
<input name="address" value={this.state.address} className="form-control" type="text"
placeholder="Enter address" onChange={(e) => this.updateInput(e.target.name, e.target.value)} />
</div>
 
<button type="button" onClick={this.handleAddRecord.bind(this)}
className="btn btn-primary btn-sm">Add</button>
</form>
)
}
}
Views
<!-- app/views/users/index.html.erb -->
<%= react_component "UserTemplate" %>
Kết quả
Source code : https://github.com/tjn0994/react_demo
All rights reserved