-7

Nested attributes with Reactjs

Xin chào các bạn. Hôm nay mình xin chia sẻ với các bạn cách sử dụng nested attributes với react mà mình biết.

Chắc hẳn khi làm việc với rails các bạn cũng làm với nested attributes sử dụng form for. Form_for đã xây dựng sẵn params truyền lên controller cho các bạn. Tuy nhiên với Reactjs thì sao khi không có form_for cho các bạn sử dụng??? Với bài này hi vọng các bạn hiểu hơn về cách truyền params lên khi sử dụng nested attribute!!!

1. Bài toán đưa ra.

Question có nhiều answer. Khi tạo Question thì mình tạo answer sử dụng nested_attributes.

2. Xây dựng ứng dụng

Tạo project

cd work_space # Vào thư mục work_space
rails new my_app_nested_attributes
cd my_app_nested_attributes
bundle

Tạo model

rails g model Question title:string
rails g model Answer title:string question_id:integer is_correct:boolean
  • Ta có question 1-n answer
  • Add nested_attributes vào trong model question
class Question < ApplicationRecord
  has_many :answers, dependent: :destroy
  accepts_nested_attributes_for :answers, allow_destroy: true
end
  • Association trong model Answer
class Answer < ApplicationRecord
  belongs_to :question
end

Tạo controller để create question

rails g controller Questions --skip-assets
class QuestionsController < ApplicationController
  def create
    @question = Question.new question_params
    if @question.save
      render json: {message: "Create success", question: @question}, status: :ok
    else
      render json: {mesage: "Create fail", error: @question.errors}, status: :500
    end
  end
    
  private
    
  def question_params
    params.require(:question).permit :title, answers_attributes: [:id, :title, :is_correct, :_destroy]
  end
end

Tạo routes

#config/routes.rb
resources :question, only: :create

3. Client with Reactjs

  • Tới đây bạn phải có 1 app react rồi thì ms thực hiện được
  • Chúng ta sử dụng axios để gửi request từ client lên server
  • Sử dụng FormData để lưu dữ liệu
#form question

import React from 'react';
import axios from 'axios';
import ReactOnRails from 'react-on-rails';

export default class FormQuestion extends React.Component {
  constructor(props) {
    super(props);
    this.state = {
      title: ' ',
      answer_detail: {
        title: ' ',
        is_correct: ' '
      },
      answers: [ ]
    }
  }
    
  render() {
    return(
      <form onSubmit={this.handleSubmitCreateQuestion.bind(this)}>
        <div className='form-group'>
          <input type='text' placeholder='title' value={this.state.title}  
            className='form-control' name='title'
            onChange={this.handleChange.bind(this)} />
         </div>
         <button className='btn btn-success' type='submit'>Create</button>
      </form>
            
      <form>
        <div className='form-group'>
          <input type='text' placeholder='title' value={this.state.answer_detail.title}  
            className='form-control' name='title'
            onChange={this.handleChangeAnswer.bind(this)} />
        </div>
         <div className='form-group'>
           <input type='text' placeholder='title' value={this.state.answer_detail.is_correct} 
             className='form-control' name='title'
             onChange={this.handleChangeAnswer.bind(this)} />
         </div>
         <button className='btn btn-success' onClick={this.handleAddAnswer.bind(this)}>
           Add answer
         </button>
      </form>
    );
  }
  // Thay đổi input form answers
  handleChangeAnswer(e) {
    let attribute = event.target.name;
    Object.assign(this.state.answer_detail, {[attribute]: event.target.value});
    this.setState({
      answer_detail: this.state.answer_detail
    });        
  }
    
  // Thay đổi input form question
  handleChange(e) {
    let attribute = event.target.name;
    Object.assign(this.state.title, {[attribute]: event.target.value});
    this.setState({
      title: this.state.title
    });
  }
    
  //Add answer vào mảng answers
  handleAddAnswer(e) {
    e.prevenDefault();
    this.state.answers.push(this.state.answer_detail);
    this.setState({
      answers: this.state.answers,
      anser_detail: {
        title: ' ',
        is_correct: ' '
      }
    })
  }
    
  //gửi len sever
  handleSubmitCreateQuestion(e) {
    e.preventDefault();
    let formData = new FormData();
    formData.append('question[title]', this.state.title);
        
     //Đoạn code dưới này đây là đế xử lý params truyền lên controller
     this.state.answers.map((answer_detail, index) => {
       formData.append('question[answers_attributes]['+ index +'][title]', answer_detail.title );
       formData.append('question[answers_attributes]['+ index +'][is_correct]', answer_detail.is_correct );
      }
  
     formData.append('authenticity_token', ReactOnRails.authenticityToken()); //Token with react-on-rails sinh ra.
        
     axios({
       url: 'https://localhost:3000/questions,
       method: 'POST',
       data: formData
     })
     .then(response => {
       alert('success');
     })
     .catch(error => {
       console.log(error);
     });
   }
}

Trên đây là cách mà sử dụng nested_attribute với view tachs biệt rails. Bài viết có gì sai sót mong mọi người góp ý.

Note*: Quan trọng các ban có thể add được params vào request lên controller là oke. Các bạn byebug vào controller để xem params nhé. rất hay đó.

Gợi ý*: Các bạn có thể sử dụng create reactjs với webpack react-on-rails để tạo một ứng dụng sử dụng react trong rails https://github.com/shakacode/react-webpack-rails-tutorial


All Rights Reserved

Viblo
Let's register a Viblo Account to get more interesting posts.