Module: KnockoutForms::Rails::FormBuilder::Methods

Included in:
KnockoutForms::Rails::FormBuilder
Defined in:
lib/knockout_forms/rails/form_builder.rb

Constant Summary collapse

MAPPINGS =

Define attribute to bind based on input kind

{
  value: %W(text_field number_field hidden_field),
  checked: %W(check_box radio_button)
}

Class Method Summary collapse

Instance Method Summary collapse

Class Method Details

.included(form) ⇒ Object



15
16
17
18
19
20
21
22
23
24
25
26
# File 'lib/knockout_forms/rails/form_builder.rb', line 15

def self.included(form)
  # Wrap all input fields so they add a KO value data bind
  MAPPINGS.each do |bind, fields|
    fields.each do |field_name|
      form.send(:define_method, field_name) do |name, *args|
        opts = args.extract_options!
        opts['data-bind'] = "#{bind}: #{name}" unless opts.delete(:bind) == false
        super(name, *(args << opts))
      end
    end
  end
end

Instance Method Details

#action(label, action, options = {}) ⇒ Object



70
71
72
# File 'lib/knockout_forms/rails/form_builder.rb', line 70

def action(label, action, options={})
  @template.link_to(label, '#', options.merge('data-bind' => "click: #{action}"))
end

#add_item(collection_name, options = {}) ⇒ Object



46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
# File 'lib/knockout_forms/rails/form_builder.rb', line 46

def add_item(collection_name, options={})
  child_klass = object.association(collection_name).klass
  empty_child = child_klass.new

  label = options.delete(:label) || "Add new #{child_klass.model_name.singular}"
  viewmodel_collection = options.delete(:collection) || collection_name
  viewmodel = options.delete(:child_class) || child_klass.name

  # Create an empty child to inject attributes via KO mapping
  model = empty_child.to_json

  # Create new child viewmodel augmented with model attributes and
  # automatically add to viewmodel collection on click
  click_handler = options[:handler] || "    function(data, event) {\n      var viewmodel = new \#{viewmodel}(data);\n      ko.mapping.fromJS(\#{model}, {}, viewmodel);\n      \#{viewmodel_collection}.push(viewmodel);\n    };\n  JS_HANDLER\n\n  action(label, click_handler, options)\nend\n"

#fields_for(collection_name, options = {}, &block) ⇒ Object



34
35
36
37
38
39
40
41
42
43
44
# File 'lib/knockout_forms/rails/form_builder.rb', line 34

def fields_for(collection_name, options={}, &block)
  empty_child = options[:empty] || object.association(collection_name).klass.new
  collection = options[:collection] || collection_name
  # Run fields_for with a single empty child that will act as the KO template for each item
  # and use foreach data bind to delegate the iteration to KO
  @template.(:div,
      super(collection_name, [empty_child], options.merge(child_index: ""), &block),
    :'data-bind' => "foreach: #{collection}",
    :'data-collection' => collection,
    :'class' => "children-collection #{collection}-collection")
end

#select(method, choices = nil, options = {}, html_options = {}, &block) ⇒ Object

Handle select differently due to the html opts



29
30
31
32
# File 'lib/knockout_forms/rails/form_builder.rb', line 29

def select(method, choices = nil, options = {}, html_options = {}, &block)
  html_options['data-bind'] = "value: #{method}" unless options.delete(:bind) == false
  super(method, choices, options, html_options, &block)
end