Ransack search multiple classes - ruby-on-rails

I have a working search form built using Ransack with two separate search fields for two different Classes like so:
<%= search_form_for #q do |f| %>
<%= f.label :tags_id_in, 'Tags' %>
<%= f.select :tags_id_in, Tag.all.map{ |u| [u.name, u.id] }, { include_blank: "Tags" } %>
<%= f.label :sector_id_eq, 'Sector' %>
<%= f.select :sector_id_eq, Sector.all.map { |w| [w.name, w.id] }, {include_blank: 'Any'} %>
<%= f.submit "Search" %>
<% end %>
Each of these Classes are linked to a Company Class
class Company < ApplicationRecord
belongs_to :sector
has_many :company_tags
has_many :tags, through: :company_tags
accepts_nested_attributes_for :company_tags
end
I am trying to combine the two select fields into one. So far I can build the view for this like so:
<% combined = Sector.all + Tag.all %>
<%= f.select :combined, combined.map { |w| [w.name, w.id] }.sort, {include_blank: 'Any'} %>
Whilst the above works at displaying a single search form, there is no functionality to it. Can anyone help with this? Thanks in advance.

Hooray, Ransack. It seems like this answer from 2013 is still valid:
Search multiple models at once with Ransack
But if you're adventurous, maybe try this:
https://github.com/activerecord-hackery/ransack/issues/131
You can add associations' columns/ransackers to the list of attributes by passing their names to #attribute_select via the :associations option.

I'm not familiar with ransack, but maybe it's worth creating a new stand-in class? Just a PORO (plain ol' ruby object) go-between for the view and the model, using ransack for your queries rather than calling them directly from the form. You could build the tag list in that object as well.
You've got a lot of logic in your view, many frown on putting ActiveRecord queries there. A form object would fix that, and keep your controller simple as well.

Related

Complex Rails Form Best Practices

I've got a relatively complex form I'm trying to code efficiently. Most online examples of nested forms deal with very clear hierarchical relationships, mine does not.
Below is the data model. The essential job of the form is to create a "Job Entry" record while at the same time creating a new "Entity" record - which is a person. Several relationships come to bear in this form.
A "Job" is already created. The Job has 1-to-many "Questions" which exist before the user hits this form. However, they must fill in "Answers" to the questions. They also choose one of many pre-created "Job Roles".
The question is how to leverage "form_with" and "fields_for" for all these inter-related models.
My assumption is to ditch built-in helpers and just use a form_tag and roll everything together manually. But maybe there is a "correct" way to roll forms that do not necessarily abide by parent-child relationships? In my example, there is no pure top-level object to start with since many child objects already have records, but maybe I am wrong and Entity should be the starting point?
Entity has_many Job_Roles
Entity has_many Job_Entries
Job has many Job_Roles
Job has_many Job_Entries
Job has_many Questions
Question has many Answers
Answers belong_to Entity
Agency has_many Job_Entries
etc...
There is no need to ditch the built-in helper: Rails has thought about that, it's called nested forms.
Here is an example:
<%= form_with model: #job do |f| %>
Job entries:
<ul>
<%= f.fields_for :job_entries do |je_form| %>
<li>
<%= je_form.label :kind %>
<%= je_form.text_field :kind %>
<%= je_form.label :street %>
<%= je_form.text_field :street %>
...
</li>
<% end %>
</ul>
<% end %>
You can nest as many children forms as you'd like using fields_for. Don't forget to use accepts_nested_attributes_for in the parent models.
Nested forms as Mike proposed are a rails-way solution of your problem. It is ok - but for complex forms, with lot of validations, it may not be the best solution). You could consider using a FormObject pattern instead.
FormObject is a simple ruby class that uou can keep it i.e. in Forms folder and use as below:
class JobEntryForm
include ActiveModel::Model
attr_accessor :customer_id, :agency_id, :name, :question_text #you can use atributes from different models
validates :customer_id, presence: true #you can validate yu attributes as you want - your in necessity to use model validation
def initialize(attributes:)
#customer_id = attributes[:customer_id]
#agency_id = attributes[:agency_id]
#name = attributes[:name]
#question_text = attributes[:question_text]
end
#implement whatever you need
end
than in you controller:
#form = JobEntryForm.new
and you your view:
<%= form_for #form do |f| %>
<%= f.label :customer_id, 'Customer' %>:
<%= f.text_field :customer_id %>
...
<%= f.submit %>
<% end %>
And - at the end - in your controller create method:
def create
#form = CreateJobEntry.new.call(attributes: form_params) #service object to keep your controller clean.
end

