Generate ID when render new nested_field by gem cocoon - ruby-on-rails

I'm using bootstrap accordion where need set unique ID in few tags and attributes, but I can't find working way how to do it(without using JS). Is there any way to pass in local variable some counter when rendres nested field?
_form.html.slim
.products__inner
.title-products Products
.btn-main.secondary
= image_tag 'plus-icon.svg'
- #counter = 0
= link_to_add_association 'Add product', f, :products, data: { association_insertion_method: :after}, render_options: {locals: {counter: #counter}}
#products
= f.simple_fields_for :products do |product|
= render 'product_fields', locals: { f: product, counter: #counter }
- #counter += 1
= f.error_notification
_product_fields_html.slim
.nested-fields
.field.product-item
.accordion-item
h2.accordion-header id="flush-heading-#{counter}"
button.accordion-button.collapsed.counter aria-controls="flush-collapse-#{counter}" aria-expanded="false" data-bs-target="#flush-collapse-#{counter}" data-bs-toggle="collapse" type="button"
label Product
= link_to_remove_association "Remove Product", f, class: 'btn-main red deleteItem'
div aria-labelledby="flush-heading-#{counter}" data-bs-parent="#accordionFlushExample" id="flush-collapse-#{counter}" class="accordion-collapse collapse"

Related

Ruby on Rails | Collection_Select [Value] -> Param

Got a (hopefully) easy one:
Ruby partial view with a collection_select, need the value of that selection (var name = 'submod') passed as a param up to controller. Literally just stuck on how to get from collection_select/onchange() to named param.
html.erb code:
<% #modName = locals[:moduleName] %>
<% #id = locals[:id] %>
<%= form_with url: admin_command_path() do |f| %>
<%= collection_select(#refcode, :Code, Command.where(FLD: #modName), :Code, :Definition, options ={prompt: true}, html_options = {:onchange => "updateSubMod(this.value)"}) %>
<br /><br />
<button class="btn_new">
<%= link_to "Execute", new_admin_command_path(mod: #modName, submod: #refcode, id: #id) %>
</button>
<% end %>
The onchange function works as expected, so feel free to make use of that to solve for param:
<script>
function updateSubMod(Code) {
var submod = Code
console.log(submod, '********************')
}
</script>

How do you Display Avarage Rating in index page?

I'm trying to display average ratings for jobs on my index page, it works perfectly fine on my show page
but on my index page the stars are there but are blank, how do i get them to display on my index page?
My Show Page:
<h4>Average Rating</h4>
<div class="average-review-rating" data-score=<%= #average_review %>></div>
<span class="number-of-reviews">Based on <%= #job.reviews.count %> reviews</span>
</div>
</div>
<script>
$('.review-rating').raty({
readOnly: true,
score: function() {
return $(this).attr('data-score');
},
path: '/assets/'
});
</script>
<script>
$('.average-review-rating').raty({
readOnly: true,
path: '/assets/',
score: function() {
return $(this).attr('data-score')
}
});
</script>
Jobs Show controller
def show
if #job.reviews.blank?
#average_review = 0
else
#average_review = #job.reviews.average(:rating).round(2)
end
end
My Index Page:
<% #jobs.each do |job| %>
<%= link_to job.title, job_path(job) %></h4>
<%= job.category %></p>
<%= job.city %>
<div class="average-review-rating" data-score=<%= average_review %>></div>
<span class="number-of-reviews">Based on <%= job.reviews.count %> reviews</span>
</div>
</div>
<% end %>
<script>
$('.average-review-rating').raty({
readOnly: true,
path: '/assets/',
score: function() {
return $(this).attr('data-score')
}
});
</script>
In your show page, you have #average_review defined. I'm guessing this was done in your jobs controller in the show action.
In your index page, you will need to calculate the average rating for each job as you are iterating through them. You can do this the same way you defined #average_rating. If you are defining your #average_rating in the show action as:
#average_rating = job.reviews.sum('score') / job.reviews.count
You will need to either define this method in the model (the better option), so something like:
app/models/job.rb
def average_review
reviews.sum('score') / reviews.count
end
Then in your index page:
<div class="average-review-rating" data-score=<%= job.average_review %>></div>
The other option is to set a variable for each object on the index page itself, less work but probably not as neat:
<% #jobs.each do |job| %>
<%= link_to job.title, job_path(job) %></h4>
<%= job.category %></p>
<%= job.city %>
<% average_review = job.reviews.sum('score') / job.reviews.count %>
<div class="average-review-rating" data-score=<%= average_review %>></div>
<span class="number-of-reviews">Based on <%= job.reviews.count %> reviews</span>
</div>
</div>
<% end %>
EDIT:
In your app/models/job.rb
def average_review
reviews.blank? ? 0 : reviews.average(:rating).round(2)
end
And index.html.erb
<div class="average-review-rating" data-score=<%= job.average_review %>></div>
You can fetch these results like so:
def index
#jobs = Job.all
#average_reviews = Review.where(job_id: #jobs)
.group(:job_id)
.average(:rating)
.transform_values { |rating| rating.round(2) }
end
This will return a hash with job ids as keys and the average rating as value (rounded to 2 decimals). To access these average ratings you can do the following in your index view:
#jobs.each do |job|
average_review = #average_reviews[job.id]
end
The above code only makes 2 SQL calls, 1 for fetching your jobs and 1 for fetching all average reviews for all the fetched jobs. Keep in mind that if a job has no reviews. When fetching the average rating from the hash the value nil is returned.

How to display a selection based on user input using ajax and jquery

Think of the below as a bike rental. Someone fills out a form and gets a bike assigned to them which they can rent and borrow for a certain amount of time.
The problem I am having is I am trying to show the person who wants to rent the bikes what bikes are available before they submit the form. Below is my attempt using ajax. I have no errors but also my select is not updating.
request controller methods below
def new
#bikes = Bike.available_based_on_request_date(params[:Borrow_date], params[:Return_date])
#new_request = Request.new
end
create method below (with a temporary workaround, that reloads the form with a warning about availability.)
def create
#request = Request.new(request_params)
available_bikes = #request.new_request(current_user.id)
if (available_bikes >= #request.number_of_bikes_wanted) && #request.save
redirect_to root_path
else
flash[:warning] = "You have requested more bikes than available. There are only #{available_bikes} bikes available"
redirect_to new_request_url
end
end
params in request controller
def request_params
params.require(:request).permit(:Borrow_time, :Borrow_date,
:Return_date, :Return_time,
:number_of_bikes_wanted, bike_ids: [])
end
new.html.erb view
<div class="form" align = "center">
<%= render 'form.js.erb' %>
</div>
_form.js.erb below
<script type="text/javascript">
$(document).ready(function() {
$('.my-date').on('change', function() {
var data = {}
$('.my-date').each(function() {
if($(this).val()) {
data[$(this).attr("id")] = $(this).val();
}
});
if(Object.keys(data).length > 1) {
$.ajax({
type: "POST",
url: <%= new_request_path %>,
data: data
});
}
});
});
var options = "";
<% #bikes.each do |bike| %>
options += "<option value='<%= bike.id %>'><%= bike.name %></option>"
<% end %>
$('#request_number_of_bikes_wanted').html(options);
</script>
<div class="block-it" align=center>
<br>
<%= form_for #new_request do |request| %>
<%= request.label :Borrow_date, 'Borrow on' %>
<%= request.date_field :Borrow_date, id: 'Borrow_date', class: 'my-date', min: Date.today, :required => true %>
<%= request.label :Borrow_time, 'Borrow at' %>
<%= request.time_field :Borrow_time, value: '10:00', min: '9:00 AM', max: '4:30 PM', default: '10:00 AM', :ignore_date => true, :required => true %>
<br><br>
<%= request.label :Return_date, 'Return On' %>
<%= request.date_field :Return_date, id: 'Return_date', class: 'my-date', min: Date.today, :required => true %>
<%= request.label :Return_time, 'Return at' %>
<%= request.time_field :Return_time, value: '10:00', min: '9:00 AM', max: '4:30 PM', default: '10:00 AM', :ignore_date => true, :required => true %>
<br><br>
<br><br>
<%= request.label :NumberOfBikesWanted, 'Number of bikes' %>
<%= request.select :number_of_bikes_wanted, %w(select_bike), :required => true %>
<br>
<%= request.submit 'Submit' %>
<%= request.submit 'Reset', :type => 'reset' %>
<% end %>
<br>
</div>
There are a two main problems with your code:
Controller
Use a different action to set the endpoint that you will call with ajax, so instead of this:
def new
#bikes = Bike.available_based_on_request_date(params[:Borrow_date], params[:Return_date])
#new_request = Request.new
end
Try this:
def bikes
#bikes = Bike.available_based_on_request_date(params[:Borrow_date], params[:Return_date])
def new
#new_request = Request.new
end
If you want to keep REST routes, then create a new controller and use the index action within that controller.
Form
This code:
var options = "";
<% #bikes.each do |bike| %>
options += "<option value='<%= bike.id %>'><%= bike.name %></option>"
<% end %>
$('#request_number_of_bikes_wanted').html(options);
doesn't belong here, it must be deleted from your file and instead put it on a new file called bikes.js.erb; also rename your form to _form.html.erb.
And update your ajax call to use your new route:
$.ajax({
type: "POST",
url: <%= bikes_path %>,
data: data
});
What you want to setup is a new endpoint but instead of returning html, it will return a js. But you must treat it as an independent action, just as any other action in rails. The only difference is how you call that action (ajax) and how you respond to it (js).

Instant search engine in rails

I am trying to create an instant search engine in rails.
My searchable model is tournament
I added the gem 'pg_search'to my gemfile
In my tournament model I have :
class Tournament < ActiveRecord::Base
include PgSearch
pg_search_scope :search,
:against => [:name, :address, :city, :club_organisateur, :starts_on, :ends_on, :postcode],
:using => {:tsearch => {:prefix => true} }
The search takes place on my Tournament#index view, so in my tournaments controller I have :
def index
#tournaments = policy_scope(Tournament)
if #tournaments.blank? && current_user.judge?
render 'pages/partials/_no_tournaments_judge'
elsif #tournaments.blank?
render 'pages/partials/_no_tournaments'
elsif params[:content].blank?
elsif params[:content]
#tournaments = #tournaments.search(params[:content])
respond_to do |format|
format.js {}
format.html{ render :index }
end
end
end
In my view (slim) tournament/index I have :
div class="container"
div class="row"
div class="col-xs-12"
h1 class="emperor" Recherche de tournois
= form_tag tournaments_path, method: "get", :remote => true do ||
div class="form-group"
= label_tag :content, "recherche"
= text_field_tag :content, nil, class: "form-control"
= render "tournaments"
Here is the tournaments' partial :
div class="row" id="tournament_row"
- #tournaments.each do |tournament|
div class="col-md-4"
div class="panel panel-default"
div class="panel-heading"
h2
= tournament.name
br
small = tournament.city.upcase
div class="panel-body"
p
'Du
strong> = tournament.starts_on.strftime("%d/%m/%Y")
'au
strong = tournament.ends_on.strftime("%d/%m/%Y")
ul
- tournament.competitions.each do |competition|
li
= "#{competition.category} #{competition.genre.text}"
div class="panel-footer"
= link_to 'En savoir plus', tournament_path(tournament), class: 'btn btn-primary'
and Here is my index.js.erb file :
$('#tournament_row').html("<%= render 'tournaments' %>");
When I check my logs everytime I submit the form, params[:content] is sent to my tournament#index action and I can see that
#tournaments = #tournaments.search(params[:content]) is working.
My problem is that my tournament/index view is never updating to display the tournaments contained in #tournaments... It always stay the same. What is the problem ?
Try to add escape_javascript
$('#tournament_row').html("<%= escape_javascript(render 'tournaments') %>");
or
$('#tournament_row').html('<%=j render 'tournaments' %>')

Dynamic Partial Based on Select Box - Rails 2.3.5

I've edited my request to hopefully be clearer. I need to render a partial dynamically based on a previous selection box.
REQUEST belongs to PRODUCT
PRODUCT belongs to CATEGORY
CATEGORY has many PRODUCTS
PRODUCT has many REQUESTS
User hits form: create_request.html.erb
User selects a category, then the products select list is populated (like Railscast 88 - dynamic select boxes)
What I now need is to render different partial forms based on which product is selected. I suck at jquery.
create_request.html.erb:
<%= javascript_include_tag "dynamic_products.js" %>
<% form_for :request, :url => {:controller => :requests, :action => :create_request, :id => params[:id]} do |f| %>
<label>Select Category:</label>
<%= select( "request", "category_id", Category.find( :all).collect { |c| [c.name, c.id] })%></br>
<div id="product_field">
<label>Select Product</label>
<%= select( "request", "product_id", Product.find( :all).collect { |p| [p.name, p.id] })%></br>
</div>
#### and here is where I need help:
#### if request.product_id = 1, render partial _form1
#### if request.product_id = 2, render partial _form2
<button type="submit">Submit</button>
<% end %>
dynamic_products.js.erb:
var products = new Array();
<% for product in #products -%>
products.push(new Array(<%= product.category_id %>, '<%=h product.name %>', <%= product.id %>, <%= product.active %>));
products.sort()
<% end -%>
function categorySelected() {
category_id = $('request_category_id').getValue();
options = $('request_product_id').options;
options.length = 1;
products.each(function(product) {
if (product[0] == category_id && product[3] == 1) {
options[options.length] = new Option(product[1], product[2]);
}
});
if (options.length == 1) {
$('product_field').hide();
} else {
$('product_field').show();
}
}
document.observe('dom:loaded', function() {
categorySelected();
$('request_category_id').observe('change', categorySelected);
});
one reminder first before we start. I'm not sure about this but I think request is a reserved word in rails.
JS
this just observes the dropdown and performs an ajax call
$(document).ready(function() {
$('#request_product_id').change(function() {
$.ajax({ url: '/products/' + this.value + '/form_partial' });
});
});
ROUTES
nothing fancy here either. Just setting up a route where the ajax will go to when it is triggered
resources :products do
get :form_partial, on: :member
end
CONTROLLER
we just fetch the product using :id which is passed from ajax
def form_partial
#product = Product.find params[:id]
end
JS TEMPLATE
you need to create a form_partial.js.erb which will render the partial depending on the product. The code below appends the partial after the product_field div
# app/views/products/form_partial.js.erb
$('#product_partial').remove();
<% if #product.id == 1 %>
$('#product_field').after('<div id="product_partial"><%= escape_javascript render('partial1') %></div>');
<% else %>
$('#product_field').after('<div id="product_partial"><%= escape_javascript render('partial2') %></div>');
<% end %>
UPDATE: for rails 2.x
we just need to change the routes and the js template in order for this to run on rails 2.x
ROUTES 2.x
map.resources :products, member: { form_partial: :get }
JS TEMPLATE 2.x
if I remember correctly, the file should be named form_partial.js.rjs. This will give you a page variable which you can use to add js.
# app/views/products/form_partial.js.rjs
page << "$('#product_partial').remove();"
page << "<% if #product.id == 1 %>"
page << " $('#product_field').after('<div id="product_partial"><%= escape_javascript render('partial1') %></div>');"
page << "<% else %>"
page << " $('#product_field').after('<div id="product_partial"><%= escape_javascript render('partial2') %></div>');"
page << "<% end %>"

Resources