FrontendLab: Programming UI with FRP and Bacon js - Вячеслав Ворончук
Upcoming SlideShare
Loading in...5
×
 

FrontendLab: Programming UI with FRP and Bacon js - Вячеслав Ворончук

on

  • 1,003 views

 

Statistics

Views

Total Views
1,003
Views on SlideShare
1,003
Embed Views
0

Actions

Likes
1
Downloads
12
Comments
1

0 Embeds 0

No embeds

Accessibility

Categories

Upload Details

Uploaded via as Adobe PDF

Usage Rights

© All Rights Reserved

Report content

Flagged as inappropriate Flag as inappropriate
Flag as inappropriate

Select your reason for flagging this presentation as inappropriate.

Cancel
  • Full Name Full Name Comment goes here.
    Are you sure you want to
    Your message goes here
    Processing…
Post Comment
Edit your comment

FrontendLab: Programming UI with FRP and Bacon js - Вячеслав Ворончук FrontendLab: Programming UI with FRP and Bacon js - Вячеслав Ворончук Presentation Transcript

  • Programming UI with FRP and Bacon.js Vyacheslav Voronchuk voronchuk@wm-software.com Skype: voronhukvk http://ua.linkedin.com/in/voronchuk/
  • What is FRP? l  Sources of data l  Events l  Side-effects l  Switching
  • Javascript FRP frameworks l  Elm l  Flapjax l  Javelin l  RxJS l  others
  • How it helps? l  Callback spaghetti l  Refactoring l  New features and tweaks
  • 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(); }); }); }); }); }); });
  • 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');
  • 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); } );
  • Bus var $deleteItem = new Bacon.Bus(); $deleteItem.plug(Bacon.fromEventTarget($('.delete'), 'click')); $deleteItem.map('.target.remove'); $deleteItem.push($('item1'));
  • 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({}) }
  • 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)
  • 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);
  • Debug l  Observable.log() l  Observable.toString() l  Observable.deps() l  Observable.internalDeps() l  Bacon.spy(f)
  • Notices l  onValue l  fromArray
  • Links l  Github page: https://github.com/baconjs/bacon.js l  Bacon.js blog: http://baconjs.blogspot.fi/ l  Snake game: http://philipnilsson.github.io/badness/ l  Worzone game: http://juhajasatu.com/worzone/