Zoinks10
Zoinks10

Reputation: 629

Why is my Rails Controller expecting JSON from the format.js call?

I'm adding a new Sale_Contact to my Rails app using a Bootstrap 3 modal and AJAX. It isn't working - I'm getting a 200 OK code returned from the server, and the Object is being added to the database, but because there is no JSON returned (the create action just sends back the create.js.erb file to the JQuery.parse.json action) then I'm firing ajaxError rather than ajaxSuccess, my modal isn't closing and the javascript to update the view with the new sale_contact object is not executed.

I can't work out why my Rails controller is expecting a Json object returned in this instance - I've basically copied this code directly from other objects I also build with modals and AJAX, which all work perfectly. Can anyone help me understand what's going wrong in this instance?

My Controller:

  def create
   @sale_contact = SaleContact.new(sale_contact_params)
   @sales_opportunity = @sale_contact.sales_opportunity

respond_to do |format|
  if @sale_contact.save
    #format.html { redirect_to @sale_contact.sales_opportunity, notice: 'Sale contact was successfully created.' }
    format.js 
    #format.json { render json :show, status: :created, location: @sale_contact }
  else
    format.html { render :new }
    format.json { render json: @sale_contact.errors, status: :unprocessable_entity }
    format.js { render json: @sale_contact.errors, status: :unprocessable_entity }
  end
 end
end

I've tested this, and I can see that the if @sale_contact.save block is being run. As you can see above I've also commented out the format.html and format.json lines but this doesn't change the outcome.

