Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Build a Form Builder with Ruby and JQuery

I'm looking for some advice on designing the above for a RoR application that basically has two sections:

1) Admin Form Builder 2) End User Form (that was created by the admin in step 1)

I've prototyped the JQuery form (end result) I want and it uses "progressive disclosure" (similar to Yehuda Katz's Bamboo example in the JQuery in Action book), so if you click one item another item might open (eg click the "registration checkbox" and another registration sub-form is displayed). I also have the rails application actually building a couple of inputs as described. However, I feel like I'm not really utilizing the dynamic capabilities of Ruby. For example, here's a slice of one of my module methods that builds a particular simple html input:

module Levin 
  module EventReg 
    module Jquery

  def self.simple_registration_text(input_params)
    raise ArgumentError if input_params.nil?
    ip_id = input_params[:input_div_id] 
    simple_reg_input_html = '<p><label for="'
    simple_reg_input_html << "#{ip_id + '_label_id'}\">"
    ........<A BUNCH MORE CODE TO BUILD INPUT>........
    simple_reg_input_html << ' class="required"' unless input_params[:is_required].eql?("0")    
    simple_reg_input_html << '/></p>'
  end

Basically, the administrator can name the input item and I use that for the div id's, etc. Given that I have many input types (select drop downs, textareas, inputs with children like an input type text with a checkbox that, when checked, opens a data grid, that can add/remove items via a dialog popup form, etc.), I'd like to avoid "hard-wiring" build methods like in my example above. Also, it would be great to allow the caller to be responsible for BOTH the input value choices (which is already happening when they pass input params parameter), and ALSO the builder itself.

I'm very new to functional programming but I'm thinking of something to the affect of the caller passing in a "builder" lambda that builds the specific form input they need using the input params leading to:

builder_lambda.call(input_params)

But when I think about this, it seems the lambda will look very similar to what the Jquery module is already doing. Yes, the caller will have the ability to create a custom builder to their liking, but then what is my module method gonna really do! What value will it provide?

I will also sometimes need to connect these inputs to specific event handlers. Perhaps there could be an array of lamdas, a builder and an add event handler like:

builder_lambda.call(input_params)

add_event_handlers_lambda.call(input_params)

Am I on the right track? Sorry if I'm rambling on, but I'm obviously having trouble decomposing this problem into a "Ruby-istic" solution and ideas for a more dynamic approach would be greatly appreciated.

EDIT: This was helpful for me: Obie Fernandez DSL Development PDF

like image 679
Rob Avatar asked Nov 15 '22 15:11

Rob


1 Answers

It might sound to counter your idea, but I think you are struggling with this because part of what you want is reinventing the wheel (creating a meta-language where the frameworks you're using already provide the tools).

My approach would be something along the lines:

Create HTML templates for your partials (in your view) using some kind of a javascript template engine (my choice: Handlebars or jQuery.tmpl ). No builders yet.

<script id="form-partial-registration" type="text/x-handlebars-template">
 <div class="registration_field field">
  <label for="{{field_id}}" id="label_for_{{field_id}}" class="label registration"></label>
  <input id="{{field_id}}" />
 </div>
</script> 

then in your javascript simply compile the template

var form_builder = {templates:{}};
function compile_template(template_id, key) {
   form_builder.templates[key] = Handlebars.compile($("#" + template_id).html());
}
function render_template(template_id, data) {
   form_builder.templates[key](data);
}

// other stuff

compile_template('form-partial-registration', 'registration')

then you have a collection of every piece of HTML code needed for your forms, clearly written in HTML instead of javascript. Then use them:

function build_some_form(has_registration, has_expiration, event_handlers) {
  var form_content = [];
  var form_prefix = '...some_prefix...'

  // something optional
  if (has_registration) { 
    form_content.push( render_template('registration', {field_id:form_prefix + '_reg'}) )
  }

  // another optional 
  if (has_expiration) { 
    form_content.push( render_template('expiration', {field_id:form_prefix + '_exp'}) )
  }

  // put it into the form
  $('#some_form').append(form_content.join(''));


  // hook up handlers, in this example for onclick only
  for(var k in event_handlers) {
    $('#some_form #' + form_prefix + '_' + k ).click(event_handlers[k]);
  }
}

build_some_form(true, false, {reg: function() { alert('registration clicked') })

My point is: try focusing on the "business logic" of your application, and aim for simplicity for the rest (to paraphrase Joel Spolsky: care deeply for your core competence and try to outsource/fake the rest).

Of course this is just a very rough draft, the approach I would take and refine it from here (there are a couple of repetitions here, that could be refactored).

like image 199
galileoMonkey Avatar answered Nov 24 '22 01:11

galileoMonkey