RiotJS

HTML

<h1>My first attempt using RiotJs</h1>

<blog></blog>

<script type="riot/tag">

    <article-list>
        <ul>
            <li each={parent.selectedCategoryArticles}>
                {title + ' by ' + author!}
            </li>
        </ul>
    </article-list>

    <category-list>
        <ul>
            <li each={parent.categories} onclick={parent.parent.onCategorySelected} class={'category-item'}>
                {title}
            </li>
        </ul>
    </category-list>

    <blog>

        <category-list categories={categories} />
        <article-list articles={selectedCategoryArticles} />
    
        this.categories = opts.categories
        this.articles   = opts.articles
        
        init() {
            this.onCategorySelected(opts.defaultCategoryId)
        }
        
        onCategorySelected(categoryId) {
            if (typeof categoryId !== 'number') categoryId = categoryId.item.id
            this.selectedCategoryArticles = this.articles.filter(function(article) {
                return article.categoryId === categoryId
            })
            this.update()
        }
        
        this.init()
            
    </blog>

</script>

<script>
    riot.mount('blog', {
        categories: [
            { id: 1, title: 'AngularJS' },
            { id: 2, title: 'React' }
        ],
        defaultCategoryId: 1,
        articles: [
            { id: 1, categoryId: 1, title: 'Managing Client Only State in AngularJS', author: 'M Godfrey' },
            { id: 2, categoryId: 1, title: 'The Best Way to Share Data Between AngularJS Controllers', author: 'M Godfrey' },
            { id: 3, categoryId: 2, title: 'Demystifying React Component State', author: 'M Godfrey' }
        ]
    })
</script>

CSS

.category-item {
    cursor: pointer;
}

JavaScript

/* Riot WIP, @license MIT, (c) 2015 Muut Inc. + contributors */

;(function(window) {
  var riot = { version: 'WIP', settings: {} }

riot.observable = function(el) {

  el = el || {}

  var callbacks = {},
      _id = 0

  el.on = function(events, fn) {
    if (typeof fn == 'function') {
      fn._id = typeof fn._id == 'undefined' ? _id++ : fn._id

      events.replace(/\S+/g, function(name, pos) {
        (callbacks[name] = callbacks[name] || []).push(fn)
        fn.typed = pos > 0
      })
    }
    return el
  }

  el.off = function(events, fn) {
    if (events == '*') callbacks = {}
    else {
      events.replace(/\S+/g, function(name) {
        if (fn) {
          var arr = callbacks[name]
          for (var i = 0, cb; (cb = arr && arr[i]); ++i) {
            if (cb._id == fn._id) { arr.splice(i, 1); i-- }
          }
        } else {
          callbacks[name] = []
        }
      })
    }
    return el
  }

  // only single event supported
  el.one = function(name, fn) {
    if (fn) fn.one = 1
    return el.on(name, fn)
  }

  el.trigger = function(name) {
    var args = [].slice.call(arguments, 1),
        fns = callbacks[name] || []

    for (var i = 0, fn; (fn = fns[i]); ++i) {
      if (!fn.busy) {
        fn.busy = 1
        fn.apply(el, fn.typed ? [name].concat(args) : args)
        if (fn.one) { fns.splice(i, 1); i-- }
         else if (fns[i] !== fn) { i-- } // Makes self-removal possible during iteration
        fn.busy = 0
      }
    }

    if (callbacks.all && name != 'all') {
      el.trigger.apply(el, ['all', name].concat(args))
    }

    return el
  }

  return el

}
;(function(riot, evt, window) {

  // browsers only
  if (!window) return

  var loc = window.location,
      fns = riot.observable(),
      win = window,
      current

  function hash() {
    return loc.href.split('#')[1] || ''
  }

  function parser(path) {
    return path.split('/')
  }

  function emit(path) {
    if (path.type) path = hash()

    if (path != current) {
  ...