Widget.coffee


TimNew

JQUERY STYLE



var attachTilesClick = function() {
  _.each(selfDashboard.sections(), function(section) {
    _.each(section.tiles(), function(tile) {
      var theTile = $("#" + tile.uniqueId);
      theTile.unbind("click");
      theTile.click(function() {
        if(tile.url) {
          window.open(tile.url);
        }
      });
    });
  });
};

PROS and CONS

  • Flexible and lightweight
  • 3rd Party Friendly

  • DOM-JS is separated
    • Complicated query to locate the elements
    • Code is still executed when element doesn't exist

  • Initialize in public functions
    • Codes for different features are mixed together
    • Codes are hard to reuse

  • Life-time management is hard in page with multiple components

EXTJS STYLE


 Ext.define('KitchenSink.view.form.FieldTypes', {
    extend: 'Ext.form.Panel',
    xtype: 'form-fieldtypes',
    frame: true,
    title: 'Form Fields',
    width: 400,
    bodyPadding: 10,
    layout: 'form',
    items: [{
        xtype: 'textfield',
        name: 'textfield1',
        fieldLabel: 'Text field',
        value: 'Text field value'
    }, {
        xtype: 'hiddenfield',
        name: 'hidden1',
        value: 'Hidden field value'
    },{
        xtype: 'textfield',
        name: 'password1',
        inputType: 'password',
        fieldLabel: 'Password field'
    }, {
        xtype: 'filefield',
        name: 'file1',
        fieldLabel: 'File upload'
    }, {
        xtype: 'textareafield',
        name: 'textarea1',
        fieldLabel: 'TextArea',
        value: 'Textarea value'
    }, {
        xtype: 'displayfield',
        name: 'displayfield1',
        fieldLabel: 'Display field',
        value: 'Display field <span style="color:green;">value</span>'
    }, {
        xtype: 'numberfield',
        name: 'numberfield1',
        fieldLabel: 'Number field',
        value: 5,
        minValue: 0,
        maxValue: 50
    }, {
        xtype: 'checkboxfield',
        name: 'checkbox1',
        fieldLabel: 'Checkbox',
        boxLabel: 'box label'
    }]
});

PROS & CONS 


  • Powerful framework
    • Included most common scenarios
    • Feature Rich
    • Predesigned Widgets

  • Everything is in JavaScript
    • No HTML, No CSS
    • Hard to customize
  • Extremely Heavy
  • Extremely Intrusive 
  • Hard to debug

AGULAR JS


<html lang="en" ng-app>
<head>
  <meta charset="utf-8">
  <title>My HTML File</title>
  <link rel="stylesheet" href="bower_components/bootstrap/dist/css/bootstrap.css">
  <link rel="stylesheet" href="css/app.css">
  <script src="bower_components/angular/angular.js"></script>
</head>
<body>

  <p>Nothing here {{'yet' + '!'}}</p>

</body>
</html>

PROS AND CONS


  • Very Powerful
  • Extensible
  • 3rd party friendly
  • Code and DOM bounded
  • Reusable

  • Very heavy to use
  • Learning curve is cliffy
  • Hard to debug



Widget.coffee


http://bower.io/search/?q=widget.coffee



WHY Widget.coffee 


  • Designed for Page 
    • Not for One-Page-Application
  • Light Weight
  • Non-intrusive
  • Easy to debug
  • Simple to learn


FEatures


  • DOM Binding
  • Life Cycle Management
  • Relationship Management
  • Event Binding

HTML

<html>
  <head>    
    <script type="text/javascript" src="jquery.js"></script>
    <script type="text/javascript" src="widget.js"></script>
    <script type="text/javascript" src="sample_widgets.js"></script>
  </head>
  <body>
    <div data-widget="Page">
      <button data-action-handler='resetAll'>Reset All</button>
      <hr>
      <div data-widget="Timer" data-widget-part='timer'>
        <div class="time">no record!</div>
        <button class="toggle-button">start</button>
        <button class="lap-button">lap</button>
        <button class="reset-button">reset</button>
      </div>
      <hr>
      <div data-widget="ScoreBoard">
        <div>History</div>
        <ol data-widget-part="scoreList"></ol>
        <button data-action-handler="clean">Clean</button>
      </div>  
    </div>
  </body>
</html>

CoffeeScript

# sample_widget.coffee

class @Timer extends Widget
  bindDom: ->
    @timeSpan = @element.find('.time')
    @toggleButton = @element.find('.toggle-button')
    @lapButton = @element.find('.lap-button')
    @resetButton = @element.find('.reset-button')
    @scoreBoard = Widget.findWidgetByType('ScoreBoard')

  enhancePage: ->        
    @toggleButton.click @toggle
    @lapButton.click @lap
    @resetButton.click @reset

  initialize: ->
    @running = false
    @refreshButtonStatus()
    @reset()

  tick: =>
    @time++
    @refreshTime()

  refreshTime: ->
    @timeSpan.text("#{@time} ms")

  toggle: =>       
    @controlTimer !@running

  controlTimer: (running) ->
    @running = running

    @refreshButtonStatus()

    if @running
      @time = 0
      @refreshTime()
      @token = setInterval(@tick, 1)
    else
      if @token?
        clearInterval(@token) 
        delete @token

  refreshButtonStatus: ->
    if @running
      @toggleButton.text('Stop')        
      @lapButton.removeAttr('disabled')        
      @resetButton.attr('disabled', 'disabled')
    else
      @toggleButton.text('Start')  
      @lapButton.attr('disabled', 'disabled')
      @resetButton.removeAttr('disabled')  

  lap: =>
    @scoreBoard.addScore(@time)

  reset: =>
    delete @time 
    @timeSpan.text('no record!')

class @ScoreBoard extends Widget
  bindDom: ->
    @bindWidgetParts()

  enhancePage: ->
    @bindActionHandlers()

  addScore: (score) ->
    $('>/code>>li>>code>').text("#{score} ms").appendTo(@scoreList)

  clean: ->
    @scoreList.html('')


class @Page extends Widget
  bindDom: ->
    @scoreBoard = @findSubWidgetByType('ScoreBoard')
    @bindWidgetParts()

  enhancePage: ->
    @bindActionHandlers()

  resetAll: ->
    @timer.controlTimer(false)
    @timer.reset()
    @scoreBoard.clean() 

Debug


Widget.findWidgetByType('Timer').toggle()





Showcase


THANKS

TimNew
http://timnew.me

widget.coffee

By timnew

widget.coffee

  • 95