Demo Part 4

In notebook:
FrontEndMasters Hardcore Functional
Created at:
2017-07-10
Updated:
2017-07-16
Tags:
Functional Programming JavaScript jQuery libraries

Now we want to display the videos from the returned list from the AJAX call.

  /* global define */
define([
...
], function($, _, P, Maybe, Player, bacon, http) {
  'use strict';
  io.extendFn()

  // HELPERS ///////////////////////////////////////////////////////////////////////////
  var compose = P.compose;
  ..


  // PURE //////////////////////////////////////////////////////////////////////////////
  var listen = _.curry(function (type, elt) {
    return Bacon.fromEventTarget(elt, type)
  })
  
  var getDom = $.toIO()
  
  var keypressStream = listen('keyup')

  // eventValue :: DomEvent -> String
  var eventValue = compose(_.get('value')), _.get('target'))  
  //  valueStream :: Dom -> EventStream String
  var valueStream = compose(map(eventValue), keypressStream)
  
  // termUrl :: String -> URL
  var termUrl = function (term) {
    return "http://gdata.youtube.com/feeds/api/videos?" + 
      $.param({q:term, alt: 'json'})
  }
  
  // urlStream :: Dom -> EventStream URL (String)
  var urlStream = compose(map(termUrl, valueStream))
  
  // search :: String -> Future JSON
  var searchStream = comose(map(http.getJSON), urlStream)
  
  // **** 2. 
  //  entryToLi :: Entry -> Dom
  var entryToLi = function (elt) {
    // practical to construct it with jQuery...
    // adding the attributes is easier with jQuery
    return $("<li />", { 'data-youtubeid': elt.id.$t, text: elt.title.$t })  
  }
  
  // **** 1. 
  // videoUrls :: JSON -> [URL]
  // getting the values from the JSON
  // the JSON uses "$t" for the entry id value property name!!
  var videoUrls = compose(map(getEntryUrl), _.get('entry'),  _.get('feed'))
  
  // **** 4. 
  var liStream = compose(map(resultToHtml), searchStream)
  
  // IMPURE ////////////////////////////////////////////////////////////////////////////
  // **** 3. log out again
  // set the right side of the of fork to setHTML("#results")
  getDom('#search').map(searchStream).runIO().onValue(fork(log, setHTML("#results")))

});

We need to get the entry.id property from the JSON response.

(They're basically writing Haskell. Verbose Haskell)

At step #4 above, they decide it's better to go through the diffs of the GIT repo rather than debugging the app step by step. (time: -2.34)


The final app.js from the repo:

  /* global define */
define([
..
], function($, _, P, Maybe, Player, io, bacon, http) {
  'use strict';
  io.extendFn();

  // HELPERS ///////////////////////////////////////////
  var compose = P.compose;
  var map = P.map;
  var log = function(x) { console.log(x); return x; }
  var fork = _.curry(function(f, future) { return future.fork(log, f); })
  var setHtml = _.curry(function(sel, x) { return $(sel).html(x); });
  var listen = _.curry(function (event, target) {
    return bacon.fromEventTarget(target, event);
  });
  var getData = _.curry(function(name, elt) { return $(elt).data(name); });
  var last = function(ar) { return ar[ar.length - 1]; };

  // PURE //////////////////////////////////////////////////
  
  //  api_key :: String
  var api_key = 'AIzaSyAWoa7aqds2Cx_drrrb5FPsRObFa7Dxkfg';

  //+ eventValue :: DomEvent -> String
  var eventValue = compose(_.get('value'), _.get('target'));

  //+ valueStream :: DomEvent -> EventStream String
  var valueStream = compose(map(eventValue), listen('keyup'));

  //+ termToUrl :: String -> URL
  var termToUrl = function(term) {
    return 'https://www.googleapis.com/youtube/v3/search?' +
      $.param({part: 'snippet', q: term, key: api_key});
  };

  //+ urlStream :: DomEvent -> EventStream String
  var urlStream = compose(map(termToUrl), valueStream);

  //+ getInputStream :: Selector -> IO EventStream String
  var getInputStream = compose(map(urlStream), $.toIO());

  //+ render :: Entry -> Dom
  var render = function(e) {
    return $('<li/>', {text: e.snippet.title, 'data-youtubeid': e.id.videoId});
  };

  //+ videoEntries :: YoutubeResponse -> [Dom]
  var videoEntries = compose(map(render), _.get('items'));

  //+ search :: URL -> Future [Dom]
  var search = compose(map(videoEntries), http.getJSON);

  //+ DomElement -> EventStream DomElement
  var clickStream = compose(map(_.get('target')), listen('click'));

  //+ URL -> String
  var idInUrl = compose(last, _.split('/'));

  //+ youtubeLink :: DomElement -> Maybe ID
  var youtubeId = compose(map(idInUrl), Maybe, getData('youtubeid'));

  // IMPURE /////////////////////////////////////////////////////

  getInputStream('#search').runIO().onValue(
    compose(fork(setHtml('#results')), search)
  );

  clickStream(document).onValue(
    compose(map(compose(setHtml('#player'), Player.create)), youtubeId)
  );

});