ails, CoffeeScript - classでラップしてネームスペース管理


経緯

  • グローバルで変数名が衝突しないよう、ネームスペース・モジュール管理をやるべきらしい。
  • JSは多用しないので、可能であればRailsサイドで解決したい。
  • 今後は意識して実践していきたいのでメモ。

手法

調べてみるといろんな手法があったが、この方法が気に入った。

ネームスペース用のディレクトリーを作る(幾つでも良い)

/javascripts
    /namespace    # ネームスペース用のディレクトリー
        base.coffee
        charts.coffee
        moving_items.coffee
        [..]
    application.js
    namespace.js  # ネームスペース設定ファイル

ネームスペースをアセットパイプラインに読み込んでもらう

/app/assets/javascripts/application.js
//= require jquery
//= require jquery_ujs
//= require jquery.turbolinks
//= require bootstrap
//= require turbolinks
//...
//= require namespace
/app/assets/javascripts/namespace.js
//= require ./namespace/base
//= require_tree ./namespace

baseファイルにネームスペース用オブジェクトを準備

/app/assets/javascripts/namespace/base.coffee
@Namespace = {}

# $(document).on "ready page:load", ->
#  $('a[href^="/documents/"]').attr('target', '_blank')

JSコードをネームスペース配下のクラスでラップする

クラス内のコードはインスタンス化されるまで待機することになる

constructorで何かを戻り値として返す例

/app/assets/javascripts/namespace/charts.coffee
# Chart.js configuration

Chart.defaults.global.tooltipEvents = ["mousemove", "touchstart", "touchmove"]
Chart.defaults.global.scaleLabel    = "<%=value%>cu.ft"

class @Components.ChartComponent

  constructor: (chartType)->
    return @createClass(chartType)

  createClass: (chartType) ->

    React.createClass
      displayName: "#{chartType}Chart"
      propTypes:
        name:    React.PropTypes.string
        data:    React.PropTypes.oneOfType([React.PropTypes.array, React.PropTypes.object])
        height:  React.PropTypes.number
        width:   React.PropTypes.number
        options: React.PropTypes.object

      getInitialState: ->
        chartInstance: null

      render: ->
        React.DOM.canvas
          ref:   @props.name
          style: { height: @props.height, width: @props.width }

      componentDidMount: ->
        @initializeChart()

      componentWillUnmount: ->
        @state.chartInstance.destroy() if @state.chartInstance

      initializeChart: ->
        canvas = React.findDOMNode(@refs[@props.name])
        ctx    = canvas.getContext("2d")
        chart  = new Chart(ctx)[chartType](@props.data)
        @setState.chartInstance = chart

constructorでイベントリスナーを登録する例

/app/assets/javascripts/namespace/moving_items.coffee
# moving_items/new, moving_items/edit

class @Namespace.MovingItems

  constructor: ->

    setVolume = (volume) -> $("#moving_item_volume").val(volume)
    setSlider = (volume) -> $("#volume_slider").val(volume)

    # Slider

    document.getElementById('volume_slider').addEventListener 'change', ->
      setVolume(document.getElementById('volume_slider').value)

    # AutoComplete

    $('#moving_item_name').autocomplete
      source: Object.keys( $('#suggestions').data('items') )
      select: (e, ui) =>
        itemVolume = $('#suggestions').data('items')[ui.item.value]
        setVolume(itemVolume)
        setSlider(itemVolume)

    $('#moving_item_room').autocomplete
      source: $('#suggestions').data('rooms')

    $('#moving_item_category').autocomplete
      source: $('#suggestions').data('categories')

HAMLの:coffeeフィルターを利用しJSコードを呼び出しインスタンス化

/app/views/movings/_chart_panel.html.haml
:coffee
  window.BarChartComponent = new Namespace.ChartComponent("Bar")
  window.PieChartComponent = new Namespace.ChartComponent("Pie")

.panel.panel-blue
  .panel-heading
    /...
  - if @total_volume > 0
    .panel-body
      .row
        .col-sm-6
          = react_component 'BarChartComponent', { name: "MovingBarChart",
            data: @dataForBarChart, height: 200, width:  400 }
        .col-sm-6
          = react_component 'PieChartComponent', { name: "MovingPieChart",
            data: @dataForPieChart, height: 200, width:  200 }

同様のことが、content_for(:javascript)= yield(:javascript)でも可能。

/app/views/layouts/application.html.haml
!!!
%html
  %head
    /...
  %body
    = yield               # ページコンテンツ
    /...
    = yield(:javascript)  # 必要なJSコードを生成する
/app/views/movings/_chart_panel.html.haml
- content_for(:javascript) do
  :coffeescript
    jQuery ->
      window.BarChartComponent = new Namespace.ChartComponent("Bar")
      window.PieChartComponent = new Namespace.ChartComponent("Pie")
/...

資料