3.1.0 • Published 9 years ago

ccnq-ko v3.1.0

Weekly downloads
-
License
Unlicense (http:/...
Repository
-
Last release
9 years ago

Usage

In the main application:

ko = require 'knockout'
{Widget,widget} = (require 'ccnq-ko-widget') ko
html = ->
  widget 'foo'
ko.applyBindings foo: new Widget (...)

In the widget:

module.exports = (require 'ccnq-ko').widget 'widget-tag', ->

  # data-model/constructor
  @data (value) -> @field = @ko.observable value
  # @constructor is a synonim for @data

  @view ({$root,data}) ->
  # any behavior
  # the fields of the constructor are already injected
  # the data object itself is available as {data}, {value} and {doc}

  @html ({div}) ->   # teacup+databind as parameter

Widget creation

The name provided is expected to be the HTML tag, it should be dash-separated: my-widget

widget = (tag_name,f) ->

The class name is: MyWidget

  re = /(^.|-.)/gi
  class_name = tag_name.replace re, (x) -> (x.substr x.length-1).toUpperCase()

The Teacup tag name: my_widget

  re = /-/g
  widget_name = tag_name.replace re, '_'

The function provided to widget is called with a small DSL:

  ctx =
    tag_name: tag_name
    class_name: class_name
    widget_name: widget_name

  ctx._tag = (f) ->
    {tag} = teacup
    tag tag_name, params: "value:#{f},$root:$root"
  • data is the constructor for the data model; ideally it should work with ko.toJS

    ctx.data = ctx.constructor = (data) ->
      ctx._data = data
  • view is the view model (behavior); the root of this is already populated with copies of the content of the object set by data

    ctx.view = (view) ->
      ctx._view = view
  • html is the content as a Teacup template; teacup+databind is provided as parameter

    ctx.html = (html) ->
      ctx._html = html

The widget module returns a function that must be called with the instance of Knockout

  (ko) ->

    init ko

    ctx.ko = ko
    f.call ctx, ko

If no data is provided the default behavior is to inject observables. FIXME: Replace with ko.mapping?

    ctx._data ?= class DefaultData
      constructor: (value) ->
        if typeof value is 'object'
          for own k,v of value
            this[k] = ko.observable v
        else
          @value = ko.observable value

If no view is provided the default behavior is to do nothing.

    ctx._view ?= ->

If no html is provided the default behavior is to output a comment that it wasn't provided.

    ctx._html ?= -> "<!-- No html was provided in #{tag_name} -->"

    if ko.components.isRegistered tag_name
      console.log "Not registering #{tag_name} twice."
    else
      ko.components.register tag_name,
        viewModel: ({value,$root}) ->
          for own k,v of value
            this[k] = v
          ctx._view.call this, {value,$root,ko,data:value,doc:value}
        template: teacup: ctx._html

    res = {tag_name,widget_name,class_name}

and returns a new Teacup widget

    res[widget_name] = ctx._tag

and the data-model constructor

    res[class_name] = ctx._data
    res

Loaders for Knockout

init = (ko) ->
  return if ko.__ccnq_init
  ko.__ccnq_init = true

  loader =

This allows us to dynamically render Teacup template. This solves for example problems with non-unique names for radio-buttons across multiple renderings.

    loadTemplate: (name, config, next) ->
      if config.teacup?
        html = teacup.render config.teacup, teacup
        ko.components.defaultLoader.loadTemplate name, html, next
      else
        next null

  ###
    loadViewModel: (name, config, next) ->
      if config.special
        constructor = ->
          config.special.apply this, arguments
        ko.components.defaultLoader.loadViewModel name, constructor, next
  ###

  ko.components.loaders.unshift loader

teacup = require 'teacup'
teacup.use (require 'teacup-databind')()

module.exports = widget
module.exports.widget = widget
3.1.0

9 years ago

3.0.0

9 years ago

2.0.1

9 years ago

2.0.0

9 years ago

1.1.0

9 years ago

1.0.0

9 years ago