Showing posts with label scriptaculous. Show all posts
Showing posts with label scriptaculous. Show all posts

Tuesday, August 9, 2011

How to create scriptaculous ajax inplace editor fields in Rails3 with validation

First, we should do our html view in app/views/ips/index.html:

<div id="ips_table_div">
<%= render "ips_table" %>
</div>

Then, we create the table and the javascript code in our partial view (app/views/ips/_ips_table.html.erb).

<table cellpadding="10"><tbody>

<% unless @ips.nil? %>

		<tr>

			<td id="ip_new_ip_td_div">
				<%= form_tag url_for(:controller => :ips,
							:action	=> :create_ip),
						:remote => true,
						:id 	=> "new_ip" %>
					
						<%= text_field_tag "ip" %>
						<%= submit_tag t(:add_button, :scope => [:application]), :id => "new_ip_submit" %>
				</form>
			</td>

			<td>
			</td>

		</tr>

	<% @ips.each do |ip| %>
		<tr>

			<td id="ip_ip_td_div_<%=ip.id %>">
				<div id="ip_ip_text_div_<%=ip.id %>"><%= ip.ip %></div>
			</td>

			<td>
				<%= link_to t(:delete_link, :scope => [:application]),
 								url_for(:controller => :ips,
									:action	=> :delete_ip,
									:id 	=> ip.id),
								:remote => true,
								:confirm => t(:ip_delete_confirm, :scope => [:application])%>
			</td>

		</tr>
	<% end %>

<% end %>

</tbody></table>

<script type="text/javascript">
var ip_validate_background_color_error = "#fff999";
var ip_validate_border_color_error = "#f80000";
var ip_validate_background_color = "#ffffff";
var ip_validate_border_color = "#999999";

var ipModifyFormOptions= $H({
		cancelText:'<%=t :cancel_link, :scope => [:application] %>',
		okText: 'ok',
		savingText: '<%=t :saving_text, :scope => [:application] %>',
		loadingText: '<%=t :loading_text, :scope => [:application] %>',
		clickToEditText: '<%=t :click_to_edit_text, :scope => [:application] %>',
		
});

<% unless @ips.nil? %>

Form.findFirstElement('new_ip').style.borderColor = ip_validate_border_color;
Form.findFirstElement('new_ip').style.backgroundColor = ip_validate_background_color;
Form.findFirstElement('new_ip').setAttribute("onkeypress","return input_filter_exact(event, 'ip')");
$('new_ip_submit').disabled=true;
new Form.Observer('new_ip', 0.3, function(form, value) {
		if(validateIpAddress(Form.findFirstElement('new_ip').value, false) == true) {
				Form.findFirstElement('new_ip').style.borderColor = ip_validate_border_color;
				Form.findFirstElement('new_ip').style.backgroundColor = ip_validate_background_color;
				$('new_ip_submit').disabled=false;
		}	else {
				Form.findFirstElement('new_ip').style.borderColor = ip_validate_border_color_error;
				Form.findFirstElement('new_ip').style.backgroundColor = ip_validate_background_color_error;
				$('new_ip_submit').disabled=true;
		}
});


	<% @ips.each do |ip| %>

new Ajax.InPlaceEditor('ip_ip_text_div_<%=ip.id %>', '<%= url_for :controller => :ips, :action => :modify_ip, :id => ip.id	%>', ipModifyFormOptions.merge({
	formId: 'ip_ip_form_<%=ip.id %>',
	onEnterEditMode: function(form, value) {
			setTimeout(function() {
					Form.findFirstElement('ip_ip_form_<%=ip.id %>').style.borderColor = ip_validate_border_color;
					Form.findFirstElement('ip_ip_form_<%=ip.id %>').style.backgroundColor = ip_validate_background_color;
					Form.findFirstElement('ip_ip_form_<%=ip.id %>').setAttribute("onkeypress","return input_filter_exact(event, 'ip')");
					new Form.Observer('ip_ip_form_<%=ip.id %>', 0.3, function(form, value) {
						if(validateIpAddress(Form.findFirstElement('ip_ip_form_<%=ip.id %>').value, false) == true) {
							Form.findFirstElement('ip_ip_form_<%=ip.id %>').style.borderColor = ip_validate_border_color;
							Form.findFirstElement('ip_ip_form_<%=ip.id %>').style.backgroundColor = ip_validate_background_color;
							$('ip_ip_form_<%=ip.id %>').elements[1].disabled=false;
						}	else {
							Form.findFirstElement('ip_ip_form_<%=ip.id %>').style.borderColor = ip_validate_border_color_error;
							Form.findFirstElement('ip_ip_form_<%=ip.id %>').style.backgroundColor = ip_validate_background_color_error;
							$('ip_ip_form_<%=ip.id %>').elements[1].disabled=true;
						}
				});
			}, 1000);
	}
}).toObject());

	<% end %>

<% end %>
</script>

Then we should make the controller (app/controllers/ips_controller.rb):

respond_to :js, :only => [:delete_ip, :create_ip]
respond_to :html, :except => [:delete_ip, :create_ip]
layout :application, :except => [:delete_ip, :create:ip, :modify_ip]
layout false, :only => [:delete_ip, :create:ip, :modify_ip]

def index
  @ips=Ip.all
  respond_with(@ips)
end

def delete_ip
  Ip.destroy(params[:id])
  # it will returns a javascript code by default, so have to do the
  # app/views/ips/delete_ip.js.erb, what will loads the table completely
 @ips=Ip.all
  respond_with(@ips)
