ReactJs với Ruby on Rails 5 (Phần 3)

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>
            &emsp;
            <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>
            &emsp;
            <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)}} />
          &emsp;
          <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)} />
          &emsp;
          <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)} />
          &emsp;
          <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)} />
          &emsp;
          <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)} />
          &emsp;
          <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)} />
          &emsp;
          <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>
        &emsp;
        <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