0

I know the title sounded a bit wonky, but this is what I am trying to do.

I have two models - Job and Company. Someone can create a Job listing, but before the listing is saved, it should be associated with a company. This company can be newly created, or should be populated from companies the current_user has previously created.

A job belongs_to :company, and a company has_many :jobs.

I know I could do two different views, and just send the user to two different actions on two different controllers, but I would like to simplify it and just have everything done in one view.

When they go to /jobs/new, they should see the normal jobs/_form.html.erb partial, but I would love to be able to show either a Company dropdown for existing companies or new company creation fields that the user fills out and that new company gets associated with this new job that is being created.

This is what my jobs/_form.html.erb partial looks like:

<%= simple_form_for(@job) do |f| %>
    <%= f.input_field :title %>
    <%= f.input_field :salary %>
    <%= f.input_field :description, as: :text %>
    <%= f.input_field :apply_description, as: :text %>
    <%= f.input_field :language %>
    <%= f.input :premium, as: :boolean, inline_label: true, label: "Make this listing stand out",  class: "form-control" %>
    <%= f.button :submit, class: "btn btn-lg btn-primary pull-right" %>
<% end %>

This is what my companies/_form.html.erb looks like:

<%= simple_form_for(@company) do |f| %>
    <%= f.input :name %>
    <%= f.input :logo %>
    <%= f.input :description %>
    <%= f.input :city %>
    <%= f.input :state %>
    <%= f.input :country %>
    <%= f.input :email %>
    <%= f.button :submit %>
<% end %>

How can I combine them into 1 form, or some other unified workflow within 1 view where it works seamlessly to the user?

Edit 1

Based on Jay-Ar's answer, this is what is happening now.

When I select New Company in the Company Dropdown, it doesn't show the fields in the <fieldset>. I believe that's the case because there is no value=0 in the select tags rendered in the HTML, as can be seen in the screenshot below.

enter image description here

Edit 2

After attempting the latest update from Jay-Ar, the JS still doesn't work and the form is no longer hidden.

This is what it looks like on first load, and always:

enter image description here

Ideally I would like for this form not to show up until they have chosen "New Company" from the dropdown.

This is what the HTML looks like now:

enter image description here

The JS does appear in the source, so I know it is being loaded in the asset pipeline correctly.

marcamillion
  • 32,933
  • 55
  • 189
  • 380

2 Answers2

2

You should use nested model forms, I suggest you watch this video it has a very detailed explanation of the steps to do it for a question / answer models but its the same thing.

pshoukry
  • 755
  • 5
  • 14
2

UPDATED & TESTED WORKING

  • Now supports Turbolinks

views/jobs/_form.html.erb

<%= simple_form_for(@job) do |f| %>
  <%# IMPORTANT: use `include_blank` below instead of `prompt` because prompt does not seem to work when updating, but only works when creating %>
  <%= f.association :company, collection: [['New Company', nil]] + Company.pluck(:name, :id), include_blank: 'Please Select Company', input_html: { id: 'company-select' } %>
  <fieldset id='job-fields'>
    <%= f.simple_fields_for :company, @job.build_company do |ff| %>
      <%= ff.input :name %>
      <%= ff.input :logo %>
      <%= ff.input :description %>
      <%= ff.input :city %>
      <%= ff.input :state %>
      <%= ff.input :country %>
      <%= ff.input :email %>
    <% end %>
  </fieldset>
  <%= f.input_field :title %>
  <%= f.input_field :salary %>
  <%= f.input_field :description, as: :text %>
  <%= f.input_field :apply_description, as: :text %>
  <%= f.input_field :language %>
  <%= f.input :premium, as: :boolean, inline_label: true, label: "Make this listing stand out",  class: "form-control" %>
  <%= f.button :submit, class: "btn btn-lg btn-primary pull-right" %>
<% end %>

JS

// for Rails 5, use turbolinks:load instead of page:change below
$(document).on('page:change', function(){
  var companySelect = $('#company-select');
  var jobFields = $('#job-fields');

  companySelect.change(function(){
    // if selected option is the second option
    if ($(this).find('option:selected').index() == 1)
      jobFields.show().find(':input').prop('disabled', false);
    else
      jobFields.hide().find(':input').prop('disabled', true);
  })
  // call change immediately so this still works when already updating and not just creating.
  companySelect.change();
})

controllers/jobs_controller.rb

