Skip to content

Commit 041db2e

Browse files
committed
add zip-codes example (using streams with async tasks)
1 parent 70c14e6 commit 041db2e

File tree

5 files changed

+8792
-0
lines changed

5 files changed

+8792
-0
lines changed

examples/zip-codes/index.html

+13
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
<!DOCTYPE html>
2+
<html>
3+
<head>
4+
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
5+
<title>Zip Codes</title>
6+
<script type="text/javascript" src="js/build.js"></script>
7+
<style>
8+
</style>
9+
</head>
10+
<body>
11+
<div id="container"></div>
12+
</body>
13+
</html>

examples/zip-codes/js/app.js

+119
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,119 @@
1+
/* jshint esnext: true */
2+
/* globals require, window, Request */
3+
'use strict';
4+
5+
/* This is a translation of Elm's zip-codes example, designed to show how
6+
to use asyncronous tasks, specifically http requests. ES6 Promises are used
7+
here as stand-ins for Elm's Tasks.
8+
9+
Original: http://elm-lang.org/examples/zip-codes
10+
*/
11+
12+
const R = require('ramda');
13+
const flyd = require('flyd');
14+
const stream = flyd.stream;
15+
const Type = require('union-type');
16+
const patch = require('snabbdom').init([
17+
require('snabbdom/modules/class'),
18+
require('snabbdom/modules/style'),
19+
require('snabbdom/modules/props'),
20+
require('snabbdom/modules/eventlisteners'),
21+
]);
22+
const h = require('snabbdom/h');
23+
24+
25+
// view
26+
27+
const view = (model, result) => {
28+
29+
const field = h('input', {
30+
props: { placeholder: 'Zip Code', value: model },
31+
style: myStyle,
32+
on: { input: R.pipe( targetValue, query$) }
33+
},
34+
[]
35+
)
36+
37+
const messages = Result.case({
38+
Ok: R.map((city) => h('div', {style: myStyle}, city)),
39+
Err: (msg) => [ h('div', { style: myStyle }, msg) ]
40+
}, result);
41+
42+
return h('div', {}, R.concat([field], messages));
43+
}
44+
45+
const myStyle =
46+
{ "width": "100%"
47+
, "height": "40px"
48+
, "padding": "10px 0"
49+
, "font-size": "2em"
50+
, "text-align": "center"
51+
};
52+
53+
// wiring
54+
55+
const Result = Type({
56+
Ok: [Array],
57+
Err: [String]
58+
})
59+
60+
const query$ = stream("");
61+
const result$ = stream(Result.Err("A valid US zip code is 5 numbers."));
62+
63+
const vnode$ = map2( view, query$, result$ );
64+
65+
const request$ = flyd.map(
66+
R.pipe( lookupZipCode, result$ ),
67+
query$
68+
)
69+
70+
module.exports = function start(container){
71+
flyd.scan( patch, container, vnode$ )
72+
}
73+
74+
75+
function lookupZipCode(query){
76+
const toRequest = (success,fail) => {
77+
if (query.match(/^\d{5}$/)) {
78+
success( new Request("http://api.zippopotam.us/us/" + query, {method: 'GET'}) );
79+
} else {
80+
fail( "Give me a valid US zip code!" );
81+
}
82+
}
83+
84+
return new Promise(toRequest)
85+
.then( window.fetch )
86+
.then( throwHttpErrorAnd ) // 404 errors are not caught by fetch, strange
87+
.then( R.invoker(0,'text') )
88+
.then( places )
89+
.then( Result.Ok )
90+
.catch( debugAnd( R.compose( Result.Err, R.always('Not found :(')) ) )
91+
}
92+
93+
function places(text){
94+
const place = (obj) => obj['place name'] + ', ' + obj['state']
95+
return JSON.parse(text)['places'].map(place)
96+
}
97+
98+
99+
// utils
100+
101+
function targetValue(e){ return e.target.value; }
102+
103+
function throwHttpErrorAnd(response){
104+
if (!response.ok) throw Error("" + response.status + " " + response.statusText);
105+
return response;
106+
}
107+
108+
function debugAnd(fn){
109+
return function(err){
110+
console.debug('error: %o', err);
111+
return fn(err);
112+
}
113+
}
114+
115+
function map2(fn, s1, s2){
116+
return flyd.stream([s1,s2], function(){
117+
return fn(s1(), s2());
118+
})
119+
}

0 commit comments

Comments
 (0)