Skip to content

Instantly share code, notes, and snippets.

@steveburkett
Last active April 25, 2018 16:06
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save steveburkett/b9993e3a5b6f6079935636b6bf3b54ed to your computer and use it in GitHub Desktop.
Save steveburkett/b9993e3a5b6f6079935636b6bf3b54ed to your computer and use it in GitHub Desktop.
Stonecrop ReactJS basics
rails g model Part name:string description:text
rake db:migrate
gem 'faker'
#db/seeds.rb
Part.destroy_all
1.upto(10) {|num|
Part.create!(name: "Part No. #{num}", description: Faker::Superhero.power)
}
rake db:create
rake db:migrate
rake db:seed
# app/controllers/application_controller.rb
class ApplicationController < ActionController::Base
protect_from_forgery with: :null_session
end
# app/models/part.rb
class Part < ApplicationRecord
end
# app/controllers/api/parts_controller.rb
class Api::PartsController < ApplicationController
def index
@parts = Part.all
render json: { data: @parts }
end
def create
@part = Part.create!(part_params)
render json: { data: @part.reload }
end
def destroy
@part = Part.find(params[:id])
@part.destroy
head :no_content
end
def update
@part = Part.find(params["id"])
@part.update_attributes(part_params)
render json: { data: @part.reload }
end
private
def part_params
params.require(:part).permit(:id, :name, :description)
end
end
#app/config/routes.rb
Rails.application.routes.draw do
namespace :api, defaults: { format: :json } do
resources :parts, only: [:index, :create, :destroy, :update]
end
end
#controllers/pages_controller.rb
class PagesController < ApplicationController
def index
end
end
#in routes.rb, add ...
root to: 'pages#home'
#creating a parts_cart pack
// app/assets/javascripts/packs/parts_cart.js
import 'parts'
#the entry point into the (react) parts app
// app/javascript/parts/index.js
import React from 'react'
import ReactDOM from 'react-dom'
import App from './components/App'
const parts = document.querySelector('#parts')
ReactDOM.render(<App />, parts)
#this is the top level parts component
// app/javascript/parts/components/App.js
import React from 'react'
const App = (props) => {
return (
<p>Hi there, reactJS works!</p>
);
}
export default App
#app/views/pages/home.html.erb
<%= javascript_pack_tag 'parts_cart' %>
// app/javascript/packs/parts_cart.js
import 'parts'
#Profile.dev
web: bundle exec rails s
webpacker: ./bin/webpack-dev-server
foreman start -f Procfile.dev -p 3000
#add bootstrap to Gemfile & bundle
gem 'bootstrap', '~> 4.0.0.beta'
#add styles to app/assets/stylesheets/application.scss
@import "bootstrap";
.part div, .part input {
width: 25%;
vertical-align: top;
margin-bottom: 5px;
}
.cart {
margin-top: 30px;
}
.header, .part {
margin-bottom: 5px;
}
.part-button {
margin-left: 5px;
}
.parts {
margin-top: 10%;
text-align: center;
}
.parts h3 {
margin-bottom: 20px;
}
.parts {
margin-top: 10%;
text-align: center;
}
.parts h3 {
margin-bottom: 20px;
}
//app/javascript/parts/components/Body.js
import React from 'react';
import jquery from 'jquery'
import AllParts from './AllParts'
class Body extends React.Component {
constructor() {
super();
this.state = {
parts: []
};
}
componentDidMount() {
jquery.getJSON('/api/parts.json', (response) => {
this.setState({ parts: response.data })
});
}
render() {
return (
<div className='parts'>
<h3> Parts Cart </h3>
<AllParts parts={this.state.parts}/>
</div>
)
}
}
export default Body;
//app/javascript/parts/components/AllParts.js
import React from 'react'
import Part from './Part'
const AllParts = (props) => {
let parts = props.parts.map((part) => {
return (
<div key={part.id}>
<Part part={part} />
</div>
)
});
return(
<div> {parts} </div>
)
}
export default AllParts;
//app/javascript/parts/components/Part.js
import React from 'react';
class Part extends React.Component {
render() {
var name = <div className="d-inline-block col-sm-2">{this.props.part.name}</div>;
var description = <div className="d-inline-block col-sm-8">{this.props.part.description}</div>;
return (
<div className="part">
{name}
{description}
</div>
)
}
}
export default Part;
//app/javascript/parts/components/NewPart.js
import React from 'react';
import jquery from 'jquery'
class NewPart extends React.Component {
constructor(props) {
super(props);
this.state = { name: '', description: ''}
}
render() {
return (
<div className="part">
<input className="col-sm-2" type="text" placeholder='New part name'
onChange={event => this.setState({ name: event.target.value })}/>
<input className="col-sm-8" type="text" placeholder='New description'
onChange={event => this.setState({ description: event.target.value })}/>
<button className="part-button" onClick={(event) => this.handleClick(event) }>Add</button>
</div>
)
}
handleClick(event) {
jquery.ajax({ url: '/api/parts',
type: 'POST',
data: { part: { name: this.state.name, description: this.state.description } },
success: (part) => {
this.props.handleSubmit(part.data);
}
});
event.preventDefault();
}
}
export default NewPart;
//app/javascript/parts/components/Body.js
import React from 'react';
import jquery from 'jquery'
import AllParts from './AllParts'
import NewPart from './NewPart'
class Body extends React.Component {
constructor() {
super();
this.state = {
parts: []
};
}
componentDidMount() {
jquery.getJSON('/api/parts.json', (response) => {
this.setState({ parts: this.orderParts(response.data) })
});
}
orderParts(parts) {
return parts.sort(function(a,b) {return (a.id > b.id) ? 1 : ((b.id > a.id) ? -1 : 0);} );
}
render() {
return (
<div className='parts'>
<h3> Parts Cart </h3>
<NewPart handleSubmit={ event => this.handleSubmit(event) } />
<AllParts parts={this.state.parts}/>
</div>
)
}
handleSubmit(part) {
var newState = this.state.parts.concat(part); //note: important to create copy of state and then setState
this.setState({ parts: newState });
}
}
export default Body;
//app/javascript/parts/components/Part.js
import React from 'react';
class Part extends React.Component {
render() {
var name = <div className="d-inline-block col-sm-2">{this.props.part.name}</div>;
var description = <div className="d-inline-block col-sm-8">{this.props.part.description}</div>;
return (
<div className="part">
{name}
{description}
<button className="part-button" onClick={this.props.handleDelete} >Delete</button>
</div>
)
}
}
export default Part;
//app/javascript/parts/components/AllParts.js
import React from 'react';
import Part from './Part'
class AllParts extends React.Component {
render() {
let parts = this.props.parts.map((part) => {
return (
<div key={part.id}>
<Part part={part}
handleDelete={this.handleDelete.bind(this, part.id)}
/>
</div>
)
});
return(
<div> {parts} </div>
)
}
handleDelete(part_id) {
this.props.handleDelete(part_id);
}
}
export default AllParts;
//app/javascript/parts/components/Body.js
import React from 'react';
import jquery from 'jquery'
import AllParts from './AllParts'
import NewPart from './NewPart'
class Body extends React.Component {
constructor() {
super();
this.state = {
parts: []
};
}
componentDidMount() {
jquery.getJSON('/api/parts.json', (response) => {
this.setState({ parts: this.orderParts(response.data) })
});
}
orderParts(parts) {
return parts.sort(function(a,b) {return (a.id > b.id) ? 1 : ((b.id > a.id) ? -1 : 0);} );
}
render() {
return (
<div className='parts'>
<h3> Parts Cart </h3>
<NewPart handleSubmit={ event => this.handleSubmit(event) } />
<AllParts parts={this.state.parts} handleDelete={ this.handleDelete.bind(this) } />
</div>
)
}
handleSubmit(part) {
var newState = this.state.parts.concat(part);
this.setState({ parts: newState });
}
removePart(part_id) {
var newParts = this.state.parts.filter((part) => {
return part.id != part_id;
});
this.setState({ parts: newParts });
}
handleDelete = (part_id) => {
console.log("Body.handleDelete " + part_id);
jquery.ajax({
url: `/api/parts/${part_id}`,
context: this,
type: 'DELETE',
success(response) {
this.removePart(part_id);
}
});
}
}
export default Body;
//app/javascript/parts/components/Part.js
import React from 'react';
class Part extends React.Component {
constructor() {
super();
this.state = {
editable: false
};
}
render() {
let name = this.state.editable ?
<input className="col-sm-2" type='text' ref={(input) => this.name = input} defaultValue={this.props.part.name} /> :
<div className="d-inline-block col-sm-2">{this.props.part.name}</div>;
let description = this.state.editable ?
<input className="col-sm-4" type='text' ref={(input) => this.description = input} defaultValue={this.props.part.description} />:
<div className="d-inline-block col-sm-4">{this.props.part.description}</div>;
return (
<div className="item">
{name}
{description}
<button onClick={this.handleEdit.bind(this)}> {this.state.editable ? 'Submit' : 'Edit' } </button>
<button className="item-button" onClick={this.props.handleDelete} >Delete</button>
</div>
)
}
handleEdit() {
if(this.state.editable) {
let name = this.name.value;
let id = this.props.part.id;
let description = this.description.value;
let part = {id: id , name: name , description: description};
this.props.handleUpdate(part);
}
this.setState({ editable: !this.state.editable })
}
}
export default Part;
//app/javascript/parts/components/AllParts.js
import React from 'react';
import Part from './Part'
class AllParts extends React.Component {
render() {
let parts = this.props.parts.map((part) => {
return (
<div key={part.id}>
<Part part={part}
handleDelete={this.handleDelete.bind(this, part.id)}
handleUpdate={this.handleUpdate.bind(this)}
/>
</div>
)
});
return(
<div> {parts} </div>
)
}
handleDelete(part_id) {
this.props.handleDelete(part_id);
}
handleUpdate(part) {
this.props.handleUpdate(part);
}
}
export default AllParts;
//app/javascript/parts/components/Body.js
import React from 'react';
import jquery from 'jquery'
import AllParts from './AllParts'
import NewPart from './NewPart'
class Body extends React.Component {
constructor() {
super();
this.state = {
parts: []
};
}
componentDidMount() {
jquery.getJSON('/api/parts.json', (response) => {
this.setState({ parts: this.orderParts(response.data) })
});
}
orderParts(parts) {
return parts.sort(function(a,b) {return (a.id > b.id) ? 1 : ((b.id > a.id) ? -1 : 0);} );
}
render() {
return (
<div className='parts'>
<h3> Parts Cart </h3>
<NewPart handleSubmit={ event => this.handleSubmit(event) } />
<AllParts parts={this.state.parts}
handleDelete={ this.handleDelete.bind(this) }
handleUpdate={ this.handleUpdate.bind(this) }
/>
</div>
)
}
handleSubmit(part) {
var newState = this.state.parts.concat(part);
this.setState({ parts: newState });
}
removePart(part_id) {
var newParts = this.state.parts.filter((part) => {
return part.id != part_id;
});
this.setState({ parts: newParts });
}
handleDelete = (part_id) => {
console.log("Body.handleDelete " + part_id);
jquery.ajax({
url: `/api/parts/${part_id}`,
context: this,
type: 'DELETE',
success(response) {
this.removePart(part_id);
}
});
}
updateParts(part) {
var parts = this.state.parts.filter((state_part) => {
return state_part.id != part.id
});
parts.push(part);
this.setState({parts: this.orderParts(parts) });
}
handleUpdate = (part) => {
console.log("Body.handleUpdate " + part.id);
jquery.ajax({
url: `/api/parts/${part.id}`,
type: 'PUT',
context: this,
data: { part: part },
success: () => { this.updateParts(part);
}
})
}
}
export default Body;
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment