0

I have a button on my new and edit views that sends a post request to my Letter controller through an Ajax call. If the Ajax call works perfectly in the new view, it throws a 404 error for my edit view.

Route:

  post 'letters/ajax_send_test_botletter', to: 'letters#send_test_botletter', as: 'send_test_botletter'

The form is defined like this:

<%= form_for(letter, :html => {class: "directUpload", remote: true}) do |f| %>

The button triggering the Ajax call in the form:

<button class="cta3" id="send_test_letter">Send a test campaign to yourself</button>

Ajax call:

  $('#send_test_letter').on('click', function(){
    $('form').submit(function() {
      var valuesToSubmit = $(this).serialize();
      $.ajax({
          type: "POST",
          url: "/letters/ajax_send_test_botletter",
          data: valuesToSubmit,
          dataType: "JSON" // you want a difference between normal and ajax-calls, and json is standard
      }).success(function(json){
          if(json['value'] == "No Recipient") {
            $('#send_test_letter').css('display', 'none');
            $('#save_test_user').css('display', 'block');
          } else {
            console.log("Success")
            $('#confirmation_test_sent').html('Test successfully sent. Check your Messenger.')
          }
          $('form').unbind('submit');
      });
      return false; // prevents normal behaviour
    });
  });

My send_test_botletter method

def send_test_botletter

    @message_content = params[:letter]['messages_attributes']['0']['content']

    @button_message = params[:letter]['messages_attributes']['0']['buttons_attributes']['0']['button_text'] if params[:letter]['messages_attributes']['0']['buttons_attributes']['0']['button_text'] != ''
    @button_url = params[:letter]['messages_attributes']['0']['buttons_attributes']['0']['button_url'] if params[:letter]['messages_attributes']['0']['buttons_attributes']['0']['button_url'] != ''
    @cards = params[:letter]['cards_attributes'] if params[:letter]['cards_attributes'].present? == true

    @test_segment = Segment.where(core_bot_id: @core_bot_active.id, name: "test").first
    @recipients = BotUser.where(core_bot_id: @core_bot_active.id, source: @test_segment.token)

    if @recipients.exists?
      send_message_onboarding if @message_content != '' and @button_message.present? == false
      send_message_button_onboarding if @message_content != '' and @button_message.present? == true and @button_url.present? == true
      send_card_onboarding if @cards

      respond_to do |format|
        format.json { render json: {"value" => "Success"}}
      end
    else
      respond_to do |format|
        format.json { render json: {"value" => "No Recipient"}}
      end
    end
  end

I get the following error in the Chrome console for the edit view:

POST http://localhost:3000/letters/ajax_send_test_botletter 404 (Not Found)

And in my Rails logs:

ActiveRecord::RecordNotFound (Couldn't find Letter with 'id'=ajax_send_test_botletter):

It seems it calls the Update method instead of the send_test_botletter method...

Any idea what's wrong here?

nico_lrx
  • 715
  • 1
  • 19
  • 36

2 Answers2

1

I found the trick. The problem was the PATCH method in the edit form.

I found a plugin in this discussion in order to modify the serialized data and change the method to "post":

$('#send_test_letter').on('click', function(){
    $('form').submit(function() {
      var valuesToSubmit = $(this).awesomeFormSerializer({
          _method: 'post',
      });
      $.ajax({
          type: "POST",
          url: "/letters/ajax_send_test_botletter",
          data: valuesToSubmit,
          dataType: "JSON" // you want a difference between normal and ajax-calls, and json is standard
      }).success(function(json){
          if(json['value'] == "No Recipient") {
            $('#send_test_letter').css('display', 'none');
            $('#save_test_user').css('display', 'block');
          } else {
            console.log("Success")
            $('#confirmation_test_sent').html('Test successfully sent. Check your Messenger.')
          }
          $('form').unbind('submit');
      });
      return false; // prevents normal behaviour
    });
  });

(function ( $ ) {
    // Pass an object of key/vals to override
    $.fn.awesomeFormSerializer = function(overrides) {
        // Get the parameters as an array
        var newParams = this.serializeArray();

        for(var key in overrides) {
            var newVal = overrides[key]
            // Find and replace `content` if there
            for (index = 0; index < newParams.length; ++index) {
                if (newParams[index].name == key) {
                    newParams[index].value = newVal;
                    break;
                }
            }

            // Add it if it wasn't there
            if (index >= newParams.length) {
                newParams.push({
                    name: key,
                    value: newVal
                });
            }
        }

        // Convert to URL-encoded string
        return $.param(newParams);
    }
}( jQuery ));
nico_lrx
  • 715
  • 1
  • 19
  • 36
  • is there a simpler way these days in Rail 6 to fix this issue without using the plugin you referred to? – Bob Dec 14 '21 at 21:02
0

form_for(letter... generates a different url and method depending whether or not the instance is persisted, defaulting to create and post or update and patch as appropriate.

When you hit submit, it's trying to hit this endpoint, before your listener kicks in. And in doing so, breaks the remaining js.

However, you can also provide url and method options to form_for. Try providing a blank url option and the correct method (form_for letter, ..., url: '', method: :post).

Alternatively, you could stop the default behaviour / propagation on form submission:

$('form').submit(function(e) { 
  e.stopPropagation() // Or could simply be `preventDefault()`, depending on your use case
  ...
  // your AJAX
}

Able to test out these approaches?


Update

Your method is actually nesting a submit listener within the click one. Try the following:

  $('#send_test_letter').on('click', function(e){
    e.stopPropagation()
    var $form = $(this).closest('form')
    var valuesToSubmit = $form.serialize();
    $.ajax({
        type: "POST",
        url: "/letters/ajax_send_test_botletter",
        data: valuesToSubmit,
        dataType: "JSON" // you want a difference between normal and ajax-calls, and json is standard
    }).success(function(json){
        if(json['value'] == "No Recipient") {
          $('#send_test_letter').css('display', 'none');
          $('#save_test_user').css('display', 'block');
        } else {
          console.log("Success")
          $('#confirmation_test_sent').html('Test successfully sent. Check your Messenger.')
        }
      return false; // prevents normal behaviour
    });
  });
SRack
  • 11,495
  • 5
  • 47
  • 60
  • Thanks, none of this worked. However, if I specify method: :post in the form definition, the Ajax call works (but the normal submit button doesn't). I guess it's a problem with the method, in the new view it's a post form, in the edit view it's PATCH. – nico_lrx Mar 16 '18 at 13:27
  • 1
    Ah yes, that'll be it. As with the path changes in my first paragraph, the method changes too. Let me update the answer. And I've actually just spotted something else. Check me answer in two mins :) – SRack Mar 16 '18 at 13:29
  • Thanks for your edits. Unfortunately, now the Ajax button works but not the one to regularly submit the form. I get this error in Chrome console: POST http://localhost:3000/letters/63/edit 404 (Not Found) – nico_lrx Mar 16 '18 at 13:33
  • 1
    Was actually just writing a further update, in there now. I think this should do it - just be sure `var $form = $(this).closest('form')` actually finds the form. Does that work? – SRack Mar 16 '18 at 13:36
  • Unfortunately, I still get the error: POST http://localhost:3000/letters/ajax_send_test_botletter 404 (Not Found) – nico_lrx Mar 16 '18 at 13:42
  • In valuesToSubmit, the method is patch, maybe that's the problem? – nico_lrx Mar 16 '18 at 13:44
  • I found the solution, just posted it. Thanks for your help! – nico_lrx Mar 16 '18 at 14:21