Form inside another form rails - javascript

Let me first say that I'm a beginner with web development, so stay with me!
The thing is that I have a page where rails will gather all the rooms from the database. And then list them in a table. So far so good. But I want the user to be able to click a button next to each row, that will book the room for the user.
Atm I have it working 80%. The thing is that I have a form inside another form, so when the user press book then it books the last row in the table ofc! So how do I fix this? I read about using javascript to fix this, but as a beginner I dont really know javascript.
I could put the Reservation.new form inside the all_rooms form and it will work. But I need to get the value from the datetimepicker from the user also. Thats why I put it outside.
<%= form_for(Reservation.new, remote: true, format: :js, url: {controller: "booker", action: "create"}) do |new_res| %>
<div class="calender">
<%= new_res.date_field :date, :value => (Time.now + 6.days).strftime('%Y-%m-%d') , min: 6.days.from_now, max: 90.days.from_now %>
</div>
<div class="time">
<%= new_res.time_select :start_time , {:default => {:hour => 10 , :minute => 00}, minute_step: 15 , :start_hour => 8, :end_hour => 18, :hour => 11} %>
<%= new_res.time_select :end_time , {:default => {:hour => 14 , :minute => 00}, minute_step: 15 , :start_hour => 8, :end_hour => 18, :hour => 11} %>
</div>
</div>
<div class="t_Data">
<table class ="rooms_to_book">
<tr>
<th>Namn</th>
<th>Storlek</th>
<th>Byggnad</th>
<th>Utrustning</th>
<th>Booka</th>
</tr>
<% #all_rooms.each do |r| %>
<tr>
<td><%= r.room %></td>
<td><%= r.room_size %></td>
<td><%= r.building %></td>
<td><%= r.room_description %></td>
<td>
<%= new_res.hidden_field :room, value: r.room %>
<%= new_res.hidden_field :building, value: r.building %>
<%= new_res.hidden_field :room_size, value: r.room_size %>
<%= new_res.hidden_field :room_description, value: r.room_description %>
<%= new_res.submit "Book" %>
</td>
</tr>
<% end %>
</table>
</div>
<% end %>
So question is. How do I fix this problem, so when I press book. I create a new reservation and gather all the data + the date the user picked. So its unique for each row.

Instead of all the hidden fields you could name each of the submit buttons like this
<%= new_res.submit "Book", name: r.id %>
then inside your create action in your controller extract the room id from the params hash, as it will be a key in the params hash pointing to "Book". Then using the id you can find the associated Room object and create a new Reservation using everything you had in the hidden fields.

Related

Create dynamic form with has_many (many-to-many) association with a single form in Rails 7