My create.js.erb file for Sale_Contacts:

    console.log('anything at all');
    //update the selection list with the new sale contact, and close the modal
    $('#sales_opp_sale_contact_modal').modal('hide')
                    .clear_previous_errors();
    resetForm($('#new_sale_contact'));
    $input = $('#contact_error');
    $input.closest('.form-group').removeClass('has-error').find('.warning-block').html('');
  //render the newly added sale contact to the table if the AJAX call succeeded
   console.log('fired');
   $(document).ajaxSuccess(function() {
    $( ".log" ).text( "Triggered ajaxSuccess handler. The ajax response was: " +
  xhr.responseText );
    $('#sales-opp-key-contacts-table-div').html("<%= escape_javascript(render :partial => 'sale_contacts/table')%>");
    //console.log(document);
    $('.alert').remove();
    $('.navbar-default .navbar-nav > li > a').removeClass('selected');
    $('#sale-contacts').DataTable({
        retrieve: true,
});

This code is never fired - instead this is passed back to the ajaxError function:

Uncaught SyntaxError: Unexpected token c
jQuery.parseJSON @ jquery.js?body=1:8483
(anonymous function) @ organizations.js?body=1:23
jQuery.event.dispatch @ jquery.js?body=1:4642
jQuery.event.add.elemData.handle @ jquery.js?body=1:4310
jQuery.event.trigger @ jquery.js?body=1:4551
done @ jquery.js?body=1:9286
jQuery.ajaxTransport.send.callback @ jquery.js?body=1:9686

As you can see, the "c" character that starts my create.js.erb file is passed in, which isn't valid JSON, and therefore the parseError is thrown.

I've added some new code to look at the returned items from the ajaxError - in case these help:

$(document).ready(function(){
 $(document).on('ajaxError', function(XMLHttpRequest, textStatus, errorThrown){
 console.log("some error" + errorThrown);
 });
 $(document).on('ajaxError', function(req, xhr, err){ 
  console.log('my message' + err + xhr.status);
 });
 $(document).bind('ajaxError', function(event, jqxhr, settings, exception){

// note: jqxhr.responseJSON undefined, parsing responseText instead
console.log(jqxhr.responseText)
$(event.data).render_form_errors( $.parseJSON(jqxhr.responseText) );
});

The output from this is:

some error[object Object]
my message[object Object]200
console.log('anything at all');
//update the selection list with the new sale contact, and close the modal
$('#sales_opp_sale_contact_modal').modal('hide')
                    .clear_previous_errors();
resetForm($('#new_sale_contact')); .....

I've edited this for brevity, you can see the first two responses show that I'm sending back objects (how do I find out which?) and the 200 OK code, and the responseText is the create.js.erb file that doesn't get fired.

I'm going crazy trying to work out why this is not working. The only difference between this object and the other objects I'm creating in this way is that my Sale_Contact is a join table (it belongs_to both Sales_Opportunities and Key_Contacts so they have_many_through this table).

In case it matters, here's the model:

class SaleContact < ActiveRecord::Base
 validates :key_contact_id, presence: true
 validates :sales_opportunity_id, presence: true
 belongs_to :key_contact
 belongs_to :sales_opportunity
 enum role: [ :not_known, :evaluator, :decision_maker, :budget_holder ]
 enum preference: [ :unknown, :supporter, :enemy ]
end

And here's my sale_contacts/show.json.jbuilder file:

json.extract! @sale_contact, :id, :key_contact_id, :sales_opportunity_id, :role, :preference, :created_at, :updated_at

Any help would be appreciated

Quick Edit Here's my routes.rb file for the relevant items, I had hoped this might be the issue:

  resources :sales_opportunities do
   resources :timeline_events, shallow: true
   resources :swots, shallow: true
   resources :sale_contacts, shallow: true
 end

Second edit

Just as an idea that came from writing this post, I ran the same ajax analysis for an ajaxSuccess event in my other modals - they also return the exact same info as above:

some error[object Object]
my message[object Object]200
whatever create.js.erb template was added successfully

So it's not the return from the server that's the issue - something else is going wrong. I'm using Bootstrap 3 modals, so maybe I need to dig into their ajax code to find the problem.

Edit 3 - adding the modal content

<%= form_for(@sale_contact, :html => {role: :form, 'data-model' => 'sale_contact'}, remote: true) do |f| %>
<% if @sale_contact.errors.any? %>
  <div id="error_explanation">
    <h2><%= pluralize(@sale_contact.errors.count, "error") %> prohibited this sale_contact from being saved:</h2>

  <ul>
  <% @sale_contact.errors.full_messages.each do |message| %>
    <li><%= message %></li>
   <% end %>
   </ul>
  </div>
<% end %>

<div class="form-group" id= "contact_error">
  <%= f.label :key_contact_id, :class => "control-label" %>
  <div id="contact_select">
  <%= f.collection_select :key_contact_id, @sales_opportunity.company.key_contacts(:full_name), :id, :full_name %>
  </div>
   <span class="warning-block"></span>
   <span class="help-block"></span>
  </div>

<div class="form-group">
  <%= f.label :role, :class => "control-label" %>
  </br>
    <%= f.select(:role, options_for_select(@roles.collect { |r| [r[0].humanize, r[0]] }, selected: @sale_contact.role), {}) %>
  <span class="help-block"></span>
</div>

<div class="form-group">
  <%= f.label :preference, :class => "control-label" %>
  </br>
    <%= f.select(:preference, options_for_select(@preferences.collect { |r| [r[0].humanize, r[0]] }, selected: @sale_contact.preference), {}) %>
  <span class="help-block"></span>
</div>


<div class="form-group">
  <%= f.hidden_field :sales_opportunity_id, :value => @sales_opportunity.id %>
</div>
  <%= f.submit "Save", class: "btn btn-large btn-success" %>
<% end %>

For full clarity - I use the exact same Modal layout for all of the modals on the site (currently have about 8 working) and I just cut/pasted the code in and changed the names. The Modal works - a new object is persisted on the database - the Jquery is where the issue lies.

Upvotes: 2

Views: 1040

Answers (1)

Zoinks10
Zoinks10

Reputation: 629

Finally I solved this issue - I used the method described in this post to isolate the issue with my create.js.erb file. I also had to delete the line

$('#sales-opp-key-contacts-table-div').html("<%= escape_javascript(render :partial => 'sale_contacts/table')%>");

From my javascript as the returned partial just left too much text/noise for the firebug console to be helpful. As soon as I did this I noticed that I was missing the }); from the end of the create.js.erb file - which meant that the javascript returned an error and threw the ajax.error function.

I feel like a total ass for wasting this much of my life and effort to fix a simple syntax error - but I did learn a lot about JQuery, AJAX, Rails controllers and debugging during the exercise.

Upvotes: 1

Related Questions