I've been scratching my brain (kinda painful, wouldn't advise it) trying to figure out a way to pull this off, but I haven't been able to come up with anything that really seems feasible.
The idea is this: I have a page that allows you to create an event. This event could be anything from a wedding to a marathon or fund raiser. Different events need to have a form specific to that event type, e.g. the wedding event needs the name of the bride, groom, address, receptions, etc.
What I'm trying to figure out is how to allow them to select an event type, and then go to localhost:3000/events/create(/:event_type) and have that view load in the correct form fields.
My original thought was to have event types stored in a database with each form stored as JSON, but this just seems more complicated than I'd think necessary.
Any ideas?
Here's routes.rb so far:
get 'my_events/', to: 'events#index', as: :events
get 'events/create', to: 'events#create', as: :new_event
match 'events/new', to: 'events#new', as: :post_new_event, via: :post
get 'event/:slug', to: 'events#show', as: :show_event
root 'static#index'
devise_for :users, :controllers => { :omniauth_callbacks => 'omniauth_callbacks' }
And the events controller:
class EventsController < ApplicationController
def index
#events = Event.all()
end
def create
#event = Event.new()
end
def show
#event = Event.find_by slug: params[:slug]
end
def delete
end
end
You can generate a form with tags to match any number of columns without knowing the column names like this
<%= form_for #event do |f| %>
<% #event.attributes.each do |k,v| %>
<% unless k == 'id' %> #in the case of an edit form, the id should not be changable
<p>
<%= f.label k.to_sym %><br> #get the name of the column, put it into a symbol
<%= f.text_field k.to_sym %> #same as above, will put the matching value in if it exists
</p>
<% end %>
<% end %>
<%= f.submit %>
<% end %>
Though this may not be suited for how your model appears to be set up, a single Event database that can hold any event. This would work better if you had a specific database for each type.
To go in to detail on how I would see this working for you...
Your planned route /events/create/:event_type would work
in your new action in the controller
def new
if params[:event_type] == 'wedding'
#event = Wedding.new #would need to do the same for each event type
end #a model/database for each different type of event
end
However if event is a parent Event database to a child event type, you may be able to make it work, though I'm not familiar enough with relational databases in Ruby on Rails to say how.
This is a fairly common pattern. On your edit/new form(or modal), you need to have a javascript handler on your event_id field. When it changes, you do an ajax request to your server to get the valid event_types for that event so that you may populate the event_types input control.
I would strongly discourage doing any client side storage (hard coding) of the event_types as this design will not allow for easy addition of future events.
Related
I have a Patient model. And I want be able as a Patient adds multiple Doctors.
Usually how I do it by creates with nested_form for Patient(without any of his attributes)
# #patient = current_patient
= simple_nested_form_for #patient do |form|
= form.simple_fields_for :doctors, #patient.doctors do |f|
= f.input :first_name
= f.input :last_name
= form.link_to_add :add, :doctors
= form.button :submit
And I am using PatientsController *#update* action to add and remove doctors from them.
I have already one PatientsController with edit action to edit Patient information.
I think right controller to add multiple Doctors to Patient should be DoctorsController. But this will not work with form above which always update Patient object.
So how this form should looks like? What is correct pattern for that?
Gem: https://github.com/ryanb/nested_form
Checkout Ryan's railscast #196 Nested Model Form (revised) where he shows what you are trying to do. Basically, instead of Patient and Doctors he uses Question and Answers. Jump to minute 10:00 to see it in action and confirm that is what you are looking for. The correct controller is Patients in this example.
I'm designing a multi dynamic select menus, i.e., I have a menu for brands, after the user select the brand, using JavaScript and AJAX, I will search for the models available from that brand and add them to the second select menu. This process repeats again but this time showing the features of the model selected.
To do this, and because I have many different areas that need the same system, I use a class with the same name in every brand select menu and another one to every model select menu.
<div class='brand_select' id='14'>
<%= f.collection_select :brand, Product.find_all_by_area(14, :group => 'brand'), :brand, :brand, :prompt => 'Choose brand' %>
</div>
<div class='model_select'>
<%= f.collection_select :model, Product.find_all_by_area(14), :model, :model, :prompt => 'Choose model' %>
</div>
<div class='brand_select' id='15'>
<%= f.collection_select :brand, Product.find_all_by_area(15, :group => 'brand'), :brand, :brand, :prompt => 'Choose brand' %>
</div>
<div class='model_select'>
<%= f.collection_select :model, Product.find_all_by_area(15), :model, :model, :prompt => 'Choose model' %>
</div>
And the JavaScript:
$('.brand_select').change(function(event) {
// option selected
var brand=$(event.target).find('option:selected').val();
// if none is selected
if (brand == ''){
$(event.target).parent().parent().find('.modelo_select').hide();
$(event.target).parent().parent().find('.caracteristica').hide();
}
else {
$(event.target).parent().parent().find('.modelo_select').show();
// find id to search on the database
var id=$(event.target).parent().attr('id');
// find the target (id of the object)
var target=$(event.target).attr('id');
$.ajax({
type: "POST",
url: "http://"+location.host+"/model/"+brand+"/"+id+"/"+target,
brand: brand,
id: id,
target: target
});
}
});
$('.model_select').change(function(event) {
// find model selected to search on the database
var model=$(event.target).find('option:selected').val();
// find brand selected to search on the database
var brand=$(event.target).parent().parent().find('.marca_select').find('option:selected').val();
// find id to search on the database
var id=$(event.target).parent().parent().find('.marca_select').attr('id');
// find the target (id of the object)
var target=$(event.target).attr('id');
$.ajax({
type: "POST",
url: "http://"+location.host+"/feature/"+brand+"/"+model+"/"+id+"/"+target,
brand: brand,
model: model,
id: id,
target: target
});
});
This code works but it repeats the event change the same number of times as the classes with that name.
What I want to do is for the function to run only one time every time a change event is called for the class.
I don't know if this is possible with class structure that I have or if I have to associate an id or a class with different names for each area to the function.
I don't see why the event should fire twice because all you're doing with $(selector).change is saying that every time a change event fires on something with that selector you want to handle it. I even ran a quick test to be sure and it doesn't fire more than once.
Can you explain a bit better what the symptom actually is? As in, what actually happens twice? Does everything in your event handler happens twice?
I was thinking that your selectors for the actions you perform on the parents might be a bit too lax ($(event.target).parent().parent()) so if you only want to do something on the container where your event was fired that wouldn't be the best way (but then again I don't know what your end purpose is here).
That for your help, I found out that the problem has nothing to do with Javascript but is instead on the Ruby on Rails.
I was adding on application.html.erb other js files and if you have the //= require_tree on the application.js it adds every js file in the tree, so adding js files on application.html.erb will make them repeat and cause strange behaviors like this one.
So I'm creating a photo proofing web app for a client. I want him to be able to skim the images on the site and under each image is an 'Approve' button, which he can click and the image border will turn green, signaling it's good for export.
Now my JS/Jquery knowledge is pretty limited, but I know that this might go deeper than just front-end work, because I want to see those changes even after browser is closed, which I think requires a both back-end and front-end solution.
I'm thinking to create a boolean attribute under my image model, and when you click the 'Approve' button, it'll switch the boolean value to true, which will change the css class to turn green. Is there a way rails can detect a boolean value and change the css accordingly? I want to be able to see the changes my client made.
Would appreciate any feedback/advice on my approach, or if there's a better way out there to go about this. Thanks!
first add on your images table an approved column with type boolean
and on your images controller and these action
def approve
#image =Image.find(params[:id])
#image.update_column(:approved,true)
respond_to do |format|
format.js
end
end
in routes add these method
resources :images do
member do
put :approve
end
end
in your html
<div class="image">
<img src="<%=#image.source%>" />
<%= link_to "approve",approve_image_path(#image),:remote=>true,:method=>"PUT",:class=>"approve_me"%>
</div>
in your image.js file add these method
$("body").on("click",".approve_me",function(e){
e.preventDefault();
$(this).parents(".image").find("img).css("border-color","green");
});
I think you are almost there.
Firstly - yes, changing the css with javascript on runtime will immediately change the appearance of dom elements.
On the client side - you want to indicate to the user which images have been selected, but you also need to store the approved value for each element in your submit form.
In Rails it is common to create hidden input elements storing additional data. Given the task at hand - they can store a value of 0/1 - rejected/approved. You can come up with your own convention for naming the image/select values.
Then, in your view, add onclick listeners to your images pointing to a javascript function:
1) checking if the clicked element is already selected,
2) changing the css of the current element,
3) updating the hidden input value for that element.
Here is a dummy demonstration - jsfiddle
Later, you can then get the approved/rejected value from the params in your controller, like (see below).
In case you want to assign a css class when constructing the view and depending on an approved flag value, you could do something like:
<img id="image_<%= image_id %>" src="<%= image_item.path" class="<%= (image_item.approved.to_i == 1) ? 'approved_img' : 'non_appr_img' %>" %>
<input id="image_<%= image_id %>_app_flg" type="hidden" value="<%= image_item.approved %>" />
where image_item is an object properties path and approved (self explanatory), image_id the id of the image object, approved_img and non_appr_img - css classes.
I'm not discussing the back-end for storing the approved flag, as it seems it is out of the scope of the question
EDIT
Brief concerning back-end
Given the you have an images model, extend it to include an approval property (prepare a database migration and edit your model .rb file to include the new columns).
In the view, include all of the hidden inputs inside a form which will be submitted to your controller(for example looping through an array on your images). For example:
<%= form_for :images, :url => {:action => "approve_images"}, :html => {:name => "testForm"} do |f| %>
<!-- TODO do stuff here - eg display images -->
<% #images.each do |imageItem| %>
<%= f.hidden_field "#{imageItem.id}_appproved", {:value => imageItem.approved}%>
<% end %>
<!-- TODO add a submit button -->
<% end %>
*You need to
Here :images is the controller, approve_images is the function in the controller that the form will be submitted to (include in routes), #images is an array with images data (from your model) and that you have prepared in the controller before rendering the view. I assume the images have ids and approved property.
This will yield in your view dom elements like this:
<input id="images_IMAGEID_appproved" name="images[IMAGEID_approved]" type="hidden" value="1" />
After submitting the form, in your controller you will be able to access these values like this:
img_approved = params[:images][IMAGEID+"_approved"]
Finally you can store that value to your database. Good luck! :)
I am omitting a lot of even more basic things, but I think the question is too broad as it is and there are plenty of resource detailing how to create,read,write a model, prepare data for the view etc. If not - please get started with http://guides.rubyonrails.org/getting_started.html
Thanks everyone who helped me answer this question below! I came up with a solution I'm pretty happy about, figured I'd share it hoping it'll help somebody else along the way.
The problem: I was looking for an AJAX solution that could make permanent changes to the data model. I wanted somebody to be able to toggle/highlight certain items on a page, and have those changes saved on the backend so I can view it later.
This required both a front-end ajax solution for the user interface and back-end solution so the ultimate changes will be saved in the data model so when I load up the site later, I can see the changes he made.
My solution (with the help of those who answered below):
Backend - I created a link that when pressed, would toggle the attribute in my model either true/false
Frontend - In order to give the client a real-time feel, I had to set the link to perform ajax requests and change the css accordingly.
My controller:
def approve
#pipe = Pipe.find(params[:id])
respond_to do |format|
if #pipe.toggle!(:approved)
format.html { redirect_to root_url }
format.js
else
format.html { render :index }
end
end
end
My pipes table has an approved:boolean attribute
My approve.js.erb file (I wrapped each pipe in a div using div_for(#pipe):
<% if #pipe.approved? %>
$('div#<%= dom_id(#pipe) %>').children('.flexslider').css('border','4px solid green');
$('div#<%= dom_id(#pipe) %>').children('a').text('un-approve');
<% else %>
$('div#<%= dom_id(#pipe) %>').children('.flexslider').css('border','4px solid white');
$('div#<%= dom_id(#pipe) %>').children('a').text('approve');
<% end %>
My Application Helper:
def approve_text(approvable)
approvable.approved? ? 'un-approve' : 'approve'
end
My trigger link (which uses abovementioned helper):
<%= link_to approve_text(pipe), approve_pipe_path(pipe), remote: true, method: 'PUT', class: approve_text(pipe) %>
My routes:
resources :pipes do
member do
put :approve
end
end
Again, thanks for those who helped provide answers. This is a solution I'm pretty happy with. I know it probably could use some help being refactored. If anyone has suggestions, would love to hear about it!
I have following situation:
I have a risk model and on my view it shows me a table with all risks. The table also includes a check_box_tag, as each risk should be able to be checked. In the tablehead there is a button and if this button is clicked, a method of the risk controller should be called, and it should have all checked risk IDs as parameter.
Actually, I don't know which would be the best way to solve this.
I have following code so far:
View:
<table>
<thead>
<tr>
<th>Risk Name</th>
<th>
<button id="mergerisks">Merge Risks</button>
</th>
</tr>
</thead>
<tbody>
<% Risks.all.each do |risk| %>
<tr>
<td><%= risk.name %></td>
<td><%= check_box_tag 'mergerisks', risk.id %>
</td>
</tr>
<% end %>
</tbody>
</table>
Javascript:
$( "#mergerisks" ).on( "click", function() {
var selectedriskmerge = new Array();
$("input:checked").each(
function() {
selectedriskmerge.push($(this).val());
});
});
Edit
Added the following ajax call to javascript
$.ajax({
url: '/riskmerge',
data:selectedriskmerge,
success:function() {
window.alert("Success!");
}
});
For now the Button only triggers the Javascript, and there are the ID's of all checked Risks are stored in an array.
But now I don't know what would be the best way to call a controller method in the risk controller and pass the IDs of all checked Risks to the method.
Thanks for any help.
NOTE: This is slightly different from what you wanted, but might provide a better user experience since they won't have to click an additional button at the end to update the records so heed with caution. No matter what though you will get a decent example of how to use ajax for your own needs.
First you need to create an action in the controller that updates the record you want which you already have. The twist is that instead of rendering html you will want to render json at the end. Something like this will do.
if risk.update_attribute(check: true)
render :json => { status: "Everything worked!"}
else
render :json => { status: "Something went wrong!"}
end
Next you will want to set up the javascript so that when a check box is clicked, an ajax post is sent to the action that updates the record. You have this partially done with your javascript. Inside your click event, you can have something like
$.post("/risk_update",
// This line below is the parameters sent to the action.
//So your action will recognize params[:risk_id]
{ risk_id: $(".clicked_check_box").attr("value") },
"json")
.done(function (responseText) {
if (responseText.status === "Everything worked!") {
// Do something on success of info being saved
} else if (responseText.status === "Something went wrong!") {
// Do something on failure of info being saved
}
});
Finally, there is a problem with the check box. Do you want the user to uncheck the box and call a record again. This of course goes beyond the discussion of the question but they are some things you have to keep in mind.
Of course in your case you will want to click a button that grabs all the ids and sends them to the action to update the records. One way would be to have javascript inject the id into an array when a check box is marked, then when the user clicks on the submit button the array is sent as params to the action that then loops through the arrays and updates the risks.
I'm sure there are better ways though, that's just the first thing that came to mind.
Your best bet would be to place all of the check boxes inside of a form.
<%= form_tag controller_method_name_your_controller_path, :method => 'get', do %>
...
<%= submit_tag 'submit' %>
<% end %>
Declare the path in the routes.rb file under your_controller
get :controller_method_name
You can group you check boxes together by using check_box:
<%= check_box 'mergerisks', risk.id %>
Then using params[:mergerisks] in the controller will return a hash with risk.id as the keys and a 1 or 0 depending on if the check box is checked.
Some documentation on routes and forms:
http://guides.rubyonrails.org/routing.html
http://guides.rubyonrails.org/form_helpers.html
Each user of my site has their own profile page, which contains several items of the same class, call them job_items, which are instance variables of the job class. I'm working on adding edit functionality for each item. In each item I have
<%= link_to "edit", job_item, :onclick => "$('#edit_job').modal();", :remote => "true" %>
Which I want to bring up a modal containing the appropriate form:
...
<div class="modal-body">
<%= semantic_form_for(job_item, :html => { :class => "form-horizontal" }) do |f| %>
...
<% end %>
However, any time I click edit, it doesn't matter which job_item I clicked edit within, the modal appears with a form for the same job_item, namely the first on the page. How can I set which job_item I want to be edited in the form? I'm confused because if I hold my mouse over the edit link, the correct job url appears, however it is not being passed to the javascript modal.
Is it possible you have more than one element with the id "edit_job" in your page? If so, $('#edit_job') will just choose the first one, because it assumes the ids are unique. You could fix this by making sure the ids are unique
:onclick => "$('#edit_job_#{job_item.id}').modal();"
and changing the corresponding element ids accordingly.