Contenu connexe Similaire à Programming UI with FRP and Bacon.js Similaire à Programming UI with FRP and Bacon.js (20) Plus de GeeksLab Odessa (20) Programming UI with FRP and Bacon.js1. Programming UI with FRP and Bacon.js
Vyacheslav Voronchuk
voronchuk@wm-software.com
Skype: voronhukvk
http://ua.linkedin.com/in/voronchuk/
6. FlatMap
$.ajax({ url: “/items” }).done(function(items) {
if(items.error) {
return handleError(items.error);
}
$.each(items, function(item) {
$.ajax({ url: “/item/” + item.id }).done(function(item) {
if(item.error) {
return handleError(item.error);
}
renderItem(item, function(itemEl) {
itemEl.children('.delete').click(function() {
$.ajax({ url: “/item/” + item.id, type:
'DELETE' }).done(function(response) {
if(response.error) {
return handleError(response.error);
}
itemEl.remove();
});
});
});
});
});
});
7. FlatMap
isError = function(serverResponse) { return typeof serverResponse.error !== 'undefined' }
isNotError = function(serverResponse) { return !isError(serverResponse); }
$allItems = Bacon.fromPromise($.ajax({ url: “/items” }));
$errors = $allItems.filter(isError);
$items = $allItems.filter(isNotError).flatMap(function (item) {
return Bacon.fromPromise($.ajax({ url: “/item” + item.id }));
});
$errors.merge($items.filter(isError));
$renderedItems = $items.filter(isNotError).flatMap(function(item) {
return Bacon.fromCallback(renderItem, item);
});
$renderedItemsDeleteClicks = $renderedItems.flatMap(function(renderedItem){
return Bacon.fromEventTarget(renderedItem.children('.delete'), 'click', function(event) {
return renderedItem;
});
});
$deleteItemRequest = $renderedItemsDeleteClicks.flatMap(function(renderedItem) {
return Bacon.fromPromise($.ajax({ url: “/item” + renderedItem.data('id'), type: 'DELETE' }));
});
$errors.merge($deleteItemRequest.filter(isError));
$errors.onValue(handleError);
$deleteItemRequest.filter(isNotError).onValue('.remove');
8. When and Combine
isError = function(serverResponse) { return typeof serverResponse.error !== 'undefined' }
isNotError = function(serverResponse) { return !isError(serverResponse); }
$items = Bacon.fromPromise($.ajax({ url: “/items” }));
$renderedItems = $items.filter(isNotError).flatMap(function(item) {
return Bacon.fromCallback(renderItem, item);
});
$quantity = $renderedItems.flatMap(function(element) {
return Bacon.fromEventTarget($(element).children('.quantity'), 'change').map(function(event) {
return [element, $(element).val()];
});
});
$price = $quantity.map(function(data) {
return $(data[0]).data('price') * data[1];
});
$refreshClick = Bacon.fromEventTarget($('#refresh_cart'), 'change');
Bacon.when(
[$refreshClick, $quantity, $price], function(event, data, price) {
$(data[0]).children('.internal-price').text(price);
$(data[0]).children('.price').text(price);
},
[$quantity, $price], function(data, price) {
$(data[0]).children('.internal-price').text(price);
}
);
9. Bus
var $deleteItem = new Bacon.Bus();
$deleteItem.plug(Bacon.fromEventTarget($('.delete'), 'click'));
$deleteItem.map('.target.remove');
$deleteItem.push($('item1'));
10. Bus
var chopsticks = [new Bacon.Bus(), new Bacon.Bus(), new Bacon.Bus()]
var hungry = [new Bacon.Bus(), new Bacon.Bus(), new Bacon.Bus()]
var eat = function(i) {
return function() {
setTimeout(function() {
chopsticks[i].push({})
chopsticks[(i+1) % 3].push({})
}, 1000);
return 'philosopher ' + i + ' eating'
}
}
var dining = Bacon.when(
[hungry[0], chopsticks[0], chopsticks[1]], eat(0),
[hungry[1], chopsticks[1], chopsticks[2]], eat(1),
[hungry[2], chopsticks[2], chopsticks[0]], eat(2)
).log()
// make all chopsticks initially available
chopsticks[0].push({}); chopsticks[1].push({}); chopsticks[2].push({})
// make philosophers hungry in some way, in this case we just push to their bus
for (var i = 0; i < 3; i++) {
hungry[0].push({}); hungry[1].push({}); hungry[2].push({})
}
11. Usage in Node.js
getInvoiceStream = (id) -> Bacon.fromNodeCallback Invoice, 'findOne', id: id
getInvoiceDataStream = ($invoice) -> $invoice.flatMap (invoice) ->
Bacon.fromNodeCallback invoice, 'toDeepJSON'
# Load Invoice and it's deps
get: (req, res) ->
$invoice = getInvoiceStream req.param 'id'
$invoiceData = getInvoiceDataStream invoice
$invoiceData.onValue _.bind(res.json, res)
$errors = Bacon.mergeAll $invoice.errors(), $invoiceData.errors()
$errors.onError _.bind(res.send, res, 500)
# Generate PDF export
pdf: (req, res) ->
$invoice = getInvoiceStream req.param 'id'
$invoiceData = getInvoiceDataStream $invoice
$invoiceRender = $invoiceData.map renderInvoicePDF
$invoiceRenderData = $invoiceRender.flatMap (pdf) -> Bacon.fromCallback pdf, 'output'
$invoiceRenderData.onValue _.bind(res.end, res)
$errors = Bacon.mergeAll $invoice.errors(), $invoiceData.errors()
$errors.onError _.bind(res.send, res, 500)
12. AJAX Search
// Stream of search queries
var $query = $("#search").asEventStream("keyup").map(function(event) {
return $(event.srcElement).val();
}).skipDuplicates();
// Search result strings
var $results = $query.throttle(500).flatMapLatest(function(query) {
return Bacon.fromPromise($.ajax("/search/" + query))
}).mapError("Search error");
// Render results
$results.onValue(function(result) {
$("#results").html(result);
});
// Search status
var searchStatus = $query.map(true).merge($results.map(false)).skipDuplicates().toProperty(false);