summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorLawouach <sh@defuze.org>2015-07-19 21:02:23 +0200
committerLawouach <sh@defuze.org>2015-07-19 21:02:23 +0200
commit12cfcf78ce7f7d497e40a379e96083417c3d7858 (patch)
treefc8f265416eddc9be2f716eb76c2219f6cc2012b
parent8320a44650586e9d3248cfce63d48b2c49b26cd7 (diff)
downloadcherrypy-12cfcf78ce7f7d497e40a379e96083417c3d7858.tar.gz
added React.js to the tutorial
-rw-r--r--docs/tutorials.rst223
1 files changed, 222 insertions, 1 deletions
diff --git a/docs/tutorials.rst b/docs/tutorials.rst
index 21318f23..68f11f35 100644
--- a/docs/tutorials.rst
+++ b/docs/tutorials.rst
@@ -726,6 +726,7 @@ Notice as well how your frontend converses with the backend using
a straightfoward, yet clean, web service API. That same API
could easily be used by non-HTML clients.
+.. _tut09:
Tutorial 9: Data is all my life
###############################
@@ -874,8 +875,228 @@ demo, this should do.
`SQLAlchemy <http://sqlalchemy.readthedocs.org>`_, to better
support your application's needs.
+.. _tut10:
+
+Tutorial 10: Make it a modern single-page application with React.js
+###################################################################
+
+In the recent years, client-side single-page applications (SPA) have
+gradually eaten server-side generated content web applications's lunch.
+
+This tutorial demonstrates how to integrate with
+`React.js <https://facebook.github.io/react/>`_, a Javascript library
+for SPA released by Facebook in 2013. Please refer to React.js
+documentation to learn more about it.
+
+To demonstrate it, let's use the code from :ref:`tutorial 09 <tut09>`.
+However, we will be replacing the HTML and Javascript code.
+
+First, let's see how our HTML code has changed:
+
+.. code-block:: html
+ :linenos:
+
+ <!DOCTYPE html>
+ <html>
+ <head>
+ <link href="/static/css/style.css" rel="stylesheet">
+ <script src="https://cdnjs.cloudflare.com/ajax/libs/react/0.13.3/react.js"></script>
+ <script src="http://code.jquery.com/jquery-2.1.1.min.js"></script>
+ </head>
+ <body>
+ <div id="generator"></div>
+ <script type="text/javascript" src="static/js/gen.js"></script>
+ </body>
+ </html>
+
+Basically, we have remove the entire Javascript code that was using jQuery.
+Instead, we load the React.js library as well as a new, local,
+Javascript module, named ``gen.js`` and located in the ``public/js``
+directory:
+
+.. code-block:: javascript
+ :linenos:
+
+ var StringGeneratorBox = React.createClass({
+ handleGenerate: function() {
+ var length = this.state.length;
+ this.setState(function() {
+ $.ajax({
+ url: this.props.url,
+ dataType: 'text',
+ type: 'POST',
+ data: {
+ "length": length
+ },
+ success: function(data) {
+ this.setState({
+ length: length,
+ string: data,
+ mode: "edit"
+ });
+ }.bind(this),
+ error: function(xhr, status, err) {
+ console.error(this.props.url,
+ status, err.toString()
+ );
+ }.bind(this)
+ });
+ });
+ },
+ handleEdit: function() {
+ var new_string = this.state.string;
+ this.setState(function() {
+ $.ajax({
+ url: this.props.url,
+ type: 'PUT',
+ data: {
+ "another_string": new_string
+ },
+ success: function() {
+ this.setState({
+ length: new_string.length,
+ string: new_string,
+ mode: "edit"
+ });
+ }.bind(this),
+ error: function(xhr, status, err) {
+ console.error(this.props.url,
+ status, err.toString()
+ );
+ }.bind(this)
+ });
+ });
+ },
+ handleDelete: function() {
+ this.setState(function() {
+ $.ajax({
+ url: this.props.url,
+ type: 'DELETE',
+ success: function() {
+ this.setState({
+ length: "8",
+ string: "",
+ mode: "create"
+ });
+ }.bind(this),
+ error: function(xhr, status, err) {
+ console.error(this.props.url,
+ status, err.toString()
+ );
+ }.bind(this)
+ });
+ });
+ },
+ handleLengthChange: function(length) {
+ this.setState({
+ length: length,
+ string: "",
+ mode: "create"
+ });
+ },
+ handleStringChange: function(new_string) {
+ this.setState({
+ length: new_string.length,
+ string: new_string,
+ mode: "edit"
+ });
+ },
+ getInitialState: function() {
+ return {
+ length: "8",
+ string: "",
+ mode: "create"
+ };
+ },
+ render: function() {
+ return (
+ <div className="stringGenBox">
+ <StringGeneratorForm onCreateString={this.handleGenerate}
+ onReplaceString={this.handleEdit}
+ onDeleteString={this.handleDelete}
+ onLengthChange={this.handleLengthChange}
+ onStringChange={this.handleStringChange}
+ mode={this.state.mode}
+ length={this.state.length}
+ string={this.state.string}/>
+ </div>
+ );
+ }
+ });
+
+ var StringGeneratorForm = React.createClass({
+ handleCreate: function(e) {
+ e.preventDefault();
+ this.props.onCreateString();
+ },
+ handleReplace: function(e) {
+ e.preventDefault();
+ this.props.onReplaceString();
+ },
+ handleDelete: function(e) {
+ e.preventDefault();
+ this.props.onDeleteString();
+ },
+ handleLengthChange: function(e) {
+ e.preventDefault();
+ var length = React.findDOMNode(this.refs.length).value.trim();
+ this.props.onLengthChange(length);
+ },
+ handleStringChange: function(e) {
+ e.preventDefault();
+ var string = React.findDOMNode(this.refs.string).value.trim();
+ this.props.onStringChange(string);
+ },
+ render: function() {
+ if (this.props.mode == "create") {
+ return (
+ <div>
+ <input type="text" ref="length" defaultValue="8" value={this.props.length} onChange={this.handleLengthChange} />
+ <button onClick={this.handleCreate}>Give it now!</button>
+ </div>
+ );
+ } else if (this.props.mode == "edit") {
+ return (
+ <div>
+ <input type="text" ref="string" value={this.props.string} onChange={this.handleStringChange} />
+ <button onClick={this.handleReplace}>Replace</button>
+ <button onClick={this.handleDelete}>Delete it</button>
+ </div>
+ );
+ }
+
+ return null;
+ }
+ });
+
+ React.render(
+ <StringGeneratorBox url="/generator" />,
+ document.getElementById('generator')
+ );
+
+
+Wow! What a lot of code for something so simple, isn't it?
+The entry point is the last few lines where we indicate that we
+want to render the HTML code of the ``StringGeneratorBox`` React.js
+class inside the ``generator`` div.
+
+When the page is rendered, so is that component. Notice how it
+is also made of another component that renders the form itself.
+
+This might be a little over the top for such a simple example
+but hopefully will get you started with React.js in the process.
+
+There is not much to say and, hopefully, the meaning of that code
+is rather clear. The component has an internal `state <https://facebook.github.io/react/docs/interactivity-and-dynamic-uis.html>`_
+in which we store the current string as generated/modified by the user.
+
+When the user `changes the content of the input boxes <https://facebook.github.io/react/docs/forms.html>`_,
+the state is updated on the client side. Then, when a button is clicked,
+that state is sent out to the backend server using the API endpoint
+and the appropriate action takes places. Then, the state is updated and so is the view.
+
-Tutorial 10: Organize my code
+Tutorial 11: Organize my code
#############################
CherryPy comes with a powerful architecture