end

def create_ip
  ip=Ip.new(params[:ip]).save
  # it will returns a javascript code, so have to do the
  # app/views/ips/create_ip.js.erb, what will loads the table completely
  @ips=Ip.all
  respond_with(@ips)
end

def modify_ip
  ip=Ip.find(params[:id])
  ip.update_attribute(:ip, params[:value])
  # don't forget some validations here also
  # it will returns a html code, so have to do the
  # app/views/ips/modify_ip.html.erb, what will contains the new text only, or:
  respond_to do |format|
    format.html { render :text => ip.ip }
  end
end

Then we can create returned javascript codes app/views/ips/create_ip.js.erb and delete_ip.js.erb:

  $("ips_table_div").innerHTML='';
  $("ips_table_div").update("<%=escape_javascript(render(:template => "/ips/_ips_table.html.erb", :locals => {:ips => @ips})) %>"); 

Finally we must define javascipt validation functions in public/apllication.js:

function validateIpAddress(value, can_be_empty) {
    if (arguments.length == 1) {
        can_be_empty = false;
    }
    if (can_be_empty == true) {
        if (value == null) {
            return false;
        }
        if (value == "") {
            return true;
        }
    }
    if (value.match(/\b(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\b/))
    {
        if (value.length>0 && value.length<256)
        {
            return true;
        }
        else
        {
            return false;
        }
    }
    else
    {
        return false; 
    }
}

function input_filter_exact(e, type) {
    var enabled_chars;    
    switch (type)
        {
            case "ip": enabled_chars="0123456789.";
                                    break;
            default: enabled_chars="-/.,_?[]()!abcdefghijklmnopqrstvuwxyzABCDEFGHIJKLMNOPQRSTVUWXYZ0123456789éáőúűóüöíÉÁŐÚÓÜÖÍ ";
        }
    var key;
  var keychar;
    if (window.event)
        key = window.event.keyCode;
  else if (e)
        key = e.which;
    else
        return true;
    keychar = String.fromCharCode(key);
    if ((key==null) || (key==0) || (key==8) || (key==9) || (key==13) || (key==27) )
        return true;
    else if (((enabled_chars).indexOf(keychar) > -1))
        return true;
    else
    return false;
} 


That's all. Works with Rails 3.0.9, Prototype 1.7 and script.aculo.us v1.8.3

Wednesday, May 18, 2011

Scriptaculous Slider in Rails3 form

So there is an excellent slider. http://madrobby.github.com/scriptaculous/slider/ It is perfect to set up a select form element where values are predefined numbers or integers. First you have to download http://script.aculo.us/downloads the slider.js file and move it to the /public/javascripts directory. Then insert this row in your layout, /app/views/layouts/application.html.erb after the defaults.

<%= javascript_include_tag "slider.js" %>

Then we define CSS style for the slider as the same as at the script.aculo.us homepage. (/app/public/stylesheets/style.css)

div.slider {
 width:256px;
 margin:10px 0;
 background-color:#ccc;
 height:10px;
 position: relative;
}

div.slider div.handle {
 width:10px;
 height:15px;
 background-color:#f00;
 cursor:move;
 position: absolute;
}

Then we create a helper for it. (/app/helpers/application_helper.rb)

def slider(attributes={})
  unless attributes.nil? then
  values=attributes[:values]
  default_value=attributes[:default_value].to_s
  object=attributes[:object].to_s
  instance=attributes[:instance].to_s
  unit=attributes[:unit] || ""
  text_div_id=object + "_" + instance + "_slider_text_div"
  slider_id=object + "_" + instance + "_slider"
        
  concat(raw(
    "<div id=\"#{slider_id}\" class=\"slider\">
       <div class=\"handle\"></div>
     </div>"
  ))

  concat(raw(hidden_field(object, instance, :value => default_value.to_i)))
  concat(raw("<div id=\"#{text_div_id}\">" + default_value + " " + unit + "</div>"))
        
  js_text = <<JS3
  (function() {
    var zoom_slider = $('#{slider_id}');
    var text_div = $('#{text_div_id}');
    var hidden_item = $('#{object + "_" + instance}');
    var values=#{values.inspect};
    
    var values_length = values.length;
    var min=40;
    var max=200;

    var unit = Math.ceil((max - min) / values_length);
    var range = $R(min, max - unit);

    var pix_values = new Array();
    for(i=1;i<=values_length;i++){
      pix_values[i-1]=min + (unit * (i - 1));
    }
        
    var default_value=pix_values[#{values.index(default_value.to_i)}];

    new Control.Slider(zoom_slider.down('.handle'), zoom_slider, {
      range: range,
      sliderValue: default_value,
            increment: unit,
            values: pix_values,
      onSlide: function(value) {
                text_div.innerHTML=values[pix_values.indexOf(value)] + " #{unit}";
                hidden_item.value=values[pix_values.indexOf(value)];
      },
      onChange: function(value) { 
                text_div.innerHTML=values[pix_values.indexOf(value)] + " #{unit}";
                hidden_item.value=values[pix_values.indexOf(value)];
      }
    });
  })();
        
JS3
      concat(raw(javascript_tag(js_text)))
  end
end

Then just define your form element in your view / form:

<table><tbody><tr><td>
<% slider :object        => "config",
          :instance      => "mem_size",
          :unit          => "MB",
          :values        => [512, 1024, 1536, 2048, 2560, 3072, 4096].sort,
          :default_value => 1024 %>
</td></tr></tbody></table>

That"s all! Questions?