Module: AttributeFu::AssociatedFormHelper

Defined in:
lib/attribute_fu/associated_form_helper.rb

Overview

Methods for building forms that contain fields for associated models.

Refer to the Conventions section in the README for the various expected defaults.

Instance Method Summary collapse

Instance Method Details

Creates a link that adds a new associated form to the page using Javascript.

Must be called from within an associated form.

Must be provided with a new instance of the associated object.

e.g. f.add_associated_link 'Add Task', @project.tasks.build

An options hash can be specified to override the default behaviors.

Options are:

  • :partial - specify the name of the partial in which the form is located.

  • :container - specify the DOM id of the container in which to insert the new element.

  • :expression - specify a javascript expression with which to select the container to insert the new form in to (i.e. $(this).up(‘.tasks’))

  • :name - specify an alternate class name for the associated model (underscored)

Any additional options are forwarded to link_to_function. See its documentation for available options.



80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
# File 'lib/attribute_fu/associated_form_helper.rb', line 80

def add_associated_link(name, object, opts = {})
  associated_name  = extract_option_or_class_name(opts, :name, object)
  variable         = "attribute_fu_#{associated_name}_count"
  
  opts.symbolize_keys!
  partial          = opts.delete(:partial)    || associated_name
  container        = opts.delete(:expression) || "'#{opts.delete(:container) || associated_name.pluralize}'"
  
  form_builder     = self # because the value of self changes in the block
  
  @template.link_to_function(name, opts) do |page|
    page << "if (typeof #{variable} == 'undefined') #{variable} = 0;"
    page << "new Insertion.Bottom(#{container}, new Template("+form_builder.render_associated_form(object, :fields_for => { :javascript => true }, :partial => partial).to_json+").evaluate({'number': --#{variable}}).gsub(/__number_/, #{variable}))"
  end
end

#fields_for_associated(associated, *args, &block) ⇒ Object

Works similarly to fields_for, but used for building forms for associated objects.

Automatically names fields to be compatible with the association_attributes= created by attribute_fu.

An options hash can be specified to override the default behaviors.

Options are: :javascript - Generate id placeholders for use with Prototype’s Template class (this is how attribute_fu’s add_associated_link works). :name - Specify the singular name of the association (in singular form), if it differs from the class name of the object.

Any other supplied parameters are passed along to fields_for.

Note: It is preferable to call render_associated_form, which will automatically wrap your form partial in a fields_for_associated call.



21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
# File 'lib/attribute_fu/associated_form_helper.rb', line 21

def fields_for_associated(associated, *args, &block)
  conf            = args.last.is_a?(Hash) ? args.last : {}
  associated_name = extract_option_or_class_name(conf, :name, associated)
  name            = associated_base_name associated_name
  
  unless associated.new_record?
    name << "[#{associated.new_record? ? 'new' : associated.id}]"
  else
    @new_objects ||= {}
    @new_objects[associated_name] ||= -1 # we want naming to start at 0
    identifier = !conf.nil? && conf[:javascript] ? '#{number}' : @new_objects[associated_name]+=1
    
    name << "[new][#{identifier}]"
  end
  
  @template.fields_for(name, *args.unshift(associated), &block)
end

Creates a link for removing an associated element from the form, by removing its containing element from the DOM.

Must be called from within an associated form.

An options hash can be specified to override the default behaviors.

Options are:

  • :selector - The CSS selector with which to find the element to remove.

  • :function - Additional javascript to be executed before the element is removed.

Any remaining options are passed along to link_to_function



51
52
53
54
55
56
57
58
59
60
# File 'lib/attribute_fu/associated_form_helper.rb', line 51

def remove_link(name, *args)
  options = args.extract_options!

  css_selector = options.delete(:selector) || ".#{@object.class.name.split("::").last.underscore}"
  function     = options.delete(:function) || ""
  
  function << "$(this).up('#{css_selector}').remove()"
  
  @template.link_to_function(name, function, *args.push(options))
end

#render_associated_form(associated, opts = {}) ⇒ Object

Renders the form of an associated object, wrapping it in a fields_for_associated call.

The associated argument can be either an object, or a collection of objects to be rendered.

An options hash can be specified to override the default behaviors.

Options are:

  • :new - specify a certain number of new elements to be added to the form. Useful for displaying a few blank elements at the bottom.

  • :name - override the name of the association, both for the field names, and the name of the partial

  • :partial - specify the name of the partial in which the form is located.

  • :fields_for - specify additional options for the fields_for_associated call

  • :locals - specify additional variables to be passed along to the partial

  • :render - specify additional options to be passed along to the render :partial call



111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
# File 'lib/attribute_fu/associated_form_helper.rb', line 111

def render_associated_form(associated, opts = {})
  associated = associated.is_a?(Array) ? associated : [associated] # preserve association proxy if this is one
  
  opts.symbolize_keys!
  (opts[:new] - associated.select(&:new_record?).length).times { associated.build } if opts[:new]

  unless associated.empty?
    name              = extract_option_or_class_name(opts, :name, associated.first)
    partial           = opts[:partial] || name
    local_assign_name = partial.split('/').last.split('.').first

    associated.map do |element|
      fields_for_associated(element, (opts[:fields_for] || {}).merge(:name => name)) do |f|
        @template.render({:partial => "#{partial}", :locals => {local_assign_name.to_sym => element, :f => f}.merge(opts[:locals] || {})}.merge(opts[:render] || {}))
      end
    end
  end
end