12/08/2018, 16:22
ReactJs với Ruby on Rails 5 (Phần 3)
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 ...
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
<%= react_component "UserTemplate" %>
Source code : https://github.com/tjn0994/react_demo