I have 4 models, User, Products, Property & ProductProperty. Here's how they relate:
All attributes in these models, such as name, value, upc, and available_on, must have a presence of value!
Once a user is created successfully, they can create products (with the Products model) that can have many properties (with the Property model) and a corresponding value (with the ProductProperty model). And I have to create all of these in a single form.
With the help of some tutorials and Reddit users, I was able to successfully implement the product form which is able to create a new product, property, and corresponding value. Here's the snap of my implementation:
Now here comes the tricky part: to add more properties dynamically to create multiple properties and their values of a product. So here's my implementation of it so far:
Product Form
<%= form_with model: #product do | prod | %>
<%= prod.label :name %>
<%= prod.text_field :name %>
<br>
<%= prod.label :upc, "UPC" %>
<%= prod.text_field :upc %>
<br>
<%= prod.label :available_on %>
<%= prod.date_field :available_on %>
<br>
<h3>Properties</h3>
<table>
<thead>
<tr>
<th>Property Value</th>
<th>Property Name</th>
</tr>
</thead>
<tbody class="fields">
<%= prod.fields_for :product_properties do | prod_prop | %>
<%= render "property", prod_prop: prod_prop %>
<% end %>
</tbody>
</table>
<br>
<%= link_to_add_fields('Add More Properties', prod, :product_properties) %>
<br>
<%= prod.submit %>
<% end %>
Property Form Partial
<tr>
<td><%= prod_prop.text_field :value %></td>
<%= prod_prop.fields_for :property do | prop | %>
<td><%= prop.text_field :name %></td>
<% end %>
</tr>
Helper Method
def link_to_add_fields(name, prod, association)
new_object = prod.object.send(association).klass.new
id = new_object.object_id
fields = prod.fields_for(association, new_object, child_index: :id) do | builder |
render "property", prod_prop: builder
end
link_to(name, "#", class: 'add_fields', data: {id: id, fields: fields.gsub("\n", "") } )
end
application.js file:
$("form").on("click", ".add_fields", function (event) {
let regexp, time;
time = new Date().getTime();
regexp = new RegExp($(this).data("id"), "g");
$(".fields").append($(this).data("fields").replace(regexp, time));
return event.preventDefault();
});
Now when I click on the "Add more Properties" link it adds only one field:
Issue 1: I believe I need to do some modifications in the helper method to implement this correctly but I am not able to figure out the logic of it.
There's one more issue! When we create additional property instances of property name & value, they will all disappear if there're any validation errors:
Product Controller:
def new
#product = Product.new
#product.product_properties.build.build_property
end
def create
#product = current_user.products.new(product_params)
if #product.valid?
#product.save
redirect_to products_path, notice: "Success!"
else
render 'new', status: :unprocessable_entity
end
end
I can think of a couple of solutions to this issue: first, to implement the logic in the create action for the dynamically created property name and value fields to persist between requests, second, to use javascript.
Issue 2: So how do I make the additional properties created by the user persist in the view between requests?
I am running rails v7.0.4 and I am not allowed to use hotwire, only Javascript.

Is there a way to regenerate link_to_add_association (specifically data-association-insertion-template)?

