Skip to content Skip to sidebar Skip to footer

Need Method Create And Not Update When Instance Actually Does Exist

Ruby 2.0.0, Rails 4.0.3 I have a _new partial. But, I render it with an instance that actually exists. This is necessary so that I can pass the instance ID around through JavaScr

Solution 1:

If I understand this correctly, you want to achieve the following:

Display a form for creating new car models. In this form, the user enters a year; the system then loads all makes for that year from the server and presents a select drop down for the user to choose a make from.

The Car model belongs_to ymm_make, so we include the selected ymm_make_id when the form is submitted.

Here is how I would solve this. I will use the standard Rails form helpers, so that we have one less abstraction layer to worry about.

The form (with Car.new):

<%= form_for [:admin, Car.new] do |f| %>
  <%= f.text_field :stock_number, autocomplete: "off", placeholder: "Stock number?" %>
  <%= text_field_tag :year_search, nil, placeholder: "Year" %>
  <%= f.select :ymm_make_id %>

  <!-- skipping models and colors here for the sake of brevity -->

  <%= f.submit "Create" %>
<% end %>

For the year search field, I use text_field_tag instead of f.text_field, because I don't want the search field value to be submitted as a part of the car when the whole form is submitted. I leave the dropdown field empty for now - we will populate that through Javascript and JSON.

For the list of makes, I'll make a resource controller that returns JSON:

class YmmMakesController < ApplicationController
  respond_to :json

  def index
    @makes = YmmMake.makes(params[:year])

    respond_with @makes
  end
end

Don't forget a route.rb entry for this controller, e.g.

namespace :admin do
  resources :ymm_makes, only: :index
end

We'll make <select> options out of our JSON in Javascript:

$("input[name=year_search]").change(function () {

    // send a GET request to /admin/ymm_makes with the 'year' parameter
    // set to the value of the year_search text field
    $.getJSON( "/admin/ymm_makes", {year: $(this).val()}, function(data) {
      var options_html = [];

      // iterate over the JSON that we received back; each entry is one 'ymm_make'
      // in JSON form
      $.each( data, function( index, make ) {
        // make a new <option> tag for each make and push it into the options_html array
        // I assume here that YmmMake has an attribute called 'name' you want to display
        options_html.push( "<option value='" + make.id + "'>" + make.name + "</option>" );
      });

      // put all our generated <options> tags into the <select> tag
      $('select#car_ymm_make_id').html( options_html.join('') );
    });

});

With all this in place, you should have a working form for creating new cars that are associated with a YmmMake model object.

A few observations about your existing code:

Nested forms

<%= simple_form_for [:admin, @car] do |f| %>
  <%= render partial: "makes", locals: {form: 'new_admin_car', car_id: car_id} %>
<% end %>

If I see this correctly, your 'makes' partial also contains a form, so you are creating a <form> nested in a <form>. HTML does not allow that.

Wrong closing tag order

      <div class="col-xs-6 col-sm-3">
        <br/>
      <input type="submit" form="new_admin_car" value="Create Car" class="btn btn-default btn btn-primary">
  <% end %>
  </div>

The closing </div> must come before the <% end %>. If you build invalid HTML, you risk strange visual behaviour and Javascript errors.

Redundant arguments

<%= simple_form_for [:admin, @car],
                      html: {id: 'new_admin_car', class: 'form-vertical', method: post},
                      do |f| %>

"id" and "method" should be the default values. Leaving them out makes the code easier to read.

Invalid <input> attributes through input_html

<%= f.input(:stock_number, {input_html: {form: 'new_admin_car', car: @car, value: nil}, autocomplete: :off, placeholder: 'Stock number?'}) %>

I'm not familiar with simple_form, but from what I see, input_html is used to add attributes to the input element. Lines like the above would thus produce a text input with an invalid car attribute. The form attribute should not be necessary anymore as soon as you remove the nested form.

Wrong HTTP method

$.post('/admin/cars/make_list/'

You load your makes through a POST AJAX request. For requests that only return data, but do not change anything, GET is usually more appropriate.


Solution 2:

In your form you can specify the path the submit button should go to:

<%= simple_form_for [:admin, @car], url: create_action_path

...

Solution 3:

I think your answer is fairly simple, to create instead of update, just clear the model's id.
Example: @car.id = nil


Post a Comment for "Need Method Create And Not Update When Instance Actually Does Exist"