├── run.sh ├── .bowerrc ├── .gitignore ├── requirements.txt ├── project ├── app.py ├── static │ ├── css │ │ └── style.css │ └── js │ │ └── src │ │ └── main.js └── templates │ ├── hello.html │ └── index.html ├── bower.json ├── package.json ├── gulpfile.js └── README.md /run.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | python project/app.py -------------------------------------------------------------------------------- /.bowerrc: -------------------------------------------------------------------------------- 1 | { 2 | "directory": "./project/static/bower_components" 3 | } 4 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | node_modules 3 | bower_components 4 | env 5 | project/static/js/dist 6 | -------------------------------------------------------------------------------- /requirements.txt: -------------------------------------------------------------------------------- 1 | Flask==0.10.1 2 | Jinja2==2.7.3 3 | MarkupSafe==0.23 4 | Werkzeug==0.9.6 5 | itsdangerous==0.24 6 | wsgiref==0.1.2 7 | -------------------------------------------------------------------------------- /project/app.py: -------------------------------------------------------------------------------- 1 | from flask import Flask, render_template 2 | 3 | app = Flask(__name__) 4 | 5 | 6 | @app.route('/') 7 | def index(): 8 | return render_template('index.html') 9 | 10 | 11 | @app.route('/hello') 12 | def hello(): 13 | return render_template('hello.html') 14 | 15 | 16 | if __name__ == '__main__': 17 | app.run(debug=True) 18 | -------------------------------------------------------------------------------- /project/static/css/style.css: -------------------------------------------------------------------------------- 1 | /* custom styles */ 2 | 3 | .container { 4 | text-align: center; 5 | padding-top: 50px; 6 | max-width: 300px; 7 | } 8 | 9 | input{ 10 | text-align: center; 11 | border: 5px solid; 12 | padding: 13px 12px; 13 | font-size: 16px; 14 | outline: none; 15 | margin-bottom: 20px; 16 | } 17 | 18 | ul{ 19 | margin-left: 20px; 20 | text-align: left; 21 | }/* custom styles */ 22 | -------------------------------------------------------------------------------- /bower.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "reactive-flask", 3 | "version": "0.0.0", 4 | "authors": [ 5 | "Bhargav Patel " 6 | ], 7 | "license": "MIT", 8 | "ignore": [ 9 | "**/.*", 10 | "node_modules", 11 | "bower_components", 12 | "test", 13 | "tests" 14 | ], 15 | "dependencies": { 16 | "bootstrap": "~3.3.2", 17 | "jquery": "~2.1.3", 18 | "react": "~0.12.2" 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "reactive-flask", 3 | "version": "1.0.0", 4 | "description": "", 5 | "main": "index.js", 6 | "scripts": { 7 | "test": "echo \"Error: no test specified\" && exit 1" 8 | }, 9 | "author": "Bhargav R. Patel (http://github.com/bhargavrpatel)", 10 | "license": "ISC", 11 | "devDependencies": { 12 | "bower": "^1.4.1", 13 | "gulp": "^3.9.0", 14 | "gulp-browserify": "^0.5.1", 15 | "gulp-clean": "^0.3.1", 16 | "gulp-size": "^1.2.2", 17 | "react": "^0.13.3", 18 | "reactify": "^1.1.1" 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /gulpfile.js: -------------------------------------------------------------------------------- 1 | var gulp = require('gulp'), 2 | browserify = require('gulp-browserify'); 3 | 4 | gulp.task('clean', function () { 5 | return gulp.src(['./project/static/scripts/js'], {read: false}) 6 | .pipe(require('gulp-clean')()); 7 | }); 8 | 9 | 10 | gulp.task('transform', function () { 11 | return gulp.src('./project/static/js/src/main.js') 12 | .pipe(browserify({transform: ['reactify']})) 13 | .pipe(gulp.dest('./project/static/js/dist/')) 14 | .pipe(require('gulp-size')()); 15 | }); 16 | 17 | 18 | gulp.task('default', ['clean'], function () { 19 | console.log("Hello, World!"); 20 | gulp.start('transform'); 21 | }); 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # React-Flask 2 | Following a tutorial, I created a basic AJAXy filter component that searches and changes the DOM/view live. 3 | 4 | The backend is boilerplate flask application that renders template. Here however, we serve most of the page using React. This has some interesting implications. I am planning to run this in future projects. 5 | 6 | ## Running 7 | 1. Clone the repo 8 | 2. Create virtual env and source it using `source env/bin/activate` (OSX. 9 | 3. Run `[sudo] pip install -r requirements.txt` or 'pip install flask' 10 | 4. Run `npm install` 11 | 5. Run `bower install` 12 | 6. Run `python project/app.py` or `sh run.sh` to run the application 13 | 7. Visit `localhost:5000` to see the application 14 | -------------------------------------------------------------------------------- /project/templates/hello.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Flask React 6 | 7 | 8 | 9 | 10 |
11 |

Flask React

12 |
13 |
14 | 15 | 16 | 17 | 18 | 33 | 34 | 35 | 36 | 37 | -------------------------------------------------------------------------------- /project/templates/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Flask React 6 | 7 | 8 | 9 | 10 | 11 | 12 |
13 |

Flask React

14 |
15 |
16 |
17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | -------------------------------------------------------------------------------- /project/static/js/src/main.js: -------------------------------------------------------------------------------- 1 | var DynamicSearch = React.createClass({ 2 | 3 | // sets initial state 4 | getInitialState: function(){ 5 | return { searchString: '' }; 6 | }, 7 | 8 | // sets state, triggers render method 9 | handleChange: function(event){ 10 | // grab value form input box 11 | this.setState({searchString:event.target.value}); 12 | console.log("scope updated!") 13 | }, 14 | 15 | render: function() { 16 | 17 | var countries = this.props.items; 18 | var searchString = this.state.searchString.trim().toLowerCase(); 19 | 20 | // filter countries list by value from input box 21 | if(searchString.length > 0){ 22 | countries = countries.filter(function(country){ 23 | return country.name.toLowerCase().match( searchString ); 24 | }); 25 | } 26 | 27 | return ( 28 |
29 | 30 |
    31 | { countries.map(function(country){ return
  • {country.name}
  • }) } 32 |
33 |
34 | ) 35 | } 36 | 37 | }); 38 | 39 | // list of countries, defined with JavaScript object literals 40 | var countries = [ 41 | {"name": "Sweden"}, {"name": "China"}, {"name": "Peru"}, {"name": "Czech Republic"}, 42 | {"name": "Bolivia"}, {"name": "Latvia"}, {"name": "Samoa"}, {"name": "Armenia"}, 43 | {"name": "Greenland"}, {"name": "Cuba"}, {"name": "Western Sahara"}, {"name": "Ethiopia"}, 44 | {"name": "Malaysia"}, {"name": "Argentina"}, {"name": "Uganda"}, {"name": "Chile"}, 45 | {"name": "Aruba"}, {"name": "Japan"}, {"name": "Trinidad and Tobago"}, {"name": "Italy"}, 46 | {"name": "Cambodia"}, {"name": "Iceland"}, {"name": "Dominican Republic"}, {"name": "Turkey"}, 47 | {"name": "Spain"}, {"name": "Poland"}, {"name": "Haiti"} 48 | ]; 49 | 50 | React.render( 51 | , 52 | document.getElementById('main') 53 | ); 54 | --------------------------------------------------------------------------------