Skip to content

Commit

Permalink
Merge pull request #2699 from pallets/javascript
Browse files Browse the repository at this point in the history
add javascript ajax example
  • Loading branch information
davidism committed Apr 12, 2018
2 parents d8bf589 + fce1885 commit 746b91d
Show file tree
Hide file tree
Showing 21 changed files with 341 additions and 13 deletions.
5 changes: 3 additions & 2 deletions docs/patterns/jquery.rst
Original file line number Diff line number Diff line change
Expand Up @@ -162,5 +162,6 @@ explanation of the little bit of code above:
argument. Note that we can use the `$SCRIPT_ROOT` variable here that
we set earlier.

If you don't get the whole picture, download the :gh:`sourcecode
for this example <examples/jqueryexample>`.
Check out the :gh:`example source <examples/javascript>` for a full
application demonstrating the jQuery on this page, as well as the same
thing using ``XMLHttpRequest`` and ``fetch``.
6 changes: 0 additions & 6 deletions docs/testing.rst
Original file line number Diff line number Diff line change
Expand Up @@ -209,12 +209,6 @@ Running that should now give us three passing tests::

============= 3 passed in 0.23 seconds ==============

For more complex tests with headers and status codes, check out the
`MiniTwit Example`_ from the sources which contains a larger test
suite.

.. _MiniTwit Example:
https://github.com/pallets/flask/tree/master/examples/minitwit/

Other Testing Tricks
--------------------
Expand Down
14 changes: 14 additions & 0 deletions examples/javascript/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
venv/
*.pyc
__pycache__/
instance/
.cache/
.pytest_cache/
.coverage
htmlcov/
dist/
build/
*.egg-info/
.idea/
*.swp
*~
31 changes: 31 additions & 0 deletions examples/javascript/LICENSE
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
Copyright © 2010 by the Pallets team.

Some rights reserved.

Redistribution and use in source and binary forms of the software as
well as documentation, with or without modification, are permitted
provided that the following conditions are met:

* Redistributions of source code must retain the above copyright notice,
this list of conditions and the following disclaimer.

* Redistributions in binary form must reproduce the above copyright
notice, this list of conditions and the following disclaimer in the
documentation and/or other materials provided with the distribution.

* Neither the name of the copyright holder nor the names of its
contributors may be used to endorse or promote products derived from
this software without specific prior written permission.

THIS SOFTWARE AND DOCUMENTATION IS PROVIDED BY THE COPYRIGHT HOLDERS AND
CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING,
BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
THIS SOFTWARE AND DOCUMENTATION, EVEN IF ADVISED OF THE POSSIBILITY OF
SUCH DAMAGE.
4 changes: 4 additions & 0 deletions examples/javascript/MANIFEST.in
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
include LICENSE
graft js_example/templates
graft tests
global-exclude *.pyc
49 changes: 49 additions & 0 deletions examples/javascript/README.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
JavaScript Ajax Example
=======================

Demonstrates how to post form data and process a JSON response using
JavaScript. This allows making requests without navigating away from the
page. Demonstrates using |XMLHttpRequest|_, |fetch|_, and
|jQuery.ajax|_. See the `Flask docs`_ about jQuery and Ajax.

.. |XMLHttpRequest| replace:: ``XMLHttpRequest``
.. _XMLHttpRequest: https://developer.mozilla.org/en-US/docs/Web/API/XMLHttpRequest

.. |fetch| replace:: ``fetch``
.. _fetch: https://developer.mozilla.org/en-US/docs/Web/API/WindowOrWorkerGlobalScope/fetch

.. |jQuery.ajax| replace:: ``jQuery.ajax``
.. _jQuery.ajax: https://api.jquery.com/jQuery.ajax/

.. _Flask docs: http://flask.pocoo.org/docs/patterns/jquery/


Install
-------

::

python3 -m venv venv
. venv/bin/activate
pip install -e .


Run
---

::

export FLASK_APP=js_example
flask run

Open http://127.0.0.1:5000 in a browser.


Test
----

::

pip install -e '.[test]'
coverage run -m pytest
coverage report
5 changes: 5 additions & 0 deletions examples/javascript/js_example/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
from flask import Flask

app = Flask(__name__)

from js_example import views
33 changes: 33 additions & 0 deletions examples/javascript/js_example/templates/base.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
<!doctype html>
<title>JavaScript Example</title>
<link rel="stylesheet" href="https://unpkg.com/sakura.css@1.0.0/css/normalize.css">
<link rel="stylesheet" href="https://unpkg.com/sakura.css@1.0.0/css/sakura-earthly.css">
<style>
ul { margin: 0; padding: 0; display: flex; list-style-type: none; }
li > * { padding: 1em; }
li.active > a { color: #5e5e5e; border-bottom: 2px solid #4a4a4a; }
form { display: flex; }
label > input { width: 3em; }
form > * { padding-right: 1em; }
#result { font-weight: bold; }
</style>
<ul>
<li><span>Type:</span>
<li class="{% if js == 'plain' %}active{% endif %}">
<a href="{{ url_for('index', js='plain') }}">Plain</a>
<li class="{% if js == 'fetch' %}active{% endif %}">
<a href="{{ url_for('index', js='fetch') }}">Fetch</a>
<li class="{% if js == 'jquery' %}active{% endif %}">
<a href="{{ url_for('index', js='jquery') }}">jQuery</a>
</ul>
<hr>
<p>{% block intro %}{% endblock %}</p>
<hr>
<form>
<label>a <input name="a"></label>
<span>+</span>
<label>b <input name="b"></label>
<input type="submit" value="Calculate">
</form>
<span>= <span id="result"></span></span>
{% block script %}{% endblock %}
35 changes: 35 additions & 0 deletions examples/javascript/js_example/templates/fetch.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
{% extends 'base.html' %}

{% block intro %}
<a href="https://developer.mozilla.org/en-US/docs/Web/API/WindowOrWorkerGlobalScope/fetch"><code>fetch</code></a>
is the <em>new</em> plain JavaScript way to make requests. It's
supported in all modern browsers except IE, which requires a
<a href="https://github.com/github/fetch">polyfill</a>.
{% endblock %}

{% block script %}
<script src="https://unpkg.com/promise-polyfill@7.1.2/dist/polyfill.min.js"></script>
<script src="https://unpkg.com/whatwg-fetch@2.0.4/fetch.js"></script>
<script>
function addSubmit(ev) {
ev.preventDefault();
fetch('{{ url_for('add') }}', {
method: 'POST',
body: new FormData(this)
})
.then(parseJSON)
.then(addShow);
}

function parseJSON(response) {
return response.json();
}

function addShow(data) {
var span = document.getElementById('result');
span.innerText = data.result;
}

document.forms[0].addEventListener('submit', addSubmit);
</script>
{% endblock %}
27 changes: 27 additions & 0 deletions examples/javascript/js_example/templates/jquery.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
{% extends 'base.html' %}

{% block intro %}
<a href="https://jquery.com/">jQuery</a> is a popular library that
adds cross browser APIs for common tasks. However, it requires loading
an extra library.
{% endblock %}

{% block script %}
<script src="https://code.jquery.com/jquery-3.3.1.min.js"></script>
<script>
function addSubmit(ev) {
ev.preventDefault();
$.ajax({
method: 'POST',
url: '{{ url_for('add') }}',
data: $(this).serialize()
}).done(addShow);
}

function addShow(data) {
$('#result').text(data.result);
}

$('form:first').on('submit', addSubmit);
</script>
{% endblock %}
27 changes: 27 additions & 0 deletions examples/javascript/js_example/templates/plain.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
{% extends 'base.html' %}

{% block intro %}
<a href="https://developer.mozilla.org/en-US/docs/Web/API/XMLHttpRequest"><code>XMLHttpRequest</code></a>
is the plain JavaScript way to make requests. It's natively supported
by all browsers.
{% endblock %}

{% block script %}
<script>
function addSubmit(ev) {
ev.preventDefault();
var request = new XMLHttpRequest();
request.addEventListener('load', addShow);
request.open('POST', '{{ url_for('add') }}');
request.send(new FormData(this));
}

function addShow() {
var data = JSON.parse(this.responseText);
var span = document.getElementById('result');
span.innerText = data.result;
}

document.forms[0].addEventListener('submit', addSubmit);
</script>
{% endblock %}
16 changes: 16 additions & 0 deletions examples/javascript/js_example/views.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
from flask import jsonify, render_template, request

from js_example import app


@app.route('/', defaults={'js': 'plain'})
@app.route('/<any(plain, jquery, fetch):js>')
def index(js):
return render_template('{0}.html'.format(js), js=js)


@app.route('/add', methods=['POST'])
def add():
a = request.form.get('a', 0, type=float)
b = request.form.get('b', 0, type=float)
return jsonify(result=a + b)
13 changes: 13 additions & 0 deletions examples/javascript/setup.cfg
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
[metadata]
license_file = LICENSE

[bdist_wheel]
universal = True

[tool:pytest]
testpaths = tests

[coverage:run]
branch = True
source =
js_example
30 changes: 30 additions & 0 deletions examples/javascript/setup.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
import io

from setuptools import find_packages, setup

with io.open('README.rst', 'rt', encoding='utf8') as f:
readme = f.read()

setup(
name='js_example',
version='1.0.0',
url='http://flask.pocoo.org/docs/patterns/jquery/',
license='BSD',
maintainer='Pallets team',
maintainer_email='contact@palletsprojects.com',
description='Demonstrates making Ajax requests to Flask.',
long_description=readme,
packages=find_packages(),
include_package_data=True,
zip_safe=False,
install_requires=[
'flask',
],
extras_require={
'test': [
'pytest',
'coverage',
'blinker',
],
},
)
15 changes: 15 additions & 0 deletions examples/javascript/tests/conftest.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
import pytest

from js_example import app


@pytest.fixture(name='app')
def fixture_app():
app.testing = True
yield app
app.testing = False


@pytest.fixture
def client(app):
return app.test_client()
28 changes: 28 additions & 0 deletions examples/javascript/tests/test_js_example.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
import pytest

from flask import template_rendered


@pytest.mark.parametrize(('path', 'template_name'), (
('/', 'plain.html'),
('/plain', 'plain.html'),
('/fetch', 'fetch.html'),
('/jquery', 'jquery.html'),
))
def test_index(app, client, path, template_name):
def check(sender, template, context):
assert template.name == template_name

with template_rendered.connected_to(check, app):
client.get(path)


@pytest.mark.parametrize(('a', 'b', 'result'), (
(2, 3, 5),
(2.5, 3, 5.5),
(2, None, 2),
(2, 'b', 2),
))
def test_add(client, a, b, result):
response = client.post('/add', data={'a': a, 'b': b})
assert response.get_json()['result'] == result
3 changes: 1 addition & 2 deletions examples/tutorial/README.rst
Original file line number Diff line number Diff line change
Expand Up @@ -65,12 +65,11 @@ Test

::

pip install pytest
pip install '.[test]'
pytest

Run with coverage report::

pip install pytest coverage
coverage run -m pytest
coverage report
coverage html # open htmlcov/index.html in a browser
2 changes: 1 addition & 1 deletion examples/tutorial/flaskr/auth.py
Original file line number Diff line number Diff line change
Expand Up @@ -56,7 +56,7 @@ def register():
elif db.execute(
'SELECT id FROM user WHERE username = ?', (username,)
).fetchone() is not None:
error = 'User {} is already registered.'.format(username)
error = 'User {0} is already registered.'.format(username)

if error is None:
# the name is available, store it in the database and go to
Expand Down
2 changes: 1 addition & 1 deletion examples/tutorial/setup.cfg
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
license_file = LICENSE

[bdist_wheel]
universal = False
universal = True

[tool:pytest]
testpaths = tests
Expand Down

0 comments on commit 746b91d

Please sign in to comment.