Skip to content

Commit 7c6c30b

Browse files
committed
Merge pull request shakacode#102 from shakacode/77-simple-example
Add simple React comment form example
2 parents 162eae7 + d677265 commit 7c6c30b

17 files changed

+234
-56
lines changed

.gitignore

+3
Original file line numberDiff line numberDiff line change
@@ -28,3 +28,6 @@ vendor/ruby
2828

2929
# Generated js bundles
3030
/app/assets/javascripts/generated/*
31+
32+
# Rubymine/IntelliJ
33+
.idea

app/assets/javascripts/application.js

+5
Original file line numberDiff line numberDiff line change
@@ -20,3 +20,8 @@
2020

2121
//= require bootstrap-sprockets
2222
//= require turbolinks
23+
24+
$(document).on('ready page:load', function () {
25+
$('nav a').parents('li,ul').removeClass('active');
26+
$('nav a[href="' + this.location.pathname + '"]').parents('li,ul').addClass('active');
27+
});

app/controllers/pages_controller.rb

+3
Original file line numberDiff line numberDiff line change
@@ -21,4 +21,7 @@ def index
2121
# format.html
2222
# end
2323
end
24+
25+
def simple
26+
end
2427
end

app/views/comments/_form.html.erb

+3-3
Original file line numberDiff line numberDiff line change
@@ -12,14 +12,14 @@
1212
<% end %>
1313

1414
<div class="field">
15-
<%= f.label :author %><br>
15+
<%= f.label :author, 'Your Name' %><br>
1616
<%= f.text_field :author %>
1717
</div>
1818
<div class="field">
19-
<%= f.label :text %><br>
19+
<%= f.label :text, 'Say something using markdown...' %><br>
2020
<%= f.text_area :text %>
2121
</div>
2222
<div class="actions">
23-
<%= f.submit %>
23+
<%= f.submit 'Post' %>
2424
</div>
2525
<% end %>

app/views/comments/show.html.erb

+11-9
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,16 @@
1-
<p id="notice"><%= notice %></p>
1+
<div class="comment">
2+
<p id="notice"><%= notice %></p>
23

3-
<p>
4-
<strong>Author:</strong>
5-
<%= @comment.author %>
6-
</p>
4+
<p>
5+
<strong>Author:</strong>
6+
<%= @comment.author %>
7+
</p>
78

8-
<p>
9-
<strong>Text:</strong>
10-
<%= @comment.text %>
11-
</p>
9+
<p>
10+
<strong>Text:</strong>
11+
<%= @comment.text %>
12+
</p>
13+
</div>
1214

1315
<%= link_to 'Edit', edit_comment_path(@comment) %> |
1416
<%= link_to 'Back', comments_path %>

app/views/layouts/application.html.erb

+3-2
Original file line numberDiff line numberDiff line change
@@ -22,9 +22,10 @@
2222
<!-- Collect the nav links, forms, and other content for toggling -->
2323
<div class="collapse navbar-collapse" id="bs-example-navbar-collapse-1">
2424
<ul class="nav navbar-nav">
25-
<li><a href="/">React Demo</a></li>
25+
<li class="active"><%= link_to "React Demo", root_path %></li>
26+
<li><%= link_to "Simple React", simple_path %></li>
2627
<li><%= link_to "Classic Rails", comments_path %></li>
27-
<li><%= link_to "by Justin Gordon, www.railsonmaui.com", "http://www.railsonmaui.com" %></li>
28+
<li><%= link_to "www.shakacode.com", "http://www.shakacode.com" %></li>
2829
<li><%= link_to "Source on Github", "https://github.com/shakacode/react-webpack-rails-tutorial" %></li>
2930
<li><%= link_to "Tutorial Article", "http://www.railsonmaui.com/blog/2014/10/03/integrating-webpack-and-the-es6-transpiler-into-an-existing-rails-project/" %></li>
3031
<li><%= link_to "Forum Discussion", "http://forum.railsonmaui.com/t/fast-rich-client-rails-development-with-webpack-and-the-es6-transpiler/82/22" %></li>

app/views/pages/index.html.erb

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
<h1>Using React + Redux + Rails Backend (using the react_on_rails gem)</h1>
1+
<h2>Using React + Redux + Rails Backend (using the react_on_rails gem)</h2>
22
<ul>
33
<li>
44
If this work interests you and you're a developer or designer looking for full or part-time remote work: please

app/views/pages/simple.html.erb

+9
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
<h2>Using React (no Flux framework) + Rails Backend (with react_on_rails gem)</h2>
2+
<p>This example is much simpler than the one using React + Redux and is appropriate when:</p>
3+
<ul>
4+
<li>No or minimal MVC</li>
5+
<li>No async necessary</li>
6+
</ul>
7+
<hr/>
8+
9+
<%= react_component('SimpleCommentScreen', {}, generator_function: false, prerender: false) %>

client/app/components/CommentBox.jsx

+2-2
Original file line numberDiff line numberDiff line change
@@ -31,9 +31,9 @@ const CommentBox = React.createClass({
3131

3232
return (
3333
<div className="commentBox container">
34-
<h1>
34+
<h2>
3535
Comments { this.isSendingAjax() && `SENDING AJAX REQUEST! Ajax Counter is ${this.ajaxCounter()}` }
36-
</h1>
36+
</h2>
3737
<p>
3838
Text take Github Flavored Markdown. Comments older than 24 hours are deleted.
3939
<b>Name</b> is preserved, <b>Text</b> is reset, between submits.

client/app/components/CommentForm.jsx

+1-1
Original file line numberDiff line numberDiff line change
@@ -202,7 +202,7 @@ const CommentForm = React.createClass({
202202
if (!this.props.error) return undefined;
203203
return (
204204
<Alert bsStyle="danger" key="commentSubmissionError">
205-
<strong>Your comment was not saved!</strong>
205+
<strong>Your comment was not saved! </strong>
206206
A server error prevented your comment from being saved. Please try again.
207207
</Alert>
208208
);

client/app/components/CommentList.jsx

+1-1
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@ const CommentList = React.createClass({
1818
if (!this.props.error) return undefined;
1919
return (
2020
<Alert bsStyle="danger" key="commentFetchError">
21-
<strong>Comments could not be retrieved.</strong>
21+
<strong>Comments could not be retrieved. </strong>
2222
A server error prevented loading comments. Please try again.
2323
</Alert>
2424
);
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,80 @@
1+
import React from 'react';
2+
import Immutable from 'immutable';
3+
import request from 'axios';
4+
import CommentForm from './CommentForm';
5+
import CommentList from './CommentList';
6+
import metaTagsManager from '../utils/metaTagsManager';
7+
8+
const SimpleCommentScreen = React.createClass({
9+
displayName: 'SimpleCommentScreen',
10+
11+
getInitialState() {
12+
return {
13+
$$comments: Immutable.fromJS([]),
14+
ajaxSending: false,
15+
fetchCommentsError: null,
16+
submitCommentError: null,
17+
};
18+
},
19+
20+
componentDidMount() {
21+
this.fetchComments();
22+
},
23+
24+
fetchComments() {
25+
return request.get('comments.json', { responseType: 'json' })
26+
.then(res => this.setState({ $$comments: Immutable.fromJS(res.data) }))
27+
.catch(error => this.setState({ fetchCommentsError: error }));
28+
},
29+
30+
handleCommentSubmit(comment) {
31+
this.setState({ ajaxSending: true });
32+
33+
const requestConfig = {
34+
responseType: 'json',
35+
headers: {
36+
'X-CSRF-Token': metaTagsManager.getCSRFToken(),
37+
},
38+
};
39+
40+
return request.post('comments.json', { comment }, requestConfig)
41+
.then(() => {
42+
const { $$comments } = this.state;
43+
const $$comment = Immutable.fromJS(comment);
44+
45+
this.setState({
46+
$$comments: $$comments.push($$comment),
47+
ajaxSending: false,
48+
});
49+
})
50+
.catch(error => {
51+
this.setState({
52+
submitCommentError: error,
53+
ajaxSending: false,
54+
});
55+
});
56+
},
57+
58+
render() {
59+
return (
60+
<div className="commentBox container">
61+
<h2>Comments</h2>
62+
<p>
63+
Text take Github Flavored Markdown. Comments older than 24 hours are deleted.
64+
<b> Name</b> is preserved, <b>Text</b> is reset, between submits.
65+
</p>
66+
<CommentForm
67+
ajaxSending={this.state.ajaxSending}
68+
actions={{ submitComment: this.handleCommentSubmit }}
69+
error={this.state.submitCommentError}
70+
/>
71+
<CommentList
72+
$$comments={this.state.$$comments}
73+
error={this.state.fetchCommentsError}
74+
/>
75+
</div>
76+
);
77+
},
78+
});
79+
80+
export default SimpleCommentScreen;

client/app/startup/clientGlobals.jsx

+2
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
11
import App from './ClientApp';
2+
import SimpleCommentScreen from '../components/SimpleCommentScreen';
23

34
window.App = App;
5+
window.SimpleCommentScreen = SimpleCommentScreen;

client/app/utils/commentsManager.js

+2-18
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
import request from 'axios';
2+
import metaTagsManager from './metaTagsManager';
23

34
const API_URL = 'comments.json';
45

@@ -29,29 +30,12 @@ const CommentsManager = {
2930
url: API_URL,
3031
responseType: 'json',
3132
headers: {
32-
'X-CSRF-Token': this.getCSRFToken(),
33+
'X-CSRF-Token': metaTagsManager.getCSRFToken(),
3334
},
3435
data: { comment },
3536
});
3637
},
3738

38-
/**
39-
* Get CSRF Token from the DOM.
40-
*
41-
* @returns {String} - CSRF Token.
42-
*/
43-
getCSRFToken() {
44-
const metas = document.getElementsByTagName('meta');
45-
for (let i = 0; i < metas.length; i++) {
46-
const meta = metas[i];
47-
if (meta.getAttribute('name') === 'csrf-token') {
48-
return meta.getAttribute('content');
49-
}
50-
}
51-
52-
return null;
53-
},
54-
5539
};
5640

5741
export default CommentsManager;

client/app/utils/metaTagsManager.js

+21
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
const MetaTagsManager = {
2+
3+
/**
4+
* Get CSRF Token from the DOM.
5+
*
6+
* @returns {String} - CSRF Token.
7+
*/
8+
getCSRFToken() {
9+
const metas = document.getElementsByTagName('meta');
10+
for (let i = 0; i < metas.length; i++) {
11+
const meta = metas[i];
12+
if (meta.getAttribute('name') === 'csrf-token') {
13+
return meta.getAttribute('content');
14+
}
15+
}
16+
17+
return null;
18+
},
19+
};
20+
21+
export default MetaTagsManager;

config/routes.rb

+1
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
Rails.application.routes.draw do
22
root "pages#index"
3+
get "simple", to: "pages#simple"
34

45
resources :comments
56
end

0 commit comments

Comments
 (0)