class JobsController < ApplicationController
  ...
  def create
    @job = Job.new(job_params)
    ...
  end

  def update
    @job = Job.find(params[:id]) # not needed if using before_action #set_job
    if @job.update(job_params)
    ...
  end

  private

  def job_params
    params.require(:job).permit(:id, :title, :salary, :description, :apply_description, :language, :premium, :company_id, company_attributes: [:name, :logo, :description, :city, :state, :country, :email]
  end
end

models/job.rb

class Job < ActiveRecord::Base
  accepts_nested_attributes_for :company
  validates :company, presence: true
  ...
end

You should get something like the following:

  • On fresh load:

fresh page load

  • After selecting 'New Company' option':

after selecting 'New Company' option

Jay-Ar Polidario
  • 6,463
  • 14
  • 28
  • I really like where you are going here. The JS doesn't seem to work for me. When I choose the 'New Company' option within the select dropdown, I don't see the fields within the `
    ` show up.
    – marcamillion Jun 14 '16 at 22:08
  • I think I know why that is. Refresh the question to see my update based on what you suggest. – marcamillion Jun 14 '16 at 22:12
  • I made a lot of changes to jobs/_form.html.erb and the JS to solve some issues I encountered when replicating the scenario. I tested this now working. – Jay-Ar Polidario Jun 15 '16 at 09:25
  • Still doesn't work :( I have updated the question with more details about what is happening after these latest changes. – marcamillion Jun 15 '16 at 19:35
  • @marcamillion I updated my answer to handle your request to have 'New Company' fields to only show up when selected. Tested this also working as well. But this still does not solve why yours is not working. Can you check if the actual function is being called? like can you add `alert('IM INSIDE PAGE:CHANGE')` at the start? --> i.e. `$(document).on('page:change', function(){ alert('IM INSIDE PAGE:CHANGE');` and can you modify it also to be something like the following as well? `companySelect.change(function(){ alert('IM INSIDE COMPANYSELECT CHANGE FUNCTION')` Tell me if these are called – Jay-Ar Polidario Jun 16 '16 at 09:19
  • are you using turbolinks? if not can you try `$(function(){` instead of `$(document).on('page:change', function(){` – Jay-Ar Polidario Jun 16 '16 at 09:24
  • I am using TL, I have the `turbolinks (5.0.0.beta2)` gem installed and in my JS & stylesheet tags, I have the following: `'data-turbolinks-track': 'reload'`. However, I also tried adding that alert and I don't see any alerts appear on the page when I use that dropdown. For what it's worth, I am putting this inside `app/assets/javascripts/jobs.js` where `jobs` is my controller & the view this form appears on. – marcamillion Jun 16 '16 at 23:27
  • Are you certain that that JS file (jobs.js) is being called? i.e. at the very start inside jobs.js, can you try adding `alert('jobs.js is called');` and if you don't get that alert message, that means that file isn't actually used. However if the alert message is shown, then maybe the `page:change` event is not being triggered. Then, instead of `$(document).on('page:change', function(){`, can you try `$(function(){` instead? Lastly, if that JS file doesn't get called, make sure that your `application.js` has either only `//= require jobs` or just simply `//= require_tree .` – Jay-Ar Polidario Jun 17 '16 at 08:25
  • One more thing, if that jobs.js file still isn't being called, then you may simply just embed my JS script into your `views/jobs/_form.html.erb` by something like: `` Note however that embedding the script is not best practice standard, but should hopefully work as a last resort. – Jay-Ar Polidario Jun 17 '16 at 08:34
  • Ok....so `jobs.js` is being called (I saw the alert 'jobs.js is being called'). So I changed the `page:change` function to just `$(function() {` and that works now. However, now the behavior is weird. The form disappears when I select `New Company`, but once I choose an existing Company, it shows the form fields, which should be the opposite right? i.e. it should show the fields only when 'New Company' is selected. – marcamillion Jun 17 '16 at 18:55
  • Ok once I change the `if ($(this).find('option:selected').index() == 1)` to `0`, it works now. How do I get this form to be TL friendly? Also, how do I get it to render the company `_form.html.erb` partial rather than manually specifying the fields within the `jobs/_form.html.erb` partial? – marcamillion Jun 17 '16 at 18:59
  • It should be index() == 1, because index=0 is 'Please Select a Company', index=1 is 'New Company', index=2,3,4,etc... are your Companies. Maybe you forgot the `collection: ` part. Check again the `_form.html.erb` just in case you missed something that I have updated. – Jay-Ar Polidario Jun 18 '16 at 17:17
  • What do you mean by "TF friendly"? If you want to reuse `companies/_form.html.erb` into `jobs/_form.html.erb`, you'd have to modify it. You cannot use it directly. It's going to be a little messy if you still plan to reuse the partial. – Jay-Ar Polidario Jun 18 '16 at 17:22
  • You are right. I missed the `collection` attribute. Once I added that in, it works like a charm now. – marcamillion Jun 18 '16 at 23:46
  • What I meant by "TF friendly" is TurboLinks friendly. Meaning, say I am on the page with the form and I go to another page without doing a manual page refresh, what happens is that TurboLinks just swaps out the content of the page (not the entire page). So as a result of that, the form becomes broken. For example, the company portion of the form remains visible and doesn't disappear when I choose something else in the dropdown. How do I make it work for `page:load` too? You had tried doing it initially but that didn't work. Now that is removed, it isn't TF-5 friendly. – marcamillion Jun 19 '16 at 10:03
  • Oh turbolinks-friendly i see. I just tried googling for it now, and maybe the reason why yours is not working is because you're using Rails 5 and the Turbolinks event naming is changed into `turbolinks:load` instead of `page:load` I found [here](http://stackoverflow.com/questions/18770517/rails-4-how-to-use-document-ready-with-turbo-links). Can you try that if it will work? – Jay-Ar Polidario Jun 19 '16 at 18:03