I'm using the Twitter Bootstrap framework within a Rails app. What I'm trying to work out is how to render the errors within the window and not have a page reloaded.
Below is an example:
#modalEvent.modal.hide
.modal-header
%button.close{"data-dismiss" => "modal", :type => "button"} ×
%h3 Schedule Form
= form_for(#schedule, :html => { :class => "form-horizontal"}) do |f|
.modal-body
- if #schedule.errors.any?
#notice.alert.alert-error
%button.close{"data-dismiss" => "alert"} ×
%strong Error:
= pluralize(#schedule.errors.count, "error")
prohibited #{event_display(#schedule.event)} from being
saved:
%ul
- #schedule.errors.full_messages.each do |msg|
%li= msg
.widget-content.nopadding
.control-group
= f.label :event_type, :class =>'control-label'
.controls
= f.select :event, Schedule::EVENT_TYPES
#3{:style => 'display:none'}
.control-group
= f.label :name, :class =>'control-label'
.controls
= f.text_field :result_id, :class => "required error"
.control-group
= f.label :date_and_time, :class =>'control-label'
.controls
= f.text_field :time, :class => "datepicker", :required => :required, :type => :datetime, "data-date-format" =>"dd/mm/yyyy"
.control-group
= f.label :duration, :class =>'control-label'
.controls
.input-append
= f.number_field :duration, :placeholder => 'Time in Minutes', :required => :required
%span.add-on
%i.icon-time
%span.help-block Duration of event in minutes
.control-group
= f.label :arrival_time, :class =>'control-label'
.controls
.input-append
= f.number_field :arrival_time, :placeholder => 'Time in Minutes', :required => :required
%span.add-on
%i.icon-time
%span.help-block Time in minutes before event
.control-group
= f.label :location, :class =>'control-label'
.controls
= select("schedule", "location_id", Location.all.collect { |p| [p.name, p.id] }, {:include_blank => 'None'})
.control-group
= f.label :players, :class =>'control-label'
.controls
= select(:schedule, :selected_players, #players.map { |p| [full_name(p), p.id] }, {:include_blank => false}, "data-placeholder" => 'Add Players to Lineup', :prompt => 'Add Players to Lineup', :multiple => "multiple")
#1{:style => 'display:block'}
-if current_user.admin?
.control-group
= f.label :team, :class =>'control-label'
.controls
= select("schedule", "team_id", Team.all.collect { |p| [p.name, p.id] }, {:include_blank => 'None'})
- else
=f.hidden_field :team_id, :value => current_user.team_id
.control-group
= f.label :opponent, :class =>'control-label'
.controls
= select("schedule", "opponent_id", Opponent.all.collect { |p| [p.name, p.id] }, {:include_blank => 'None'})
.control-group
= f.label :home_or_away, :class =>'control-label'
.controls
= f.select :home_or_away, Schedule::HOME_OR_AWAY, {:include_blank => 'None'}
.modal-footer
= f.submit 'Save Event', :class => 'btn btn-primary'
%a.btn.btn-danger{"data-dismiss" => "modal", :href => "#"} Cancel
controller
def create
#schedule = Schedule.new(params[:schedule])
#user = User.find(current_user)
#players = User.where(:team_id => current_user[:team_id]).all
respond_to do |format|
if #schedule.save
Notifier.event_added(#user,#schedule).deliver
format.html { redirect_to(schedules_url,
:notice => "#{event_display_c(#schedule.event)} vs #{#schedule.opponent.name} was successfully created.") }
format.json { render :json => #schedule, :status => :created, :location => #schedule }
else
format.html { render :action => "new" }
format.json { render :json => #schedule.errors, :status => :unprocessable_entity }
end
end
end
If you want to avoid page reload and still show server provided error messages you have to use AJAX somehow. I think there is still no one right way of doing it. You should start by googling PJAX. Another thing you should learn about is Rails provided unobtrusive JavaScript
Also I would recommend you try out is simple_form gem, which has nothing to do with AJAX but it would simplify your views ;)
I do something very similar to this albeit using PHP/CodeIgniter instead of Rails, but I use bootstrap within a modal window for this.
What I do is, upon form submit, I ajax the form data to a process script which then validates the data - if the validation fails, it returns (via a JSON object) the control-group class containing the error and an optional error message to display. If the validation succeeds, it performs whatever action you hoped to perform and simply returns a "success" flag that signals the program to display a success message and close the modal.
Let me know if this sample is of any use to you. If it is, I can provide a sample of the server-side validation and output I perform, although it would not be in ruby.
Here's a sample form, in the bootstrap modal format:
<div class="modal-header">
<button type="button" class="close" data-dismiss="modal">×</button>
<h3>Add Something</h3>
</div>
<div class="modal-body">
<form id="your_form">
<fieldset class="control-group id-group">
<label>Some ID:</label><input type="text" name="SomeID" /><div class="clear"></div>
<label>Another ID:</label><input type="text" name="Another ID" /><div class="clear"></div>
</fieldset>
<fieldset class="control-group category-group">
<label>Category 1:</label><input type="text" name="Cat1" /><div class="clear"></div>
<label>Category 2:</label><input type="text" name="Cat2" /><div class="clear"></div>
</fieldset>
<fieldset class="control-group description-group">
<label>Description:</label><input type="text" name="Description" /><div class="clear"></div>
</fieldset>
<div class="clear"></div>
</form>
<div class="clear"></div>
<div class="alert alert-error" id="addError">
</div>
<div class="alert alert-success" id="addSuccess">
</div>
</div>
<div class="modal-footer">
Cancel
Save
</div>
Here's a sample of my javascript call upon form submit:
$("#saveSomethingButton").click(function() {
//hide any previous errors should this be a second submit
$(".alert").hide();
$(".control-group").removeClass('error');
$("input").blur();
//set the save button to a "please wait" state to prevent double submission
$("#saveSomethingButton").button('loading');
//post to the validation script - if
$.post("<?php echo base_url();?>somecontroller/processfunction", $("#your_form").serialize(), function(data) {
//if we were successful, show the happiness message and close the modal
if (data.success == 1) {
$("#addSuccess").html("<strong>Success! </strong> Something successfully added to database.");
$("#addSuccess").fadeIn(300, function() {
setTimeout(function() {
$("#someModal").modal('hide');
}, 2000);
});
//otherwise, highlight the problem fields and display the error
} else {
$("#addError").html("<strong>Error: </strong> "+data.message);
$("."+data.fieldset).addClass("error");
$("#addError").fadeIn(300);
$("."+data.fieldset+":first input:first").focus();
}
//reset the button state so that they can correct errors and submit again
$("#saveSomethingButton").button('reset');
}, "json");
//return false to prevent default form submission, which would reload the page
return false;
});
Related
Here is code sample:
<%= form_for #article, html: { class: "form-horizontal" } do |f| %>
<div class="form-group">
<%= f.label :keywords, class: 'col-md-1 control-label' %>
<div class="col-md-3">
<%= f.select :keywords, ['test_1', 'test_2', 'test_3', 'test_4', 'test_5'], {}, { :multiple => true, :size => 10, :class => 'form-control' } %>
</div>
</div>
<% end %>
When I set multiple to 'false' it works just fine, but if I set it to true (for multiple select), it just doesn't pass any data. If I have verification it gives me an "keyword is empty" error and if I remove validation - it is just empty. Any ideas?
<%= f.select :keywords, options_for_select([['test_1','test_1'], ['test_2','test_2'], ['test_3','test_3'], ['test_4','test_4'], ['test_5','test_5']]), {}, { :multiple => true, :size => 10, :class => 'form-control' } %>
I have a form like so:
.row-fluid
= form_for(#client) do |f|
- if #client.errors.any?
#error_explanation
%h2
= pluralize(#client.errors.count, "error")
prohibited this client from being saved:
%ul
- #client.errors.full_messages.each do |msg|
%li= msg
.fieldset
%legend
= #header
.control-group
%label.control-label
Name
.controls
= f.text_field :name, :class => "input-xlarge"
.control-group
%label.control-label
Street Address
.controls
= f.text_field :street, :class => "input-xlarge"
.control-group
%label.control-label
Unit
.controls
= f.text_field :unit, :class => "input-small", :placeholder => "optional"
.control-group
%label.control-label
City
.controls
= f.text_field :city, :class => "input-xlarge"
.control-group
%label.control-label
State
.controls
= f.text_field :state, :class => "input-small"
.control-group
%label.control-label
Zip
.controls
= f.text_field :zip, :class => "input-small"
.form-actions
= f.submit "Save", :class => "btn btn-primary"
.btn
= link_to "Cancel", :root, :style => "color: #333; text-decoration: none;"
and I have a modal i'm loading up with the form inside like so:
.span2.offset2
%a.btn{"data-toggle" => "modal", :href => "#myModal", :role => "button"} New Client
/ Modal
#myModal.modal.hide.fade{"aria-hidden" => "true", "aria-labelledby" => "myModalLabel", :role => "dialog", :tabindex => "-1"}
.modal-header
%button.close{"aria-hidden" => "true", "data-dismiss" => "modal", :type => "button"} ×
%h3#myModalLabel Modal header
.modal-body
%p
= render "form"
.modal-footer
%button.btn{"aria-hidden" => "true", "data-dismiss" => "modal"} Close
%button.btn.btn-primary Save changes
I'm wondering how to get the Save Changes button to submit the form. Do I need to add javascript to get it to work?
You need to make sure that the inputs and the buttons belong to one form element. This may be hard to achieve with your current markup.
Alternatively, you could submit the form with JavaScript, but the first method is much better, as you get a lot of functionality for free.
Following the directions here (and correcting for the tag issues in the code sample) I am attempting to implement a pie chart in my app. I load the initial page but the pie chart does not load on the partial (I make it into the html.erb file). A lot of things are hard coded right now so for instance Chart Type, etc do not get passed back to the controller yet.
Can someone help point out where I went awry?
Controller:
def dynamic_chart
#chart_types = ChartType.all
#user_visible_models = ["Asset","Asset Manufacturer","Asset Model", "Map","Campus","Tag","Room", "Room Monitor"]
#date_ranges = ["Day", "Week", "Month"]
end
def gen_chart
#rooms = Array.new
Room.all.each do |room|
#rooms << [room.name, room.asset_count]
end
#chart = LazyHighCharts::HighChart.new('pie') do |f|
f.chart({:defaultSeriesType=>"pie" , :margin=> [50, 200, 60, 170]} )
series = {
:type=> 'pie',
:name=> 'Asset by Rooms',
:data=> #rooms
}
f.series(series)
f.options[:title][:text] = "THA PIE"
f.legend(:layout=> 'vertical',:style=> {:left=> 'auto', :bottom=> 'auto',:right=> '50px',:top=> '100px'})
f.plot_options(:pie=>{
:allowPointSelect=>true,
:cursor=>"pointer" ,
:dataLabels=>{
:enabled=>true,
:color=>"white",
:style=>{
:font=>"13px Trebuchet MS, Verdana, sans-serif"
}
}
})
end
dynamic_chart.html.erb
<% javascript 'dynamic_chart' %>
<div id="graph_controls">
<%= form_tag('activate_reports/dynamic_chart', :class => "well form-inline") do %>
<%= text_field_tag(:name, nil, :class => "input-small", :placeholder => "Report Name") %>
<%= collection_select(:activate_report, :chart_type_id, #chart_types, :id, :name, :prompt => "Select chart") %>
<%= label_tag :x_axis, "X:", :class => "control-label" %>
<%= select :x_axis, nil, #user_visible_models, :prompt => true %>
<%= label_tag :y_axis, "Y:", :class => "control-label" %>
<%= select :y_axis, nil, #user_visible_models, :prompt => true %>
<%= select :date_range, nil, #date_ranges, :prompt => "Select a day" %>
<%= link_to("Chart!", "#", :remote => true, :class => "btn", :onclick => 'gen_chart()') %>
<% end %>
</div>
<div id="chart_area"></div>
_gen_chart.html.erb Partial:
<%= high_chart("chart_area", #chart) %>
gen_chart js function used to initiate the new chart
function gen_chart(){
jQuery.ajax({
url: "/activate_reports/gen_chart",
type: "GET",
data: {},
dataType: "html"
});
}
I have following controller:
def personalization
#title = t "generic.forms.personalization"
end
def update_personalization
begin
#user.user_data.birthdate = Date.civil(params[:user_data][:"birthdate(1i)"].to_i,params[:user_data][:"birthdate(2i)"].to_i,params[:user_data][:"birthdate(3i)"].to_i)
rescue
wrong_data = 1
end
if #user.user_data.update_attributes(params[:user_data])
if wrong_data
flash[:Error] = t "generic.messages.error.wrong_data"
redirect_to :back and return
end
flash[:Success] = t "generic.messages.success.account_updated"
redirect_to :back
else
flash[:Error] = #user.user_data.errors.full_messages.join(".<br>")
redirect_to :back
end
end
and following view:
<div id="ProfileEditForm" class="ContentBorders">
<h1 class="FormsHeading"><%= t #title %></h1>
<div class="FormsSpacing">
<%= form_for(#user.user_data, :html => { :id => "UpdateUserForm", :class => "EditForms"}, :url => {:action => 'update_personalization'}) do |f| %>
<% flash.each do |key, value| %>
<div class="FormsMargins <%= key %>"><%=raw value + "." %></div>
<% end %>
<div class="Field"><div class="LabelInline"><%= t "generic.site.first_name" %>:</div>
<%= f.text_field :first_name, :id => "EditFirstName", :class => "Rounded5", :maxlength => "30" %></div>
<div class="Field"><div class="LabelInline"><%= t "generic.site.last_name" %>:</div>
<%= f.text_field :last_name, :id => "EditLastName", :class => "Rounded5", :maxlength => "30" %></div>
<div class="Field DateSelection"><div class="LabelInline"><%= t "generic.site.birthdate" %>:</div>
<%= date_select("user_data", "birthdate", :start_year => 1901, :end_year => 2011, :include_blank => true) %>
</div>
<div class="Field GenderSelection"><div class="LabelInline"><%= t "generic.site.gender" %>:</div>
<%= f.radio_button :gender, "0", :id => "EditGenderMale" %> <span><%= t "generic.site.male" %></span>
<%= f.radio_button :gender, "1", :id => "EditGenderFemale" %> <span><%= t "generic.site.female" %></span>
</div>
<div class="Field EducationSelection"><div class="LabelInline"><%= t "generic.site.educational_level" %>:</div>
<%= f.select :education_level, options_for_select({
" " => 0, (t "generic.site.education_levels.first") => 1, (t "generic.site.education_levels.second") => 2,
(t "generic.site.education_levels.third") => 3, (t "generic.site.education_levels.fourth") => 4,
(t "generic.site.education_levels.fifth") => 5, (t "generic.site.education_levels.sixth") => 6,
(t "generic.site.education_levels.seventh") => 7 }, #user.user_data.education_level) %>
</div>
<div class="Action"><%= f.submit (t "generic.forms.update_data"), :id => "EditSubmit", :class => "ProfileEditAction Shadow1 Rounded5 AcceptButtonsBorder" %></div>
<% end %>
</div>
</div>
<%= render :partial => 'profile_panel' %>
Now. The problem is with date_select method. Each of form field works properly (data from database fills them up), except that which was generated from data_select.
If I select some proper data, and click update button, then it saves that proper data to the db. Problem comes with the moment, when it is generated, and it doesn't come with any values (it's always empty when loaded).
Any ideas, how can that be fixed?
maybe...
you have:
<%= date_select("user_data", "birthdate", :start_year => 1901, :end_year => 2011, :include_blank => true) %>
you need:
<%= f.date_select("user_data", "birthdate", :start_year => 1901, :end_year => 2011, :include_blank => true) %>
just put the "f"
<%= f.date_select ...
I'm trying to pass a string with a link_to_remote call as the :id, and the string should be collected from an input field with and id of "movie_title".
<div id="search_list">Nothing here yet</div>
<br />
<% semantic_form_for #movie do |f| %>
<% f.inputs do -%>
<%= f.input :title, :class => "movie_title" %> <%= link_to_remote( 'Search...', { :url => { :action => :imdb_search, :id => "'+$('\#movie_title').value+'" } }, { :title => "Search for this movie", :class => "imdb_search" } ) -%>
[...removed text that does not matter...]
<% end -%>
<%= f.buttons %>
<% end %>
I keep getting an javascript error, and if I remove the # from the jquery in the link, it returns "Undefined".
The link I get is:
<a class="imdb_search" href="#" onclick="jQuery.ajax({data:'authenticity_token=' + encodeURIComponent('yHPHYTZsPTQLi9JYSauUYcoie/pqPPk2uHBTN0PzNsQ='), dataType:'script', type:'post', url:'/movies/imdb_search/'+$('%23movie_title').value+''}); return false;" title="Search for this movie">Search...</a>
So I want the link updated with the contents of movie_title.
How do I do that?
I'd try something like
<%= link_to_remote( 'Search...', {
:url => { :action => :imdb_search},
:with => "'id=' + $('movie_title').value",
{:title => "Search for this movie", :class => "imdb_search"}
)
Fixed it
Used:
$('movie_title').val()
Insted of
$('movie_title').value