How to display only unchecked items in active admin for has many relationship?

I am using Ruby on Rails 5 with active admin as a backend for resources management. I need to show the only unchecked items for a check_boxes field all the time in new and edit action. Instead of running a complex query for the collection I think this is the best way to manage. All of my associated models stuff related to this are working fine.
It should show only 2nd item if it is not checked already.
Right now my code snippet is
f.input :subscribers, :as => :check_boxes, :collection => Subscriber.all.collect {|subscriber| [subscriber.email, subscriber.id]}
Is there any way in active admin to display only unchecked values ?
Have you considered using collection_check_boxes for this case?
It would look something like this:
<%= f.collection_check_boxes(:subscribers_ids, Subscriber.all, :id, :email) do |b| %>
<% if !b.check_box.include?(checked="checked") %>
<%= b.label %>
<%= b.check_box %>
<% end %>
<% end %>
I think that should solve your problem. If you want to learn more about collection_check_boxes

Rails: Create Model and join table at the same time, has_many through

I have three Models:
class Question < ActiveRecord::Base
has_many :factor_questions
has_many :bigfivefactors, through: :factor_questions
accepts_nested_attributes_for :factor_questions
accepts_nested_attributes_for :bigfivefactors
end
class Bigfivefactor < ActiveRecord::Base
has_many :factor_questions
has_many :questions, through: :factor_questions
end
and my join-table, which holds not only the bigfivefactor_id and question_id but another integer-colum value.
class FactorQuestion < ActiveRecord::Base
belongs_to :bigfivefactor
belongs_to :question
end
Creating an new Question works fine, using in my _form.html.erb
<%= form_for(#question) do |f| %>
<div class="field">
<%= f.label :questiontext %><br>
<%= f.text_field :questiontext %>
</div>
<%= f.collection_check_boxes :bigfivefactor_ids, Bigfivefactor.all, :id, :name do |cb| %>
<p><%= cb.check_box + cb.text %></p>
<% end %>
This let's me check or uncheck as many bigfivefactors as i want.
But, as i mentioned before, the join model also holds a value.
Question:
How can I add a text-field next to each check-box to add/edit the 'value' on the fly?
For better understanding, i added an image
In the console, i was able to basically do this:
q= Question.create(questiontext: "A new Question")
b5 = Bigfivefactor.create(name: "Neuroticism")
q.bigfivefactors << FactorQuestion.create(question: q, bigfivefactor: b5, value: 10)
I also found out to edit my questions_controller:
def new
#question = Question.new
#question.factor_questions.build
end
But i have no idea how to put that into my view.
Thank you so much for your help!
Big Five Factors model considerations
It looks like your Bigfivefactors are not supposed to be modified with each update to question. I'm actually assuming these will be CMS controlled fields (such that an admin defines them). If that is the case, remove the accepts_nested_attributes for the bigfivefactors in the questions model. This is going to allow param injection that will change the behavior sitewide. You want to be able to link to the existing bigfivefactors, so #question.factor_questions.first.bigfivefactor.name is the label and #question.factor_questions.first.value is the value. Notice, these exist on different 'planes' of the object model, so there wont be much magic we can do here.
Parameters
In order to pass the nested attributes that you are looking for the paramater needs to look like this:
params = {
question: {
questiontext: "What is the average air speed velocity of a sparrow?",
factor_questions_attributes: [
{ bigfivefactor_id: 1, value: 10 },
{ bigfivefactor_id: 2, value: 5 } ]
}
}
Once we have paramaters that look like that, running Question.create(params[:question]) will create the Question and the associated #question.factor_questions. In order to create paramaters like that, we need html form checkbox element with a name "question[factor_questions_attributes][0][bigfivefactor_id]" and a value of "1", then a text box with a name of "question[factor_question_attributes][0][value]"
Api: nested_attributes_for has_many
View
Here's a stab at the view you need using fields_for to build the nested attributes through the fields for helper.
<%= f.fields_for :factor_questions do |factors| %>
<%= factors.collection_check_boxes( :bigfivefactor_id, Bigfivefactor.all, :id, :name) do |cb| %>
<p><%= cb.check_box + cb.text %><%= factors.text_field :value %></p>
<% end %>
<% end %>
API: fields_for
I'm not sure exactly how it all comes together in the view. You may not be able to use the built in helpers. You may need to create your own collection helper. #question.factor_questions. Like:
<%= f.fields_for :factor_questions do |factors| %>
<%= factors.check_box :_destroy, {checked => factors.object.persisted?}, '0','1' %> # display all existing checked boxes in form
<%= factors.label :_destroy, factors.object.bigfivefactor.name %>
<%= factors.text_box :value %>
<%= (Bigfivefactor.all - #question.bigfivefactors).each do |bff| %>
<%= factors.check_box bff.id + bff.name %><%= factors.text_field :value %></p> # add check boxes that aren't currently checked
<% end %>
<% end %>
I honestly know that this isn't functional as is. I hope the insight about the paramters help, but without access to an actual rails console, I doubt I can create code that accomplishes what you are looking for. Here's a helpful link: Site point does Complex nested queries

Using non associated models data in a form

Is it possible to access another models attributes without having associations? For example I want to create a Prediciton record via a form using the fixture models attributes
<%= form_for #prediction do |f| %>
<%= f.fields_for :fixtures, #fixtures do |builder| %>
<%= builder.text_field :home_team %> VS <%= builder.text_field :away_team %><%= f.text_field :home_score %><%= f.text_field :away_score %><br>
<% end %>
<% end %>
how would i get the attributes of the fixture model without associating the two models?
Thanks
It's much easier if you create the association. If you are not going to create the association, such in the case where you are using a view not backed by a model and your are pulling in and modifying various models from it (assumption I am making) you can do something similar to this:
First make sure you setup routes.rb for whichever methods you are planning to use against the various models.
predictions model
#fixtures = Fixture.all
or specific attributes example
#fixtures = Fixture.select([:home_team, :away_team, :home_score, :away_score]).all
The above is if you are updating another models records. You will also need to modify the create method.
Your view you would want to change from a form_for to a form_tag:
form_tag('/predictions') do
Hopefully this gets you going in the right direction.

Multiple value form tag for ruby

Here is the problem I'm having, and I have tried thinking around, but still can't figure out the solution. So I have two models
User
Data
and Experience belongs to user, and accepts nested attributes
Now here comes the problem ! I have page/form where I would like to update or insert.
so in the Data model
flavor, body
So How do I add form tag where I can specify my flavor but let user decide the body so for example, currently I have
<%= f.text_field :body, placeholder: "...." %>
So how do I do something like (wrong syntax)
<%= f.text_field :body, :flavor => "someflav" , placeholder: "...." %>
<%= f.text_field :body, :flavor => "Otherflav" , placeholder: "...." %>
and so on...
How does one achieve this ? I have looked around rails api, and but couldn't figure out how to achieve my issue.
Thanks for your consideration and time.
You need to use fields_for
Rails constructs input names that help it determine exactly what attribute goes where.
For instance:
user[datas_attributes][0][body]
Since (if I am interpreting you correctly) User has many Datas, it would look something like this:
<%= fields_for :datas do |data_fields| %>
<%= data_fields.text_field :body %>
<% end %>
There are a few things you need to do to make this work.
In your model, you need to add the following two lines:
accepts_nested_attributes_for :datas
attr_accessible :datas_attributes

Resources