Built a very basic rails app to manipulate nested attributes using cocoon and the add and remove links work great. However, it wasn't too long until I wanted to alter the underlying content of what was inserted, say in response to another field changing the list of option values in an included select tag. It appears that the contents to be added are contained in an 'a' tag data element (data-association-insertion-template). I can quite easily change the select options for all included lines via jQuery but changing the behavior of the link_to_add_association is beyond me.
Here are snippets of my example:
_form.html.erb
<div>
<strong>Entries:</strong>
<div id="entries" style="border: thin solid">
<%= f.fields_for :entries do |oi| %>
<%= render "entry_fields", f: oi %>
<% end %>
<div class="links">
<%= link_to_add_association 'Add Entry', f, :entries, {id: 'cocoon-add-entry'} %>
</div>
</div>
</div>
_entry_fields.html.erb
<div class="nested-fields">
<%= f.label :item_id %>
<%= f.select :item_id, #items.collect {|i| [i.style, i.id]}, {include_blank: true}, {selected: :item_id, multiple: false} %>
<%= f.label :decoration_id, 'Decoration' %>
<%= f.select :decoration_id, #decorations.collect { |d| [ d.name, d.id ] }, {include_blank: true}, {selected: :decoration_id, multiple: false, class: 'decoration'} %>
<%= f.label :color %>
<%= f.text_field :color %>
<%= f.label :size_id %>
<%= f.select :size_id, #sizes.collect { |s| [ s.name, s.id ] }, {include_blank: true}, {selected: :size_id, multiple: false} %>
<%= f.label :number %>
<%= f.number_field :number, value: 1, min: 1 %>
<%= f.check_box :_destroy, hidden: true %>
<%= link_to_remove_association "Remove Entry", f %>
</div>
orders.coffee
ready = ->
$('.customer').change ->
$.ajax
url: '/orders/change_customer'
data: { customer_id : #value }
$(document).ready(ready)
$(document).on('turbolinks:load', ready)
order_controller.rb
def change_customer
#decorations = Decoration.joins(:logo).where('logos.customer_id = ?', params[:customer_id])
respond_to do |format|
format.js
end
end
change_customer.js.erb
// update all existing entry decorations with new customer driven options
<% new_decor = options_from_collection_for_select(#decorations, :id, :name) %>
var new_decor_options = "<option value='' selected='selected'></option>" + "<%=j new_decor %>";
$('.decoration').html(new_decor_options);
// now need to change $('#cocoon-add-entry').attr('data-association-insertion-template, ???);
// or regenerate link entirely - but don't have required data to do so here (form builder from original)
I have tried to manipulate the template data string directly via js str.replace but that is one ugly regular expression because of the unescapeHTML and htmlsafe operations done to make it an attribute in the first place. And, that approach doesn't smell good to me. I have been slowly working through the cocoon view_helpers and javascript but nothing seems to fit or I don't seem to have the right methods/data values to build a replacement link. Suggestions?
BTW, kudos for cocoon gem.
After a lot of gnashing of teeth, I have cobbled together a potential solution. I haven't decided if I will let this go into production yet because of the limitations but thanks to combining several different SO questions and answers, the following works:
change_customer.js.erb
// update all existing entry decorations with new customer driven options
<% new_decor = options_from_collection_for_select(#decorations, :id, :name) %>
var new_decor_options = "<option value='' selected='selected'></option>" + "<%=j new_decor %>";
$('.decoration').html(new_decor_options);
// update the Add Entry link to capture new decorations set.
// Note use of ugly hack to recreate 'similar' form.
// Also note that this will only work for new order; will have to revise for edit.
'<%= form_for(Order.new) do |ff| %>'
$('#cocoon-add-entry').replaceWith("<%=j render partial: 'add_entry_link', locals: {f: ff} %>");
'<% end %>'
_add_entry_link.html.erb
<%= link_to_add_association 'Add Entry', f, :entries, {id: 'cocoon-add-entry', data: {'association-insertion-method' => 'after'}} %>

Rails will_paginate two objects on the same page, passing one param from search field

I am using will_paginate on the results from two instance variables, each using the same param[:query] on two different PgSearch.multisearch methods. In the views, when clicking on one of the pagination links, both the tables were being updated. Is there any way to get around this problem? By passing only one param from the form? (As my search form has only one text field). I have searched and gone through the similar questions, most of them suggesting to use two params., but none of them gave me any thoughts to solve this prob using one param. :/
Code in the form:
<%= form_tag("/search", :method => "get", :remote => true) do %>
<%= label_tag(:query, "Search for: ") %>
<%= text_field_tag(:query, params[:query]) %>
<%= submit_tag("Search", class: "btn btn-primary") %>
<% end %>
Controller code:
def index
#employee = Employee.find(session[:user_id])
#search_employees = PgSearch.multisearch(params[:query]).where(:searchable_type => "Employee").paginate(page: params[:page], :per_page => 5)
#search_customers = PgSearch.multisearch(params[:query]).where(:searchable_type => "Customer").paginate(page: params[:page], :per_page => 5)
respond_to do |f|
f.html
f.js
end
end
Code in the View:
<% if !#search_employees.empty? %>
<h2>Employees</h2>
<table class="table table-hover">
.
.
<% #search_employees.each_with_index do |doc, index| %>
<% user = doc.searchable %>
<tr id="employee-<%= user.id %>">
.
.
<% end %>
</tbody>
</table>
<% end %>
<%= will_paginate #search_employees %>
<% if !#search_customers.empty? %>
<h2>Customers</h2>
<table>
.
.
</table>
<% end %>
<%= will_paginate #search_customers %>
Can I send two different params with the same text field value from the form? if so., please let me know how. Any ideas or suggestions would be much appreciated. :)
Thanks in Advance. :)
Pagination depends on the page parameter, but not on the query parameter from the search form, therefore you don't need to change anything in your form.
To update only corresponding table you need to customize page parameter in at least one of the tables. For example change page parameter for customers:
<%= will_paginate #search_customers, :param_name => 'customers_page' %>
and in controller:
#search_customers = PgSearch.multisearch(params[:query])
.where(:searchable_type => "Customer")
.paginate(:page => params[:customers_page], :per_page => 5)
This should resolve the issue

Access form_group div via id

I want to perform a check to provide instant validation feedback on the input of a field in my bootstrap form for my Profiles model using java/coffee-script. My current solution is working but feels rather clunky and I would like to improve it.
Here are some code examples of what I'm doing.
app/views/profiles/_form:
<%= bootstrap_form_for(#profile, layout: :horizontal, html: {id: "profile-form"} ) do |f| %>
<%= f.text_field :name %>
<%= f.text_field :number, id: "number-field" %> //Field to check
<%= form_group do %>
<%= f.primary %>
<% end %>
<% end %>
app/assets/javascript/profile_number_control.coffee:
$(document).on 'ready page:load', ->
$field = $("#number-field")
$group = document.getElementById("profile-form").childNodes[5]
numberPattern = "^(19|20)[0-9]{6}-[0-9]{4}$"
$helpBlock = $group.getElementsByClassName("help-block")[0]
$field.keyup ->
//This works fine, just wanted to show what is going on
if inputIsValid()
set $group class to " has-success"
set $helpBlock message to successful
else
set $group class to " has-error"
set $helpBlock message to corresponding error message
Here is the problem. I want this script to work for both profile#new and profile#edit, but since the array of elements given by document.getElementById("profile-form").childNodes differs in length depending of the current view I can not use the same index (5 in the example) for both #new and #update.
Does anyone know how to set an id to that specific form_group in my view? Or do I have to actually write out the whole html code to be able to place an id there properly?
This might help !
<%= bootstrap_form_for(#profile, layout: :horizontal, html: {id: "profile-form"} ) do |f| %>
<%= f.text_field :name %>
<%= f.text_field :number, :input_html => { id: "number-field" } %> //Field to check
<%= form_group do %>
<%= f.primary %>
<% end %>
<% end %>

Using JavaScript/Jquery to show forms with the same id/class in Ruby

I want to have a button for retweets in my web-application. When I press the button I want a form to display in order to add my own text to the tweet.
I implemented this . However I have a problem: all my forms and buttons contain the same id and class, so you can imagine that when I press a button it will display the first form with that id/class instead of the one that it has to show. How can I show the form that I have to show ?
Here are only the parts of the code that contain retweet part(the other parts are not important I think for the problem but I can post them if someone wants me to) :
<button type="buton" id="retweet-button">retweet</button>
<%= form_for(current_user.tweets.build,url: retweet_act_path, method: :post, html: { multipart: true, :class => "retweet_form" }) do |f| %>
<%= f.hidden_field :original_tweet_id, value: tweet.id %>
<%= f.text_area :content, placeholder: "Add a message to the tweet..." %>
<%= f.submit "Retweet"%>
<% end %>
And the JS:
<script type="text/javascript">
$(document).ready(function() {
$('.retweet_form').hide(); //Initially form wil be hidden.
$('#retweet-button').click(function() {
$('.retweet_form').show();//Form shows on button click
});
});
</script>
How can I show the form that I have to show when pressing a certain button ? (I guess I have to have a different ID for every button and form , but how ? )
As I understood from the code, you use one button/form pair per tweet. So that, you are able to use tweet ID to generate form ID. To connect button to appropriate form, use data-* fields.
Generation of forms and buttons on Rails side (NOTE: syntax highlight is broken):
<button type="button" class="retweet-button" data-target-form-id="<%= "#retweet-form-#{tweet.id}" %>">retweet</button>
<%= form_for(current_user.tweets.build,url: retweet_act_path, method: :post, html: { multipart: true, :class => "retweet_form", id: "retweet-form-#{tweet.id}" }) do |f| %>
<%= f.hidden_field :original_tweet_id, value: tweet.id %>
<%= f.text_area :content, placeholder: "Add a message to the tweet..." %>
<%= f.submit "Retweet"%>
<% end %>
And JS:
$('.retweet_form').hide();
$('.retweet-button').click(function() {
var formId = $(this).data('target-form-id');
$(formId).show();
});

Categories

Resources