├── requirements.txt ├── run.sh ├── .bowerrc ├── .gitignore ├── README.md ├── project ├── app.py ├── static │ ├── css │ │ └── style.css │ └── scripts │ │ ├── jsx │ │ └── main.js │ │ └── js │ │ └── main.js └── templates │ ├── index.html │ └── hello.html ├── bower.json ├── gulpfile.js └── package.json /requirements.txt: -------------------------------------------------------------------------------- 1 | Flask==0.10.1 2 | -------------------------------------------------------------------------------- /run.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | python project/app.py -------------------------------------------------------------------------------- /.bowerrc: -------------------------------------------------------------------------------- 1 | { 2 | "directory": "./project/static/bower_components" 3 | } -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | *.DS_Store 2 | *.pyc 3 | .git 4 | 5 | env 6 | venv 7 | 8 | node_modules/ 9 | npm-debug.log 10 | bower_components/ 11 | .module-cache -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | Be sure to check out the blog posts- 2 | 3 | 1. https://realpython.com/blog/python/the-ultimate-flask-front-end/ 4 | 1. https://realpython.com/blog/python/the-ultimate-flask-front-end-part-2/ -------------------------------------------------------------------------------- /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 | 10 | input { 11 | text-align: center; 12 | border: 5px solid; 13 | padding: 13px 12px; 14 | font-size: 16px; 15 | outline: none; 16 | margin-bottom: 20px; 17 | } 18 | 19 | 20 | ul { 21 | margin-left: 20px; 22 | text-align: left; 23 | } -------------------------------------------------------------------------------- /bower.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "ultimate-flask-front-end", 3 | "homepage": "https://github.com/realpython/ultimate-flask-front-end", 4 | "authors": [ 5 | "Michael Herman michael@realpython.com" 6 | ], 7 | "description": "", 8 | "main": "", 9 | "license": "MIT", 10 | "ignore": [ 11 | "**/.*", 12 | "node_modules", 13 | "bower_components", 14 | "test", 15 | "tests" 16 | ], 17 | "dependencies": { 18 | "bootstrap": "^3.3.6", 19 | "react": "^15.1.0" 20 | } 21 | } -------------------------------------------------------------------------------- /gulpfile.js: -------------------------------------------------------------------------------- 1 | // requirements 2 | 3 | var gulp = require('gulp'); 4 | var gulpBrowser = require("gulp-browser"); 5 | var reactify = require('reactify'); 6 | var del = require('del'); 7 | var size = require('gulp-size'); 8 | 9 | 10 | // tasks 11 | 12 | gulp.task('transform', function () { 13 | var stream = gulp.src('./project/static/scripts/jsx/*.js') 14 | .pipe(gulpBrowser.browserify({transform: ['reactify']})) 15 | .pipe(gulp.dest('./project/static/scripts/js/')) 16 | .pipe(size()); 17 | return stream; 18 | }); 19 | 20 | gulp.task('del', function () { 21 | return del(['./project/static/scripts/js']); 22 | }); 23 | 24 | gulp.task('default', ['del'], function () { 25 | gulp.start('transform'); 26 | gulp.watch('./project/static/scripts/jsx/*.js', ['transform']); 27 | }); -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "ultimate-flask-front-end", 3 | "version": "1.0.0", 4 | "description": "", 5 | "main": "index.js", 6 | "scripts": { 7 | "test": "echo \"Error: no test specified\" && exit 1" 8 | }, 9 | "repository": { 10 | "type": "git", 11 | "url": "git+https://github.com/realpython/ultimate-flask-front-end.git" 12 | }, 13 | "author": "", 14 | "license": "ISC", 15 | "bugs": { 16 | "url": "https://github.com/realpython/ultimate-flask-front-end/issues" 17 | }, 18 | "homepage": "https://github.com/realpython/ultimate-flask-front-end#readme", 19 | "devDependencies": { 20 | "bower": "^1.7.9", 21 | "del": "^2.2.0", 22 | "gulp": "^3.9.1", 23 | "gulp-browser": "^2.1.4", 24 | "gulp-size": "^2.1.0", 25 | "reactify": "^1.1.1" 26 | } 27 | } -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /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 | 19 | 35 | 36 | -------------------------------------------------------------------------------- /project/static/scripts/jsx/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 | 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 | ReactDOM.render( 51 | , 52 | document.getElementById('main') 53 | ); -------------------------------------------------------------------------------- /project/static/scripts/js/main.js: -------------------------------------------------------------------------------- 1 | (function e(t,n,r){function s(o,u){if(!n[o]){if(!t[o]){var a=typeof require=="function"&&require;if(!u&&a)return a(o,!0);if(i)return i(o,!0);var f=new Error("Cannot find module '"+o+"'");throw f.code="MODULE_NOT_FOUND",f}var l=n[o]={exports:{}};t[o][0].call(l.exports,function(e){var n=t[o][1][e];return s(n?n:e)},l,l.exports,e,t,n,r)}return n[o].exports}var i=typeof require=="function"&&require;for(var o=0;o 0){ 23 | countries = countries.filter(function(country){ 24 | return country.name.toLowerCase().match( searchString ); 25 | }); 26 | } 27 | 28 | return ( 29 | React.createElement("div", null, 30 | React.createElement("input", {type: "text", value: this.state.searchString, onChange: this.handleChange, placeholder: "Search!"}), 31 | React.createElement("ul", null, 32 | countries.map(function(country){ return React.createElement("li", null, country.name, " ") }) 33 | ) 34 | ) 35 | ) 36 | } 37 | 38 | }); 39 | 40 | // list of countries, defined with JavaScript object literals 41 | var countries = [ 42 | {"name": "Sweden"}, {"name": "China"}, {"name": "Peru"}, {"name": "Czech Republic"}, 43 | {"name": "Bolivia"}, {"name": "Latvia"}, {"name": "Samoa"}, {"name": "Armenia"}, 44 | {"name": "Greenland"}, {"name": "Cuba"}, {"name": "Western Sahara"}, {"name": "Ethiopia"}, 45 | {"name": "Malaysia"}, {"name": "Argentina"}, {"name": "Uganda"}, {"name": "Chile"}, 46 | {"name": "Aruba"}, {"name": "Japan"}, {"name": "Trinidad and Tobago"}, {"name": "Italy"}, 47 | {"name": "Cambodia"}, {"name": "Iceland"}, {"name": "Dominican Republic"}, {"name": "Turkey"}, 48 | {"name": "Spain"}, {"name": "Poland"}, {"name": "Haiti"} 49 | ]; 50 | 51 | ReactDOM.render( 52 | React.createElement(DynamicSearch, {items: countries }), 53 | document.getElementById('main') 54 | ); 55 | 56 | },{}]},{},[1]); --------------------------------------------------------------------------------