Narrow elements in a join table by a dropdown menu. (Rails 4) - ruby-on-rails

for one of my join tables, I'd like to filter results based on a dropdown menu selection. Before I explain further, I will provide the relevant code.
I have 3 models in question:
class Scoreboard
has_many :teams, has_many :team_matches
end
class Team
belongs_to Scoreboard
#self reference with the teams to create team_matches.
has_many :first_team, class_name: "TeamMatch", foreign_key: "team_a_id", dependent: :destroy
has_many :team_a, through: :first_team, source: :team_a
has_many :second_team, class_name: "TeamMatch", foreign_key: "team_b_id", dependent: :destroy
has_many :team_b, through: :second_teams, source: :team_b
end
And the final Model which is a join table of teams:
class Team_Match
belongs_to :team_a, class_name: "Team"
belongs_to :team_b, class_name: "Team"
belongs_to :scoreboard
end
Team Matches Controller Code for the Index View:
class TeamMatchesController < ApplicationController
require 'will_paginate/array'
def index
#selected = true
#scoreboard = Scoreboard.find(params[:scoreboard_id])
#teams = #scoreboard.teams
#matches = #scoreboard.team_matches.order("match_date DESC").paginate(page: params[:page], per_page: 5)
end
end
The Index Page View:
<h2 class="team-matches-header">TEAM MATCHES</h2>
<% if manager_or_owner?(#scoreboard, current_user) %>
<%= link_to "Add Matches", new_scoreboard_team_match_path(#scoreboard), class: "btn btn-primary" %>
<%= link_to "Clear Matches", deletematches_scoreboard_path(#scoreboard),method: :delete, class: "btn btn-danger", :data => {:confirm => "Are you absolutely sure you want to delete all matches?"} %>
<% end %>
<%= form_tag(scoreboard_team_matches_path, :method => "get", id: "match-search-form", autocomplete: "off") do %>
<div class="row new-member-field">
<div class="col-xs-12 col-sm-6">
<%= select_tag "teams", options_from_collection_for_select(#teams, "id", "name"), prompt: "Select something", class:"form-control" %>
<%= submit_tag 'Filter' %>
</div>
</div>
<% end %>
<div class="match-list">
<%= render 'match_list' %>
</div>
The Match List Partial
<% if #matches.present? %>
<% #matches.each do |game| %>
<div class="row match-div clearfix" id="match_<%= game.id %>">
<div class="col-xs-12 match-column">
<div class="facing-teams">
<div class="first-team">
<%= game.team_a.name %>
</div>
<div class="second-team">
<%= game.team_b.name %>
</div>
</div>
</div>
</div>
<% end %>
<% end %>
<div class="row">
<div class="col-xs-12 new-member-pages">
<%= will_paginate #matches %>
</div>
</div>
So far on the team matches index page, I have been able to put a dropdown menu selection with a list of teams. The trouble I'm having is how to make it functional. Upon submission of a team from the dropdown selection, I would like to display a list of Match objects(#matches) whose :team_a_id or :team_b_id matches the selected Team's id. How would I go about doing that?

I would use scopes to achieve what you want. Scopes are reusable filters that you can apply to an association to find exactly the records that you want.
This is how you would create a scope that finds the matches for a particular team:
class Team_Match
scope :has_team, ->(team_id) {
team_a = arel_table[:team_a_id].eq(team_id)
team_b = arel_table[:team_b_id].eq(team_id)
team_a_or_b = team_a.or(team_b)
where(team_a_or_b)
}
end
This uses arel (which ActiveRecord uses under the hood to create SQL queries) in order to find a match in either the team_a_id or team_b_id columns. If you have no idea what's going on with the above code, take a quick trip to Mastering AR and Arel to get up to speed.
You use the scope like this:
matches = Team_Match.has_team(team_id)
You can also use the scope on any relationship that joins to Team_Match:
matches = #scoreboard.team_matches.has_team(team_id)
Scopes can also be chained together:
matches = #scoreboard.team_matches.has_team(team_id).where(match_date: today)
The scope allows you to pass in the team id from the select box to find the matches:
def index
#selected = true
#scoreboard = Scoreboard.find(params[:scoreboard_id])
#teams = #scoreboard.teams
#matches = #scoreboard.team_matches.order("match_date DESC")
#matches = #matches.paginate(page: params[:page], per_page: 5)
# we apply the scope (if present) to only show the matches for the selected team
if (team_id = params[:selected_team_id])
#matches = #matches.has_team(team_id)
end
end
The only change you would need to make to the above code is working out what the actual name is of the param variable that holds the selected team id.

Related

Can't Get Photos to Work in Rails

I am having all kinds of trouble doing a simple task, displaying a photo as part of a result set for each one. I'm pretty new to Rails in general, coming from another language where I can do this in seconds.
The #photo query finds 0 records even though multiple records for photos are in the DB matching the property ID.
I'm not too sure what I'm doing wrong here.
Here are my relevant files:
app/controllers/properties_controller.rb:
class PropertiesController < ApplicationController
......
def all
# gets all of the properties and uses will_paginate
#properties = Property.paginate(page: params[:page])
# should get the first positioned photo matching the results in the #properties query
#photos = Photo.where(:property_id => #properties.map(&:id)).order("position").first
end
# ......
end
app/models/property.rb:
class Property < ActiveRecord::Base
belongs_to :listing_type
belongs_to :property_type
belongs_to :status
has_many :photos
# ......
end
app/models/photo.rb:
class Photo < ActiveRecord::Base
mount_uploader :photoname, PhotoUploader
belongs_to :property
acts_as_list scope: :property_id
validates :photoname, presence: true
validates :property_id, presence: true
end
details.html.erb:
<% #properties.reverse_each do |property| %>
<div class="item col-md-4">
<div class="image">
<%= link_to property_path(property) do %>
<span class="btn btn-default"><i class="fa fa-file-o"></i> Details</span>
<% end %>
<%= image_tag(property) %>
</div>
# ......
<% end %>
Since you have a has_many realtion in the Property, you have to just access to the relation of the property to read all teh photoes:
photo = #property.photos.order("position").first
Well if you need to grab all the photoes of properties, use include to properties grab:
#properties = Property.includes(:photos).paginate(page: params[:page]).reverse
the include is needed to avoid N + 1 problem, then try replacing it with a first photo:
#photos = #properties.map { |pr| pr.photos.order("position").first }
Mpve reverse to controller, and to use #photos along with #properties use index:
<% #properties.each.with_index do |property, i| %>
#...
<%= image_tag(#photos[i]) %>
<- end >
NOTE that the code selecting an image pr.photos... be better moved to a decorator (see gem draper).
After a TON of help from #МалъСкрылевъ, which also had me learning a few new ways of "thinking" this in general, his answer led me to rethink what I was doing and go back to simply starting over and reconstructing what ended up being VERY basic. All I needed to do was make the photo query for the first photo in my loop over the properties. DUH! Here's what I did in case it helps some other poor new Rails developer!
properties_controller.rb
class PropertiesController < ApplicationController
......
def all
# gets all of the properties and uses will_paginate
#properties = Property.paginate(page: params[:page])
......
end
details.html.erb
<% #properties.each do |property| %>
<div class="item col-md-4">
<% #photo = Photo.where(property_id: property.id).order("position").first %>
<div class="image">
<%= link_to property_path(property) do %>
<span class="btn btn-default"><i class="fa fa-file-o"></i> Details</span>
<% end %>
<%= image_tag(#photo.photoname.medium) %>
</div>
......

Rails 4 problems with updating/deleting fields in a model with nested fields

I've been working on introducing a nested form in my app following Railscast episode 196 http://railscasts.com/episodes/196-nested-model-form-revised and the remastered version for rails 4 https://github.com/dnewkerk/nested-model-form.
Let's say we have a 1-to-many association between receipts and articles.
Here's how their models look like:
receipt.rb:
class Receipt < ActiveRecord::Base
has_many :articles, dependent: :destroy
accepts_nested_attributes_for :articles, allow_destroy: true, reject_if: :all_blank
belongs_to :shop
belongs_to :user
def display_name
self.name
end
end
article.rb:
class Article < ActiveRecord::Base
belongs_to :receipt
def name_with_brand
"#{name} #{brand}"
end
end
Here's how the receipts_controller.rb looks like:
class ReceiptsController < ApplicationController
before_action :set_shop, only: [:show, :edit, :update, :destroy]
respond_to :html, :xml, :json
def index
#receipts = current_user.receipts
respond_with(#receipts)
end
def show
respond_with(#receipt)
end
def new
#receipt = Receipt.new
2.times do
#receipt.articles.build
end
respond_with(#receipt)
end
def edit
end
def create
#receipt = Receipt.new(receipt_params)
user_id = current_user.id
#receipt.articles.each do |article|
warranty_time = article.warranty_time
article.warranty_expires = #receipt.shopping_date.advance(months: warranty_time)
end
#receipt.user_id = user_id
#receipt.save
respond_with(#receipt)
end
def update
if #receipt.update(receipt_params)
redirect_to #receipt, notice: "Successfully updated receipt."
else
render :edit
end
end
def destroy
#receipt.destroy
respond_with(#receipt)
end
private
def set_shop
#receipt = Receipt.find(params[:id])
end
def receipt_params
params.require(:receipt).permit(:name, :shopping_date, :shop_id, :file,
articles_attributes: [:id, :name, :brand, :warranty_time, :warranty_expires,
:receipt_id, :_destroy])
end
end
Here's how my receipts.js.coffee looks like:
jQuery ->
$('#receipt_shopping_date').datepicker(dateFormat: 'yy-mm-dd')
$.datepicker.setDefaults($.datepicker.regional['PL']);
$('form').on 'click', '.remove_fields', (event) ->
$(this).prev('input[type=hidden]').val('1')
$(this).closest('fieldset').hide()
event.preventDefault()
$('form').on 'click', '.add_fields', (event) ->
time = new Date().getTime()
regexp = new RegExp($(this).data('id'), 'g')
$(this).before($(this).data('fields').replace(regexp, time))
event.preventDefault()
$(document).ready(jQuery)
$(document).on('page:load', jQuery)
And finally here's my view for adding a new receipt and adding articles to it:
(other fields...)
<div class="large-12 columns">
<p>Add articles on the receipt:</p>
</div>
<div class="field">
<div class="large-12 columns">
<%= f.fields_for :articles do |builder| %>
<div class="article_fields">
<%= render "article_fields", :f => builder %>
</div>
<% end %>
<%= link_to_add_fields "Add another article", f, :articles %>
</div>
</div>
<div class="actions">
<div class="large-12 columns">
<%= f.submit "Sumbit Receipt" %>
</div>
</div>
<% end %>
As you can see I'm using a link_to_add_fields helper method, here's how it looks like:
def link_to_add_fields(name, f, association)
new_object = f.object.send(association).klass.new
id = new_object.object_id
fields = f.fields_for(association, new_object, child_index: id) do |builder|
render(association.to_s.singularize + "_fields", f: builder)
end
link_to(name, '#', class: "add_fields small button", data: {id: id, fields: fields.gsub("\n", "")})
end
And finally as you can see I'm generating a partial called _article_fields.html.erb, here's how it looks like:
<fieldset style="width:1400px">
<legend>new article</legend>
<div class="large-2 columns">
<%= f.text_field :name%>
</div>
<div class="large-2 columns">
<%= f.text_field :brand%>
</div>
<div class="large-2 columns">
<%= f.text_field :warranty_time, class: "warranty" %>
</div>
<div class="large-12 columns">
<%= link_to "delete article", '#', class: "remove_fields button small alert" %>
</div>
</fieldset>
Now let's get down to my problem. When creating a receipt for the first time everything is fine - I see the number of articles in a receipt in my show view and the warranty_expires in every article.
Things get messed up when I'm updating or deleting article_fields through receipts/edit:
1) When I edit a receipt and want to remove any of the articles (although visually in my edit view they disappear - the JS seems to work), the fields are not removed from my DB, thus the show view remains exactly the same like it was before.
Simple example:
before edit: my receipt has 6 articles
during edit: pressed 3 times the 'delete article' button, so the receipt should have 3 articles
after edit: the receipt has still 6 articles
2) When I edit a receipt and want to add an another article field, the value warranty_expires is always nil - how can I make it work with the update action in my receipts controller? I tried using the same code as in my create action:
#receipt.articles.each do |article|
warranty_time = article.warranty_time
article.warranty_expires = #receipt.shopping_date.advance(months: warranty_time)
end
but it won't work. Any idea why?
Simple example:
A receipt has 2 articles already. When I add the 3rd one I get the following result:
3 articles - all of them have names and warranty_time fields, but only 2 of them have a warranty_expires value.
All of your help would be deeply appreciated. Thank you in advance.
I think you can use some callbacks in your Article model for solve your 2nd problem,
Start deleting this, try to keep your controller as simple as possible and handle the operations in your models.
#receipt.articles.each do |article|
warranty_time = article.warranty_time
article.warranty_expires = #receipt.shopping_date.advance(months: warranty_time)
end
In your Article Model add some callbacks
class Article < ActiveRecord::Base
belongs_to :receipt
def name_with_brand
"#{name} #{brand}"
end
before_update :set_warranty_expires
before_create :set_warranty_expires
def set_warranty_expires
self.warranty_expires = self.receipt.shopping_date.advance(months: self.warranty_time)
end
end
Code its not tested, but its the idea. Hope it helps.
Check this two gems simple_form and nested_form this helps a lot when writing large forms and they play well with each other.
Update: I managed to fix the 1st issue.
The fix for the 1st solution is the following:
the hidden field :_destroy was missing for removing articles.
So I needed to changed the following code:
<div class="large-12 columns">
<%= link_to "delete article", '#', class: "remove_fields button small alert" %>
</div>
to:
<div class="large-12 columns">
<%= f.hidden_field :_destroy %>
<%= link_to "delete article", '#', class: "remove_fields button small alert" %>
</div>
Still have no idea how to fix the 2nd issue though.
First of all i noticed that, you have a loop in your reciepts_controller new action
2.times do
#receipt.articles.build
end
This means that, article will be created for only 2 times for that reciept.
Better remove the loop so that you can add as many as article you want.
For Issue number two add bellow line to edit action to your controller
#receipt.articles.build
I guess that would help you.
Also nested_form is a great gem for manage this kind of tasks.
https://github.com/ryanb/nested_form
Check it out.
This is an issue where .hide() is called in receipts.js.coffee. The simplest way to fix this I can think of is to simply replace .hide() with .remove()

Rails link_to an .each join

I have this code which associates one table to another. Using Set it collects all data and only shows it once if there are other similar values.
genre_names = Set.new
<% #pm_relationships = PmRelationship.where(:people_id => #person.id) %>
<% #pm_relationships.each do |pm_relationship| %>
<% #movie=Movie.find(pm_relationship.movie_id) %>
<% #mg_relationships = MgRelationship.where(:movie_id => #movie.id) %>
<% #mg_relationships.each do |mg_relationship| %>
<% #genre=Genre.find(mg_relationship.genre_id) %>
<% genre_names.add(#genre.name) %>
<% end %>
<% end%>
# actual view code
<ul class="basic-info-genres">
<%= "<li>#{genre_names.to_a.join('</li><li>')}</li>".html_safe %>
</ul>
My problem here is how a link_to would work in the print code provided
<%= "<a><li><button><span>#{genre_names.to_a.join('</span></button></li></a><a><li><‌​‌​button><span>')}</span></button></li></a>".html_safe %>
How to make the above print to have this link to /genres/<%=#genre.id%>?
I've tried
<%= "<a href='/genres/#{#genre.id}'><li><button><span>#{genre_names.to_a.join('</span></‌​‌​button></li></a><a><li><button><span>')}</span></button></li></a>".html_safe %>
but this only links to the first genre shown
Any ideas?
Thanks
Add to genres the whole #genres, not only the #genre-name, then you can use the each-loop in the view code.
For the Controller (you should have your programmic logic there):
#genres = Set.new
#pm_relationships = PmRelationship.where(:people_id => #person.id)
#pm_relationships.each do |pm_relationship|
movie=Movie.find(pm_relationship.movie_id)
#mg_relationships = MgRelationship.where(:movie_id => movie.id)
#mg_relationships.each do |mg_relationship| %>
genre=Genre.find(mg_relationship.genre_id) %>
#genres.add(genre) %>
end
end
For the view:
<ul class="basic-info-genres">
<% #genres.each do |genre| %>
<li><%= link_to genre.genre_name, "#{root_url}genres/#{genre.id}" %></li>
<% end %>
</ul>
If I understand correctly, you want to link all genres that are associated with a movie.
To do this, in your controller, load the genres for a user's movies. With the correct association setup, this is as easy as #person.genres. So let's do that.
class Person < ActiveRecord::Base
has_many :pm_relationships
has_many :movies, through: :pm_relationships
has_many :genres, through: :movies
end
class PmRelationship < ActiveRecord::Base
belongs_to :person
belongs_to :movie
end
class Movie < ActiveRecord::Base
has_many :mg_relationships
has_many :genres, through: :mg_relationships
end
With that setup, in your controller, just setup an instance variable to list all genres for #person
#genres = #person.genres
Then in your view, use the block form of link_to so it's easier to code your html
<ul class="basic-info-genres">
<% #genres.each do |genre| %>
<%= link_to genre do %>
<li>
<button>
<span><%= genre.name %>‌</span>
</button>
</li>
<% end %>
<% end %>
</ul>
Basically, you can't have any direct child element instead of li within ul. You will need to have <a> within li, however, we can apply css to <a> so that it looks like whole 'li' have the clickable link.
From your actual code, update the snippet.
<ul class="basic-info-genres">
<% genre_names.each do |genre_name| %>
<li>
<%= link_to genre_name, genre_path(#genre), :class => 'clickable-li' %>
</li>
<% end %>
</ul>
You need to set your <a> to display: block;
# css file
.clickable-li {
display: block;
}

how to render this partial in rails

i am trying to render this partial of the association belongs_to and has_many i.e
my model is as thus
activity Model
class Activity < ActiveRecord::Base
has_many :talks
end
talk model
class Talk < ActiveRecord::Base
belongs :activity
end
in my talk controller i have
#activities = Activity.where(user_id: [current_user.friend_ids, current_user])
#talks = Talk.find(:all, :order => "created_at DESC")
and in view i have
<% activity.talks.each do |c| %>
<div class="media">
<a class="pull-left" href="#">
<%= image_tag(c.user.image.url(:tiny),:style=> "width: 100%;")%>
</a>
<div class="media-body">
<strong><%= c.user.username %></strong> <%= c.details %><br>
<small><span class="muted"><%="#{time_ago_in_words(c.created_at)} ago "%></span></small>
</div>
</div>
<% end %>
this displays all the talk for each activity
how do i create a partial of <% activity.talks.each do |c| %>
Create a partial in
app/views/talks/_talk.html.erb
and call
<%= render activity.talks %>
This will render one _talk.html.erb partial for each talk in the activity.talks collection, which will have access to a talk variable.
Furthermore, you can optimize the code by preventing N + 1 queries. In your controller,
#activities = Activity.includes(:talks).where(user_id: [current_user.friend_ids, current_user])
Notice the includes(:talks). Read more about eager loading in the docs linked above.

Rails 3.2 Ajax Update Div when Text Field Populated Many to Many Form

In the end I would like a text field that passes a client_id to the partial. I would like to do this asynchronously so the shipment_products partial would dynamically change when the textfield value was updated. What is the best way to do this?
In index.html.erb
<!-- Text Field Here-->
<div id="available_products">
<%= render "shipment_products" %>
</div>
In _shipment_products.html.erb
<div id="shipment_products_container">
<h3>Assign Products to Ship<\h3>
<ul class="shipment_products" id="shipment_products">
<% Product.by_client(client_id).each do |product|%> <!-- TextField value passed here -->
<%= content_tag_for :li, product, :value => product.id do %>
<%= hidden_field_tag("shipment[product_ids][]", product.id) %>
<%= product.product_name %>
<% end %>
<% end %>
<\ul>
</div>
Model relationships:
Models and Relationships
Shipment has_many :products :through => :shipment_products
Product has_many :shipments :through => :shipment_products
ShipmentProducts belongs_to :shipment, belongs_to :product
Product belongs_to :client
Client has_many :products
This is similar to what I want in the end.
Since I do not know what's in your controller and how you make routes, I made some suggestions. Change a few things to the actual. I assume that you do not need to change index action (if only to add respond_to js)
index.html.erb
<%= form_tag shipment_path, remote: true do %> - points to index action
<%= text_field_tag :client_id %>
<%= submit_tag :load %>
<% end %>
<div id="available_products">
<%= render "shipment_products" %>
</div>
index.js.erb
$('#available_products').html("<%= j render "shipment_products" %>");
I think you should use nested model concept for this please refer-
http://railscasts.com/episodes/197-nested-model-form-part-2?view=asciicast

Resources