ReactJS và Ruby on Rails

  1. ReactJS là một Javascript Framework cực kỳ mạnh được tạo ra bởi Facebook. Nó đang là chủ để rất nóng trong thời gian gần đây. Vì vậy mình đã thử viết 1 ứng dụng với ReatcJS và cảm thấy nó khá hấp dẫn về tốc độ xử lý. Ví dụ dưới đây mình viết trên Ruby on Rails

  2. Để bắt đầu thì chung ta trước hết cài đặt môi trường làm việc với ReactJS

  • Thêm react-rails tới gemfile của bạn :
gem 'react-rails', '~> 1.0'
- Tiếp đó, bạn chạy cài đặt:

rails g react:install

  • Nó sẽ tạo cho bạn 1 file components.js ở trong thư mục app/assets/javascripts/components/.
  • Trong file application.js của bạn sẽ thêm :
//= require react
//= require react_ujs
//= require components
- Tiếp đó bạn tạo 1 file model.

rails g model IngredientSuggestions item:string

  • Tại thời điểm này, bạn có thể sử dụng lệnh
rake db:migrate
- Tạo 1 controller có tên là **ingredient_suggestions_controller.rb**. Trong đó bạn viết:
```ruby

class IngredientSuggestionsController < ApplicationController
  def index
    @ingredients = IngredientSuggestion.select("id, item").order("item ASC").to_json
  end
  def update
    ingredient = IngredientSuggestion.find(params[:id])
    ingredient.item = params[:item]
    ingredient.save!
    render :nothing => true, :status => 200
  end
  def destroy
    ingredient = IngredientSuggestion.find(params[:id])
    ingredient.destroy
    render :nothing => true, :status => 200
  end
end
3. Dữ liệu Model trong Fluxxor
- Ở đây chúng ta tạo 1 file có tên là **ingredient_suggestions.jsx**. Một đối tượng JavaScript lưu giữ dữ liệu và xác định các hành động mà của nó:

```javascript
var fluxIngredientSuggestionsStore = {};
fluxIngredientSuggestionsStore.constants = {
  UPDATE_INGREDIENT: "UPDATE_INGREDIENT",
  DELETE_INGREDIENT: "DELETE_INGREDIENT",
};
- Các đối tượng có chứa các dữ liệu của mình, khởi tạo thông qua Fluxxor createStore method.
```javascript

fluxIngredientSuggestionsStore.store = Fluxxor.createStore({
      		initialize: function(options) {
    			this.ingredients = options.ingredients || [];
    			this.bindActions(fluxIngredientSuggestionsStore.constants.UPDATE_INGREDIENT, this.onUpdateIngredient, fluxIngredientSuggestionsStore.constants.DELETE_INGREDIENT, this.onDeleteIngredient);
  			},
  			getState: function() {
    			return {
      				ingredients: this.ingredients,
    			};
  			},
  			onUpdateIngredient: function(payload) {
    			payload.ingredient.item = payload.new_name;
                this.emit("change")
  			},
  			onDeleteIngredient: function(payload) {
            	this.ingredients = this.ingredients.filter(function(ingredient) {
      				return ingredient.id != payload.ingredient.id
    			});
    			this.emit("change");
  			}
});
- Các đối tượng cũng chứa các hành động của chúng, thông qua các API đơn giản:

```javascript
fluxIngredientSuggestionsStore.actions = {
  			updateIngredient: function(ingredient, new_name) {
    			this.dispatch(fluxIngredientSuggestionsStore.constants.UPDATE_INGREDIENT, {
      				ingredient: ingredient,
      				new_name: new_name
    			});
            $.ajax({
               type: "PUT",
               url: "/ingredient_suggestions/" + ingredient.id,
               data: {
                    item: new_name
               },
               success: function() {
                    $.growl.notice({
                        title: "Ingredient suggestion updated",
                    });
               },
               failure: function() {
                    $.growl.error({
                        title: "Error updating ingredient suggestion",
                    });
                }
            });
          },
  		  	deleteIngredient: function(ingredient) {
    			this.dispatch(fluxIngredientSuggestionsStore.constants.DELETE_INGREDIENT, {
      				ingredient: ingredient
    			});
                $.ajax({
                  	type: "DELETE",
                  	url: "/ingredient_suggestions/" + ingredient.id,
                  	success: function(data) {
                    	$.growl.notice({
                      		title: "Ingredient suggestion deleted",
                    	});
                  	}.bind(this),
                  	failure: function() {
                    	$.growl.error({
                      		title: "Error deleting ingredient suggestion",
                    	});
                  	}
                });
  			}
};
- Cuối cùng, các đối tượng bao gồm một phương pháp để tạo một đối tượng Flux cất chứa và hành động của mình. Các đối tượng Flux được thông qua với giao diện người dùng dựa trên phản ứng lại trong phần tiếp theo.
```javascript

fluxIngredientSuggestionsStore.init = function(ingredients) {
            var tempStore = {
                IngredientSuggestionsStore: new fluxIngredientSuggestionsStore.store({
                    ingredients: ingredients
                })
            };
            fluxIngredientSuggestionsStore.flux = new Fluxxor.Flux(tempStore, fluxIngredientSuggestionsStore.actions);
}
- Tìm hiểu thêm về Fluxxor và làm thế nào nó sẽ giúp bạn dễ dàng xác định các mô hình dữ liệu mà chơi tốt với phản ứng lại. Để biết thêm về các khái niệm, kiểm tra tổng quan Flux của Facebook.
- Hiển thị dữ liệu từ database:

```javascript
var IngredientSuggestionsEditor = React.createClass({
            mixins: [FluxMixin, StoreWatchMixin("IngredientSuggestionsStore")],
            getStateFromFlux: function() {
            	var flux = this.getFlux();
            	return {
            		ingredients: flux.store("IngredientSuggestionsStore").getState().ingredients
            	};
            },
            render: function() {
            	var props = this.props;
            	var ingredients = this.state.ingredients.map(function (ingredient) {
            	return < IngredientSuggestion ingredient={ingredient} key={ingredient.id} flux={props.flux} />
            	});
            	return (
                    <div>
                    {ingredients}
                    </div>
            	);
            }
});
- Chức năng update, delete dữ liệu:
```javascript

var IngredientSuggestion = React.createClass({
          mixins: [FluxMixin],
          getInitialState: function() {
            return {changed: false, updated: false};
          },
          	render: function() {
                return (
                  	<div>
                    <a href="#" onClick={this.handleDelete}> < i className="fa fa-times"> </ i> </a>
                    < input onChange={this.handleChange} ref="ingredient" defaultValue={this.props.ingredient.item} />
                    {
                        this.state.changed ?
                        <span>
                            <a href="#" onClick={this.handleUpdate}>Update</a>
                            <a href="#" onClick={this.handleCancelChange}>Cancel</a>
                        </span>
                    :
                        ""
                    }
                    </div>
                )
          	},
            handleChange: function() {
            	if ($(this.refs.ingredient.getDOMNode()).val() != this.props.ingredient.item) {
            		this.setState({changed: true});
            	} else {
                	this.setState({changed: false});
            	}
            },
          	handleUpdate: function(e) {
            	e.preventDefault();
            	this.getFlux().actions.updateIngredient(this.props.ingredient, $(this.refs.ingredient.getDOMNode()).val());
            	this.setState({changed: false, updated: true});
          	},
          	handleDelete: function(e) {
            	e.preventDefault();
            	if (confirm("Delete " + this.props.ingredient.item + "?")) {
              		this.getFlux().actions.deleteIngredient(this.props.ingredient);
            	}
          	},
          	handleCancelChange: function(e) {
            	e.preventDefault();
                $(this.refs.ingredient.getDOMNode()).val(this.props.ingredient.item);
            	this.setState({changed: false});
          	}
});
- Render dữ liệu:

```javascript
window.loadIngredientSuggestionsEditor = function(ingredients) {
  /* Define the Fluxxor store (above) */
  /* ... */
  /* Define the React components (above) */
  /* ... */
  /* Load the Fluxxor store and render React components to the page */
  fluxIngredientSuggestionsStore.init(ingredients);
          	React.render(< IngredientSuggestionsEditor flux={fluxIngredientSuggestionsStore.flux} />,
          	document.getElementById('js-ingredient-suggestions-editor'));
     }
- Cuối cùng là hiển thị dữ liệu thông qua view:
```haml

:javascript
          $(document).ready(function() {
            window.loadIngredientSuggestionsEditor(#{@ingredients});
          });
%h1 Edit ingredient suggestions