Suche senden
Hochladen
FRP in Practice
•
6 gefällt mir
•
1,600 views
rf0444
Folgen
Melden
Teilen
Melden
Teilen
1 von 57
Jetzt herunterladen
Downloaden Sie, um offline zu lesen
Empfohlen
Sencha Touchをさわってみた
Sencha Touchをさわってみた
Tomonori Ohba
20150530 pgunconf-pgbench-semi-structured-benchmark
20150530 pgunconf-pgbench-semi-structured-benchmark
Toshi Harada
SWF
SWF
rf0444
モナドをつくろう
モナドをつくろう
dico_leque
速くなければスマフォじゃない - インターンバージョン-
速くなければスマフォじゃない - インターンバージョン-
Kazunari Hara
Composable Callbacks & Listeners
Composable Callbacks & Listeners
Taisuke Oe
20141004 詰Excel
20141004 詰Excel
ta2c
Design mvc apps with spotify web api object model
Design mvc apps with spotify web api object model
Takao Tetsuro
Empfohlen
Sencha Touchをさわってみた
Sencha Touchをさわってみた
Tomonori Ohba
20150530 pgunconf-pgbench-semi-structured-benchmark
20150530 pgunconf-pgbench-semi-structured-benchmark
Toshi Harada
SWF
SWF
rf0444
モナドをつくろう
モナドをつくろう
dico_leque
速くなければスマフォじゃない - インターンバージョン-
速くなければスマフォじゃない - インターンバージョン-
Kazunari Hara
Composable Callbacks & Listeners
Composable Callbacks & Listeners
Taisuke Oe
20141004 詰Excel
20141004 詰Excel
ta2c
Design mvc apps with spotify web api object model
Design mvc apps with spotify web api object model
Takao Tetsuro
Start FRP
Start FRP
rf0444
PFDS 11.2.2
PFDS 11.2.2
rf0444
PFDS 10.1.2
PFDS 10.1.2
rf0444
PFDS 9.3.2
PFDS 9.3.2
rf0444
PFDS 9.3.1
PFDS 9.3.1
rf0444
PFDS 8.4.1
PFDS 8.4.1
rf0444
PFDS 7.4
PFDS 7.4
rf0444
Tapl 5
Tapl 5
rf0444
Haskellday rf
Haskellday rf
rf0444
PFDS 6.4.3
PFDS 6.4.3
rf0444
Weitere ähnliche Inhalte
Mehr von rf0444
Start FRP
Start FRP
rf0444
PFDS 11.2.2
PFDS 11.2.2
rf0444
PFDS 10.1.2
PFDS 10.1.2
rf0444
PFDS 9.3.2
PFDS 9.3.2
rf0444
PFDS 9.3.1
PFDS 9.3.1
rf0444
PFDS 8.4.1
PFDS 8.4.1
rf0444
PFDS 7.4
PFDS 7.4
rf0444
Tapl 5
Tapl 5
rf0444
Haskellday rf
Haskellday rf
rf0444
PFDS 6.4.3
PFDS 6.4.3
rf0444
Mehr von rf0444
(10)
Start FRP
Start FRP
PFDS 11.2.2
PFDS 11.2.2
PFDS 10.1.2
PFDS 10.1.2
PFDS 9.3.2
PFDS 9.3.2
PFDS 9.3.1
PFDS 9.3.1
PFDS 8.4.1
PFDS 8.4.1
PFDS 7.4
PFDS 7.4
Tapl 5
Tapl 5
Haskellday rf
Haskellday rf
PFDS 6.4.3
PFDS 6.4.3
FRP in Practice
1.
Functional Reactive Programming 実践編 ∼
画面作成、リクエスト処理 ∼ @rf0444
2.
利用ライブラリ • Bacon.js • https://github.com/raimohanska/bacon.js •
jQuery
3.
おしながき • EventStream と
Property • 画面を作る • リクエスト処理
4.
EventStream と Property
5.
EventStream • 発生するイベントの列 を表す •
クリックされた、など 時間 値
6.
Property • 時間によって変化する値 を表す •
View に表示する値、最後に返ってきたレスポンス など 時間 値
7.
EventStream / Property •
EventStream#merge(EventStream) • 2つの EventStream をくっつけた EventStream を作る。 時間 値 時間 値 時間 値 e1 e2 e1.merge(e2)
8.
EventStream / Property •
EventStream#toProperty([initVal]) • EventStream に 値が流れてくる タイミングで、値が変化する Property を作る。 • 引数に初期値を指定できる。 (なしも可) 時間 値 時間 値 v0 es es.toProperty(v0)
9.
EventStream / Property •
Property#changes() • Property の値が変化した タイミングで、変化後の 値が流れる EventStream を作る。 • 初期値は流れない 時間 値 p.changes() 時間 値 p
10.
EventStream / Property •
Property#sampledBy(EventStream) • EventStream に値が流れた時点 の Property の値が流れる EventStream を作る。 時間 値 時間 値 時間 値 p es p.sampledBy(es)
11.
画面を作る
12.
設計方針 • 出来るだけ副作用を排除したい。 • Callback
内処理を、単純な副作用だけにしたい。
13.
例: Click Counter クリックすると 増える
14.
例: Click Counter $(function()
{ var constant = function(x) { return function() { return x; }; }; var mkButton = function(conf) { var el = $('<button />').text(conf.text); return { el: el, streams: { clicked: el.asEventStream('click') } }; }; var mkText = function(conf) { var el = $('<span />'); conf.text.assign(function(text) { el.text(text); }); return { el: el }; }; var button = mkButton({ text: 'click' }); var text = mkText({ text: button.streams.clicked .map(constant(1)) .scan(0, function(a, b) { return a + b; }), }); $('body').append(button.el).append(' ').append(text.el); });
15.
例: Click Counter $(function()
{ var constant = function(x) { return function() { return x; }; }; var mkButton = function(conf) { var el = $('<button />').text(conf.text); return { el: el, streams: { clicked: el.asEventStream('click') } }; }; var mkText = function(conf) { var el = $('<span />'); conf.text.assign(function(text) { el.text(text); }); return { el: el }; }; var button = mkButton({ text: 'click' }); var text = mkText({ text: button.streams.clicked .map(constant(1)) .scan(0, function(a, b) { return a + b; }), }); $('body').append(button.el).append(' ').append(text.el); }); ボタン右のテキストの値 (時間によって変化する)
16.
例: Click Counter $(function()
{ var constant = function(x) { return function() { return x; }; }; var mkButton = function(conf) { var el = $('<button />').text(conf.text); return { el: el, streams: { clicked: el.asEventStream('click') } }; }; var mkText = function(conf) { var el = $('<span />'); conf.text.assign(function(text) { el.text(text); }); return { el: el }; }; var button = mkButton({ text: 'click' }); var text = mkText({ text: button.streams.clicked .map(constant(1)) .scan(0, function(a, b) { return a + b; }), }); $('body').append(button.el).append(' ').append(text.el); }); クリックされたら 1 が 流れてくる EventStream
17.
例: Click Counter $(function()
{ var constant = function(x) { return function() { return x; }; }; var mkButton = function(conf) { var el = $('<button />').text(conf.text); return { el: el, streams: { clicked: el.asEventStream('click') } }; }; var mkText = function(conf) { var el = $('<span />'); conf.text.assign(function(text) { el.text(text); }); return { el: el }; }; var button = mkButton({ text: 'click' }); var text = mkText({ text: button.streams.clicked .map(constant(1)) .scan(0, function(a, b) { return a + b; }), }); $('body').append(button.el).append(' ').append(text.el); }); クリックされたら 1 が 流れてくる EventStream 0 から順に、足して畳み込んでいく (初期値 0 の Property ができる)
18.
例: Click Counter $(function()
{ var constant = function(x) { return function() { return x; }; }; var mkButton = function(conf) { var el = $('<button />').text(conf.text); return { el: el, streams: { clicked: el.asEventStream('click') } }; }; var mkText = function(conf) { var el = $('<span />'); conf.text.assign(function(text) { el.text(text); }); return { el: el }; }; var button = mkButton({ text: 'click' }); var text = mkText({ text: button.streams.clicked .map(constant(1)) .scan(0, function(a, b) { return a + b; }), }); $('body').append(button.el).append(' ').append(text.el); }); 副作用 (値変化時の処理登録、 テキストの中身を変更) 副作用 (DOM 要素登録)
19.
例: Counting Button クリックすると増える
20.
例: Counting Button クリックすると増える Property
を作るために、 作成後の button の EventStream が必要
21.
例: Counting Button クリックすると増える Property
を作るために、 作成後の button の EventStream が必要 EventStream -> Property な 関数を渡すようにしてみる
22.
例: Counting Button $(function()
{ var constant = function(x) { return function() { return x; }; }; var mkButton = function(conf) { var el = $('<button />'); var streams = { clicked: el.asEventStream('click') }; var properties = conf.f(streams); properties.text.assign(function(text) { el.text(text); }); return { el: el }; }; var button = mkButton({ f: function(streams) { return { text: streams.clicked .map(constant(1)) .scan(0, function(a, b) { return a + b; }), }; }, }); $('body').append(button.el); });
23.
例: Counting Button $(function()
{ var constant = function(x) { return function() { return x; }; }; var mkButton = function(conf) { var el = $('<button />'); var streams = { clicked: el.asEventStream('click') }; var properties = conf.f(streams); properties.text.assign(function(text) { el.text(text); }); return { el: el }; }; var button = mkButton({ f: function(streams) { return { text: streams.clicked .map(constant(1)) .scan(0, function(a, b) { return a + b; }), }; }, }); $('body').append(button.el); }); EventStream -> Property な関数
24.
例: Counting Button $(function()
{ var constant = function(x) { return function() { return x; }; }; var mkButton = function(conf) { var el = $('<button />'); var streams = { clicked: el.asEventStream('click') }; var properties = conf.f(streams); properties.text.assign(function(text) { el.text(text); }); return { el: el }; }; var button = mkButton({ f: function(streams) { return { text: streams.clicked .map(constant(1)) .scan(0, function(a, b) { return a + b; }), }; }, }); $('body').append(button.el); }); 渡された EventStream を 畳み込んで、Propertyを作る
25.
例: Counting Button $(function()
{ var constant = function(x) { return function() { return x; }; }; var mkButton = function(conf) { var el = $('<button />'); var streams = { clicked: el.asEventStream('click') }; var properties = conf.f(streams); properties.text.assign(function(text) { el.text(text); }); return { el: el }; }; var button = mkButton({ f: function(streams) { return { text: streams.clicked .map(constant(1)) .scan(0, function(a, b) { return a + b; }), }; }, }); $('body').append(button.el); }); 先に EventStream を 作っておいて、
26.
例: Counting Button $(function()
{ var constant = function(x) { return function() { return x; }; }; var mkButton = function(conf) { var el = $('<button />'); var streams = { clicked: el.asEventStream('click') }; var properties = conf.f(streams); properties.text.assign(function(text) { el.text(text); }); return { el: el }; }; var button = mkButton({ f: function(streams) { return { text: streams.clicked .map(constant(1)) .scan(0, function(a, b) { return a + b; }), }; }, }); $('body').append(button.el); }); 渡された関数に適用 して、Property を得る 先に EventStream を 作っておいて、
27.
$(function() { var
constant = function(x) { return function() { return x; }; }; var mkButton = function(conf) { var el = $('<button />'); var streams = { clicked: el.asEventStream('click') }; var properties = conf.f(streams); properties.text.assign(function(text) { el.text(text); }); return { el: el }; }; var button = mkButton({ f: function(streams) { return { text: streams.clicked .map(constant(1)) .scan(0, function(a, b) { return a + b; }), }; }, }); $('body').append(button.el); }); 例: Counting Button 副作用 (値変化時の処理登録、 テキストの中身を変更) 副作用 (DOM 要素登録)
28.
例: Cross Counting
Button クリックすると 反対側が増える
29.
例: Cross Counting
Button クリックすると 反対側が増える Property を作るために、 別の button の EventStream が必要 (相互に要求)
30.
例: Cross Counting
Button クリックすると 反対側が増える Property を作るために、 別の button の EventStream が必要 (相互に要求) Bus を使い、 一旦 EventStream として渡しておき、 button を作るときに Bus につなぐ
31.
例: Cross Counting
Button $(function() { var constant = function(x) { return function() { return x; }; }; var mkButton = { streams: function() { return { clicked: new Bacon.Bus() }; }, create: function(conf) { var el = $('<button />'); conf.properties.text.assign(function(text) { el.text(text); }); conf.streams.clicked.plug(el.asEventStream('click')); return { el: el }; }, }; var streams1 = mkButton.streams(); var streams2 = mkButton.streams(); var logic = function(s) { return s.clicked.map(constant(1)).scan(0, function(a, b) { return a + b; }); }; var button1 = mkButton.create({ properties: { text: logic(streams2) }, streams: streams1, }); var button2 = mkButton.create({ properties: { text: logic(streams1) }, streams: streams2, }); $('body').append(button1.el).append(' ').append(button2.el); });
32.
例: Cross Counting
Button $(function() { var constant = function(x) { return function() { return x; }; }; var mkButton = { streams: function() { return { clicked: new Bacon.Bus() }; }, create: function(conf) { var el = $('<button />'); conf.properties.text.assign(function(text) { el.text(text); }); conf.streams.clicked.plug(el.asEventStream('click')); return { el: el }; }, }; var streams1 = mkButton.streams(); var streams2 = mkButton.streams(); var logic = function(s) { return s.clicked.map(constant(1)).scan(0, function(a, b) { return a + b; }); }; var button1 = mkButton.create({ properties: { text: logic(streams2) }, streams: streams1, }); var button2 = mkButton.create({ properties: { text: logic(streams1) }, streams: streams2, }); $('body').append(button1.el).append(' ').append(button2.el); }); button から出る EventStream を Bus として 取得できるようにしておく
33.
例: Cross Counting
Button $(function() { var constant = function(x) { return function() { return x; }; }; var mkButton = { streams: function() { return { clicked: new Bacon.Bus() }; }, create: function(conf) { var el = $('<button />'); conf.properties.text.assign(function(text) { el.text(text); }); conf.streams.clicked.plug(el.asEventStream('click')); return { el: el }; }, }; var streams1 = mkButton.streams(); var streams2 = mkButton.streams(); var logic = function(s) { return s.clicked.map(constant(1)).scan(0, function(a, b) { return a + b; }); }; var button1 = mkButton.create({ properties: { text: logic(streams2) }, streams: streams1, }); var button2 = mkButton.create({ properties: { text: logic(streams1) }, streams: streams2, }); $('body').append(button1.el).append(' ').append(button2.el); }); 先に EventStream だけ 取得しておいて、 button から出る EventStream を Bus として 取得できるようにしておく
34.
例: Cross Counting
Button $(function() { var constant = function(x) { return function() { return x; }; }; var mkButton = { streams: function() { return { clicked: new Bacon.Bus() }; }, create: function(conf) { var el = $('<button />'); conf.properties.text.assign(function(text) { el.text(text); }); conf.streams.clicked.plug(el.asEventStream('click')); return { el: el }; }, }; var streams1 = mkButton.streams(); var streams2 = mkButton.streams(); var logic = function(s) { return s.clicked.map(constant(1)).scan(0, function(a, b) { return a + b; }); }; var button1 = mkButton.create({ properties: { text: logic(streams2) }, streams: streams1, }); var button2 = mkButton.create({ properties: { text: logic(streams1) }, streams: streams2, }); $('body').append(button1.el).append(' ').append(button2.el); }); 先に EventStream だけ 取得しておいて、 EventStream から Property を作成 button から出る EventStream を Bus として 取得できるようにしておく
35.
例: Cross Counting
Button $(function() { var constant = function(x) { return function() { return x; }; }; var mkButton = { streams: function() { return { clicked: new Bacon.Bus() }; }, create: function(conf) { var el = $('<button />'); conf.properties.text.assign(function(text) { el.text(text); }); conf.streams.clicked.plug(el.asEventStream('click')); return { el: el }; }, }; var streams1 = mkButton.streams(); var streams2 = mkButton.streams(); var logic = function(s) { return s.clicked.map(constant(1)).scan(0, function(a, b) { return a + b; }); }; var button1 = mkButton.create({ properties: { text: logic(streams2) }, streams: streams1, }); var button2 = mkButton.create({ properties: { text: logic(streams1) }, streams: streams2, }); $('body').append(button1.el).append(' ').append(button2.el); }); button から出る EventStream を Bus として 取得できるようにしておく 先に EventStream だけ 取得しておいて、 一緒に EventStream (Bus) を渡す EventStream から Property を作成
36.
例: Cross Counting
Button $(function() { var constant = function(x) { return function() { return x; }; }; var mkButton = { streams: function() { return { clicked: new Bacon.Bus() }; }, create: function(conf) { var el = $('<button />'); conf.properties.text.assign(function(text) { el.text(text); }); conf.streams.clicked.plug(el.asEventStream('click')); return { el: el }; }, }; var streams1 = mkButton.streams(); var streams2 = mkButton.streams(); var logic = function(s) { return s.clicked.map(constant(1)).scan(0, function(a, b) { return a + b; }); }; var button1 = mkButton.create({ properties: { text: logic(streams2) }, streams: streams1, }); var button2 = mkButton.create({ properties: { text: logic(streams1) }, streams: streams2, }); $('body').append(button1.el).append(' ').append(button2.el); }); 渡された Bus に、 クリックの EventStream をつなげる
37.
例: Cross Counting
Button $(function() { var constant = function(x) { return function() { return x; }; }; var mkButton = { streams: function() { return { clicked: new Bacon.Bus() }; }, create: function(conf) { var el = $('<button />'); conf.properties.text.assign(function(text) { el.text(text); }); conf.streams.clicked.plug(el.asEventStream('click')); return { el: el }; }, }; var streams1 = mkButton.streams(); var streams2 = mkButton.streams(); var logic = function(s) { return s.clicked.map(constant(1)).scan(0, function(a, b) { return a + b; }); }; var button1 = mkButton.create({ properties: { text: logic(streams2) }, streams: streams1, }); var button2 = mkButton.create({ properties: { text: logic(streams1) }, streams: streams2, }); $('body').append(button1.el).append(' ').append(button2.el); }); 副作用 (値変化時の処理登録、 テキストの中身を変更) 副作用 (DOM 要素登録) 副作用 (Bus につなぐ)
38.
リクエスト処理
39.
Bacon.fromPromise • jQuery の
ajax 系メソッドが返 す Promise オブジェクトから、 EventStream を作る。
40.
Bacon.fromPromise • jQuery の
ajax 系メソッドが返 す Promise オブジェクトから、 EventStream を作る。 assign は、イベント登録 を解除する関数を返す 流れてきたレスポンス
41.
• レスポンスがエラーの場合、そのままでは assign
等に流れていかない。 エラーレスポンスの処理
42.
エラーレスポンスの処理 • レスポンスがエラーの場合、そのままでは assign
等に流れていかない。 • mapError メソッドを使って、エラー系の流れを変換関数を通して本流へ流 す。 流れてきたエラーレスポンス エラー系の流れをそのまま本流へ
43.
EventStream#flatMap/flatMapLatest • EventStream が流れてくる
EventStream を、中の EventStream に流れる値が 流れてくる EventStream にする 引用: https://github.com/raimohanska/bacon.js/wiki/Diagrams 引用: https://github.com/raimohanska/bacon.js/wiki/Diagrams flatMap flatMapLatest
44.
EventStream#flatMap/flatMapLatest • EventStream が流れてくる
EventStream を、中の EventStream に流れる値が 流れてくる EventStream にする 引用: https://github.com/raimohanska/bacon.js/wiki/Diagrams 引用: https://github.com/raimohanska/bacon.js/wiki/Diagrams flatMap flatMapLatest 全部流す 後の結果の方が速ければ、前の結果は流れない
45.
リクエスト処理の例 右に入力したパスに GET リクエストを飛ばす 返ってきたレスポンスを表示
46.
ヘルパ var constant =
function(x) { return function() { return x; }; }; var id = function(x) { return x; }; var left = function(x) { return function(f, g) { return f(x); }; }; var right = function(x) { return function(f, g) { return g(x); }; }; var mkButton = { streams: function() { return { clicked: new Bacon.Bus() }; }, create: function(conf) { var el = $('<button />'); conf.properties.text.assign(function(text) { el.text(text); }); conf.properties.enable.assign(function(enable) { el.attr('disabled', !enable); }); conf.streams.clicked.plug(el.asEventStream('click')); return { el: el }; }, }; var mkTextarea = function(conf) { var el = $('<textarea />'); conf.val.assign(function(text) { el.val(text); }); return { el: el }; };
47.
ヘルパ var constant =
function(x) { return function() { return x; }; }; var id = function(x) { return x; }; var left = function(x) { return function(f, g) { return f(x); }; }; var right = function(x) { return function(f, g) { return g(x); }; }; var mkButton = { streams: function() { return { clicked: new Bacon.Bus() }; }, create: function(conf) { var el = $('<button />'); conf.properties.text.assign(function(text) { el.text(text); }); conf.properties.enable.assign(function(enable) { el.attr('disabled', !enable); }); conf.streams.clicked.plug(el.asEventStream('click')); return { el: el }; }, }; var mkTextarea = function(conf) { var el = $('<textarea />'); conf.val.assign(function(text) { el.val(text); }); return { el: el }; }; Either
48.
ヘルパ var constant =
function(x) { return function() { return x; }; }; var id = function(x) { return x; }; var left = function(x) { return function(f, g) { return f(x); }; }; var right = function(x) { return function(f, g) { return g(x); }; }; var mkButton = { streams: function() { return { clicked: new Bacon.Bus() }; }, create: function(conf) { var el = $('<button />'); conf.properties.text.assign(function(text) { el.text(text); }); conf.properties.enable.assign(function(enable) { el.attr('disabled', !enable); }); conf.streams.clicked.plug(el.asEventStream('click')); return { el: el }; }, }; var mkTextarea = function(conf) { var el = $('<textarea />'); conf.val.assign(function(text) { el.val(text); }); return { el: el }; }; ボタンの 有効/無効 も Property で受け取る
49.
ヘルパ var constant =
function(x) { return function() { return x; }; }; var id = function(x) { return x; }; var left = function(x) { return function(f, g) { return f(x); }; }; var right = function(x) { return function(f, g) { return g(x); }; }; var mkButton = { streams: function() { return { clicked: new Bacon.Bus() }; }, create: function(conf) { var el = $('<button />'); conf.properties.text.assign(function(text) { el.text(text); }); conf.properties.enable.assign(function(enable) { el.attr('disabled', !enable); }); conf.streams.clicked.plug(el.asEventStream('click')); return { el: el }; }, }; var mkTextarea = function(conf) { var el = $('<textarea />'); conf.val.assign(function(text) { el.val(text); }); return { el: el }; }; 出力用テキストエリア 設定は出力値 Property のみ。
50.
実装 var input =
$('<input type="text" />').width(400); var bs = mkButton.streams(); var request = bs.clicked.map(function() { return input.val(); }); var response = request.flatMapLatest(function(url) { return Bacon.fromPromise($.get(url)).map(right) .mapError(function(e) { return left(e.responseText); }); }); var button = mkButton.create({ properties: { text: Bacon.constant('request'), enable: Bacon.once(true) .merge(request.map(constant(false))) .merge(response.map(constant(true))) .toProperty(), }, streams: bs, }); var output = mkTextarea({ val: response.map(function(r) { return r(function(msg) { return 'error - ' + msg; }, id); }).toProperty(), }); $('body') .append($('<p />').append(input).append(' ').append(button.el)) .append($('<p />').append(output.el.width(500).height(200)));
51.
実装 var input =
$('<input type="text" />').width(400); var bs = mkButton.streams(); var request = bs.clicked.map(function() { return input.val(); }); var response = request.flatMapLatest(function(url) { return Bacon.fromPromise($.get(url)).map(right) .mapError(function(e) { return left(e.responseText); }); }); var button = mkButton.create({ properties: { text: Bacon.constant('request'), enable: Bacon.once(true) .merge(request.map(constant(false))) .merge(response.map(constant(true))) .toProperty(), }, streams: bs, }); var output = mkTextarea({ val: response.map(function(r) { return r(function(msg) { return 'error - ' + msg; }, id); }).toProperty(), }); $('body') .append($('<p />').append(input).append(' ').append(button.el)) .append($('<p />').append(output.el.width(500).height(200))); ボタンがクリックされると 入力値が流れる EventStream
52.
実装 var input =
$('<input type="text" />').width(400); var bs = mkButton.streams(); var request = bs.clicked.map(function() { return input.val(); }); var response = request.flatMapLatest(function(url) { return Bacon.fromPromise($.get(url)).map(right) .mapError(function(e) { return left(e.responseText); }); }); var button = mkButton.create({ properties: { text: Bacon.constant('request'), enable: Bacon.once(true) .merge(request.map(constant(false))) .merge(response.map(constant(true))) .toProperty(), }, streams: bs, }); var output = mkTextarea({ val: response.map(function(r) { return r(function(msg) { return 'error - ' + msg; }, id); }).toProperty(), }); $('body') .append($('<p />').append(input).append(' ').append(button.el)) .append($('<p />').append(output.el.width(500).height(200))); 入力が流れてきたら、 リクエストを飛ばす
53.
実装 var input =
$('<input type="text" />').width(400); var bs = mkButton.streams(); var request = bs.clicked.map(function() { return input.val(); }); var response = request.flatMapLatest(function(url) { return Bacon.fromPromise($.get(url)).map(right) .mapError(function(e) { return left(e.responseText); }); }); var button = mkButton.create({ properties: { text: Bacon.constant('request'), enable: Bacon.once(true) .merge(request.map(constant(false))) .merge(response.map(constant(true))) .toProperty(), }, streams: bs, }); var output = mkTextarea({ val: response.map(function(r) { return r(function(msg) { return 'error - ' + msg; }, id); }).toProperty(), }); $('body') .append($('<p />').append(input).append(' ').append(button.el)) .append($('<p />').append(output.el.width(500).height(200))); 正常レスポンスを Right で包んでおいて、
54.
実装 var input =
$('<input type="text" />').width(400); var bs = mkButton.streams(); var request = bs.clicked.map(function() { return input.val(); }); var response = request.flatMapLatest(function(url) { return Bacon.fromPromise($.get(url)).map(right) .mapError(function(e) { return left(e.responseText); }); }); var button = mkButton.create({ properties: { text: Bacon.constant('request'), enable: Bacon.once(true) .merge(request.map(constant(false))) .merge(response.map(constant(true))) .toProperty(), }, streams: bs, }); var output = mkTextarea({ val: response.map(function(r) { return r(function(msg) { return 'error - ' + msg; }, id); }).toProperty(), }); $('body') .append($('<p />').append(input).append(' ').append(button.el)) .append($('<p />').append(output.el.width(500).height(200))); 正常レスポンスを Right で包んでおいて、 エラーレスポンスは Left で包んで本流へ
55.
実装 var input =
$('<input type="text" />').width(400); var bs = mkButton.streams(); var request = bs.clicked.map(function() { return input.val(); }); var response = request.flatMapLatest(function(url) { return Bacon.fromPromise($.get(url)).map(right) .mapError(function(e) { return left(e.responseText); }); }); var button = mkButton.create({ properties: { text: Bacon.constant('request'), enable: Bacon.once(true) .merge(request.map(constant(false))) .merge(response.map(constant(true))) .toProperty(), }, streams: bs, }); var output = mkTextarea({ val: response.map(function(r) { return r(function(msg) { return 'error - ' + msg; }, id); }).toProperty(), }); $('body') .append($('<p />').append(input).append(' ').append(button.el)) .append($('<p />').append(output.el.width(500).height(200))); レスポンスが返ってくるまでは ボタンを無効にする
56.
実装 var input =
$('<input type="text" />').width(400); var bs = mkButton.streams(); var request = bs.clicked.map(function() { return input.val(); }); var response = request.flatMapLatest(function(url) { return Bacon.fromPromise($.get(url)).map(right) .mapError(function(e) { return left(e.responseText); }); }); var button = mkButton.create({ properties: { text: Bacon.constant('request'), enable: Bacon.once(true) .merge(request.map(constant(false))) .merge(response.map(constant(true))) .toProperty(), }, streams: bs, }); var output = mkTextarea({ val: response.map(function(r) { return r(function(msg) { return 'error - ' + msg; }, id); }).toProperty(), }); $('body') .append($('<p />').append(input).append(' ').append(button.el)) .append($('<p />').append(output.el.width(500).height(200))); 正常レスポンスはそのまま出力 エラーレスポンスは ‘error - ’ に続けて出力
57.
設計 • 実際には、streams から
properties を作る部分や、リクエストを飛ばす部分 は、別モジュールにしておくといい。 ViewLogic Storage Ajax App get streams, create from properties create properties from streams and storages create response-streams
Jetzt herunterladen