Last active
April 25, 2018 16:06
-
-
Save steveburkett/b9993e3a5b6f6079935636b6bf3b54ed to your computer and use it in GitHub Desktop.
Stonecrop ReactJS basics
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
rails g model Part name:string description:text | |
rake db:migrate |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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 | |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
# 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 |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#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 | |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#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; | |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
//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; | |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
//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; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
//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