How can I change forms dynamically? - ruby-on-rails

I have 2 different models called Parent and Child.
I have a page called House that has the link_to to both forms on it:
pages/house.html.erb
link_to "Parent", new_parent_path
link_to "Child", new_child_path
When you start on the page it is empty with no forms.
I want to be able to click the new_parent link and generate the parent form on the same page but if I click the new_child link I want to remove the parent form and replace it with the child form by AJAX. How would I do this?

I think it will be easier to do it without Ajax, but with css. Generate the 2 forms in the page (using partials for example), and put than inside an hidden div (css style: display: none), and use javascript with your links to show/hide the div containing the form you want to display (with JQuery show and hide effects)

This was actually very simple to create. This question and this one helped me understand AJAX and jquery better.
Lets use the parent as an example:
I put the format.js in my controller for the new action:
ParentsController
def new
#parent = Parent.new
respond_to do |format|
format.js
end
end
I delete my parents/new.html.erb as a personal choice and replace it with new.js.erb that will be called by my "Parent" link_to.
# parents/new.js.erb
$("#generate-form").html("<%= escape_javascript(render(:partial => 'parents/form', locals: { parent: #parent })) %>");
Then create a Parent form in a partial that is remote => true for AJAX.
# parents/_form.html.erb
<%= form_for(#parent, :remote => true) do |f| %>
<%= f.input :full_name %>
<%= f.button :submit, 'Done' %>
<% end %>
Then I have one page that holds both forms.
PagesController
def index
respond_to do |format|
format.html
end
end
Then the links on that page for both parent and child.
#pages/index.html.erb
<li><%= link_to "Parent", new_parent_path, :remote => true %></li>
<li><%= link_to "Child", new_child_path, :remote => true %></li>
<div id="generate-form">
</div>
The remote => true of my links and form will respond to the js format of my new action and my div id="generate-form" acts as the boss, placing either form partial inside of it. This automatically replaces one with the other depending on the link you click, thus resulting in changing forms dynamically.
Note: The code for the child model is the same (except for form fields). Just replace the Model name (Parent to Child).

Related

Rails 5: Send form via AJAX, modifying inputs first, and append a view to the form

(This question has been written to focus only on what is relevant.)
Within a page view I have a Rails form:
<%= form_for #model, url: {controller: 'foo', action: 'bar'} do |f| %>
<!-- various inputs -->
<%= f.button "Submit", type: 'submit' %>
<% end %>
When I click the 'Submit' button, I would like to:
Modify some of the inputs before sending
Send the form via AJAX
Replace the form with the view corresponding to the controller action (/views/foo/bar.html.erb)
What's the cleanest way to do this please?
1. Enable form submission via ajax using remote: true option
<%= form_for #model, url: {controller: 'foo', action: 'bar'}, remote: true do |f| %>
2. Modify inputs before form submit
$('form.model_name').on('ajax:beforeSend', function(event, xhr, settings) {
//Modify inputs
});
3. Replace the form with the view corresponding to the controller action (/views/foo/bar.html.erb)
In the action where the form is handled:
def create
....
.....
respond_to do |format|
format.js
end
end
4. create a .js.erb template where the other templates for this controller are placed
create.js
$('form.model_name').html("<%= j(render 'foo/bar.html.erb') %>")`
And that's it!

rails select_tag without form_tag

I have a view displaying buttons to link to reports where I want the user to be able select the sort column for the report.
The select:
<%= select_tag 'select_sort_order', "<option>fleet_num</option><option>rego</option>".html_safe %>
Then the link:
<%= link_to "PDF", current_vehicle_reports_path(format: 'pdf', select_sort_order: params[:select_sort_order]), target: "_blank" %>
With the goal of being able to access the value in the controller via:
params[:select_sort_order]
So as far as I understand, the reason I'm not getting a value as desired from params is the lack of a form_tag. Could someone point me in the direction of how to pass the value from the select_tag to the controller. (if its possible)
Thanks
controller: (the if/else is just to test for now)
def current
#report = Report.new(current_user.filter_start_date, current_user.filter_end_date, current_user.vehicle_group_ids, current_user.vehicle_sub_group_ids, "Current Asset Register", false, true, "date_reported", 0, current_user.vehicle_type_ids, true )
if params[:select_sort_order] == "fleet_num"
#report.data = Vehicle.left_outer_join_vehicle_tables("sold = false #{#report.where_clause}").order(:fleet_num)
else
#report.data = Vehicle.left_outer_join_vehicle_tables("sold = false #{#report.where_clause}").order(:rego)
end
respond_to do |format|
format.pdf do
render :pdf => "#{__method__}"
end
format.xlsx
end
end
in your view
<%= form_tag current_vehicle_reports_path, id: "report_form" do %>
<%= select_tag 'select_sort_order', "<option>fleet_num</option><option>rego</option>".html_safe %>
<%= link_to "PDF", current_vehicle_reports_path(format: 'pdf'), id: 'report_id' %>
<%end %>
in application.js or in other js file
$('#report_id').click(function(e) {
e.preventDefault();
$('#report_form').attr('target', '_blank').submit();
});
You're PDF link won't be able to access from your <select> drop down. That's just not the way links work in HTML.
As far as I know you only have two options.
Wrap the <select> tag and the link in a <form> tag and switch your link to a button (you can still style it to look like a link in CSS if you want, if so use the <button> tag not the <input type="submit"> tag).
Use javascript to take the value from the drop down and append it to the link tag like so: ?select_sort_order=....
For this to work you would need a form_tag and then a submit_tag (instead of a link).
Another approach would be to create both links passing select_sort_order explicitely, hide one of them, and then toggle showing each depending on the value of your select (using the onchange event). This approach doesn't need a form and would work.
I would go with the first option as it is cleaner.

Rails render forms in same page

This is my scenario
I have a Stories controller that has a form to create a new Post :
stories.show.erb
...
<%=link_to("Create new post",new_story_post_path(#post))%>
This new post is generated by Posts controller
posts\new.erb
<%= form_for [#story,#post] do |f|%>
I want to break away from the traditional behavior - I want the new post form to be displayed inside the same 'story' page and NOT have a full page reload and appearing in a new page .
What's the best way to do this in rails ?
I think in your case it'll be best if you add the form in your stories show view and make it as hidden and then when a user clicks on the link to create a new post you can show them your form.
def show
#story = Story.find(params[:id])
#post = #story.posts.build # assuming stories have many posts
end
#show.html.erb
...
<%=link_to "Create a new Post", "#", id: "post-link"%>
<%= form_for [#story,#post], html: {id: "new-post"} do |f|%>
// form fields
<% end %>
#some_file.js
$(document).on("click","#post-link",function(){
$("#new-post").show();
});
#some.css.scss
#new-post{display:none;}
you could write:
<%= form_for [#story,#post], remote: true do |f|%>
in create.js.erb you could write
$('#post_block').html("<%= render 'your partial with posts' %>")

Rails 3.2 button_to not passing GET variable

still learning rails, doing my first project.
i'm trying to pass an additional "category" variable to "new" method in my "pages" controller
def new
#page = Page.new
#cats = Cat.all
end
i'm doing it so the new page has already selected category from dropdown menu
it works when i use link_to
<%= link_to "Create new page", new_page_path(:cat => #cat.id) %>
but when i'm trying to use button_to
<%= button_to "Create new page", new_page_path(:cat => #cat.id), method: :get %>
the variable "cat" is not passed to "new" action view. it's not a big problem but it screws up my layout because i'm using button_to in all other places and i just hoped there is a better way to solve it that adding more css so it'll look the same
One possible way to solve this is to create a form with a hidden field
<%= form_tag new_page_path(), :method => :get do
hidden_field_tag "cat", #cat.id
button_to "Create new page"
end %>
Your version doesn't work because "button_to" method creates a form which passes params to the browser only from input fields.

embeddable component in views or how to call a different controller

Let's say you have a form that has its own controller. Is there any way to embed this form in different views (governed by other controllers)? As far as I understand partial templates carry only logic in the Ruby code that is inside the template. I am thinking more of a full-blown component where maybe you can call its controller.
The form is not driven that directly by the controllers. Yeah this is the price of all this magic.
To clarify a bit:
You type in your browser http://yourhost/posts
Your request (GET /posts) hits the router, then your router says that the /post urls belongs to the PostsController and the action is index
Then your controller executes the index method, do your business logic (loads the posts from the database, for example)
loads the view (views/posts/index...) and run it by 'substituting' all the instance variables and stuff defined in your controller (eg #posts = Post.all) that you have in it
then you see the view rendered with a list of posts (if in the view you have something similar to #posts.map{|p| p.title}.join(", ") )
yes I know it's not the best /posts view in the world but it's only to grasp the idea
The same goes for form, your form tag (for example form_for) gets an instance from the controller (let's say #post) and (in edit mode) gets filled with your Post attributes.
Then when you (edit something and) click the submit button it makes a request (by default a PUT to /posts) passing all the values in the form, then your controller gets the (POST) values of the requests (the ones you see in the server log) and makes his work (like saving the post's datas)
and because of this in a controller you can use the method
render :controller => :foo, :action => :bar
to render another controller action different from the default one
Hope this will be useful!
You can create a form in any view to call any controller. In a RESTful app, you can usually just pass an empty object (using the Posts/Commments example from makevoid)
<% form_for #new_comment do |f| %>
<%= f.text_area :text %>
<%= f.submit %>
<% end %>
This form should route to the create action on CommentsController. From there, you could use redirect_to :back in order to get back to the view that triggered this controller. This does have some validation issues I think though.
If you are non-RESTful, you can use the old form_for style:
<% form_for :comment, #new_comment, :url => { :controller => "comments", :action => "create" } do |f| %>
<%= f.text_area :text %>
<%= f.submit %>
<% end %>
For either of these examples, you need to have the #new_comment, which you would create in your PostsController:
def show
#post = Posts.find(params[:id])
#new_comment = #post.comments.build
end

Resources