2

I have initially created a ROR project (which worked perfectly) and now trying to implement Ajax into this as my client side project. I have been using Rails for a while but this is my first experience with Ajax. For some reason when I am adding a new instance of ExerciseLogs, Rails creates 2 exact same records of it.

I see the follwoing calls in the network tab

Request URL: xxx/exercise_logs/67
Request Method:GET
Status Code:200 OK

Request URL: xxx/exercise_logs/68
Request Method:GET
Status Code:200 OK

When deleting (click on Delete link on exercise_logs\index page) I am being asked to confirm my action and even if I choose to cancel the record is deleted. Initially I was getting an error "Cannot find ExerciseLog with id of XX". After a bit of goggling I added e.stopPropagation(); to my script. Now I am not getting the error but I can see in the Network tab the following:

Request URL:xxx/exercise_logs/2
Request Method:DELETE
Status Code:302 Found

Request URL:xxx/exercise_logs
Request Method:GET
Status Code:404 Not Found

in my Rails terminal:

Started DELETE "/exercise_logs" for 127.0.0.1 at 2013-12-21 18:04:51 +0000

ActionController::RoutingError (No route matches [DELETE] "/exercise_logs"): actionpack (3.2.11) lib/action_dispatch/middleware/debug_exceptions.rb:21:in

I imagine that the create and delete doubling of calls must be connected but I cannot figure why. I don't have duplicate scripts with the same code and I am pulling my hair out at this point cause I cannot figure this out.

I used console.log in the script to check how many times each method is being called and I can see it being called only once. Is it some configuration problem with Rails?

My code is quite long. Index.js:

var displaySideNav = function(el) {   
    if (el.attr("href") != "/site/dashboard") {
        $(".side-nav").addClass('hide');
    }else {
        $(".side-nav").removeClass('hide');
    }
};

var displayNoticesAndAlerts = function() {

  $('#notice:not(:empty)').removeClass("hide");
  $('#notice_signed_in:not(:empty)').removeClass("hide");
  $('#alert:not(:empty)').removeClass("hide");
  $('#alert_signed_in:not(:empty)').removeClass("hide");
};


var selectedElement = $(document).find('#nav .selected');
displaySideNav(selectedElement);
displayNoticesAndAlerts();

var displayResponse = function(response) { 
    $("#loading").addClass('hide');
    console.log("displayResponse");
    var content = $("#content");

    var filteredResponse = $(response).find("#content").children();
    content.html(filteredResponse);


    var selectedElement = $(document).find('#nav .selected');
    displaySideNav(selectedElement);

    content.find('#notice:not(:empty)').removeClass("hide");
    content.find('#notice_signed_in:not(:empty)').removeClass("hide");
    content.find('#alert:not(:empty)').removeClass("hide");
    content.find('#alert_signed_in:not(:empty)').removeClass("hide");

    content.find('#exercise_log_exercise_date, #food_intake_log_intake_date, #weight_log_measured_on').datepicker({dateFormat: 'yy-mm-dd'});

    content.find('.chosen-select').chosen();

    content.find('#accordion').accordion();

    $(document).on("click","#recipes th a, #recipes .pagination a", function() {
            $.getScript(this.href);
            return false;
        }
    );


    var female = content.find("input#profile_gender_female");
    if (female.attr('checked')) {
        $("div#woman").attr('style', 'display:block;');
    }
    content.find("input[name='profile[gender]']").on('change', function(){
            $("#woman").toggle();
        }
    );

    morrisTemplate = {
        element: 'weight_logs_chart',
        data: $('#weight_logs_chart').data('weight-logs'),
        xkey: 'measured_on',
        ykeys: ['weight'],
        labels: ['Weight'],
        smooth: true,
        postUnits: 'kg',
        goals: [$('#min').text(), $('#max').text()],
        goalStrokeWidth: 1,
        goalLineColors: ['#FF0000' ]
    };
    content.find("a").on("click", loadAjax);
    content.find("form").on("submit", submitForm);

    if ($('#myfirstchart').length>0 ){
        Morris.Line(morrisTemplate);
    }

};


var submitForm = function(e) {

    e.preventDefault(); 

    $(this).find("input[type=submit]").attr("disabled", "disabled");
    $(this).off("submit"); 

    $("#loading").removeClass('hide'); 


    var url = $(this).attr("action");
    var method = $(this).attr("method"); 
    var data = {}; 

    $(this).find("input, select").each(function() { 
        var name = $(this).attr("name"); 
        var value = $(this).val(); 

        data[name] = value;
    });

    $.ajax({ 
        url: url, 
        type: method,
        data: data, 
        success: displayResponse 
    });
};

var loadAjax = function(e) { 

    e.stopPropagation();
    e.preventDefault();

    $('ul.side-nav a').css('border-bottom','none');
    $(this).attr("disabled", "disabled");
    $(this).css('border-bottom','1px solid black');

    $("#loading").show();

    var url = $(this).attr("href");
    var method = $(this).attr("data-method") || "get"; 

    if(method === "delete" && confirm("Are you sure?")) { 
        $(this).parents("tr").remove(); 
    }

    $.ajax({ 
        url: url, 
        type: method,
        success: displayResponse
    });
};

$(".side-nav a").on("click", loadAjax); 

application.js

//= require jquery
//= require jquery_ujs
//= require jquery.ui.datepicker
//= require jquery.ui.accordion
//= require raphael
//= require morris
//= require pwstrength
//= require chosen
//= require foundation/foundation
//= require foundation/foundation.topbar
//= require foundation/foundation.forms
//= require foundation/foundation.tooltips
//= require foundation/foundation.reveal.js
//= require_tree

$(function(){ $(document).foundation(
); });

sample controller destroy action:

def destroy
    @exercise_log = ExerciseLog.find(params[:id])
    @exercise_log.destroy

    respond_to do |format|
      format.html { redirect_to exercise_logs_url }
      format.json { head :no_content }
    end
end

sample controller create action:

 def create
    @exercise_log = ExerciseLog.new(params[:exercise_log])
    @exercise_log.calories_burned = @exercise_log.calculate_calories_burned

    respond_to do |format|
      if @exercise_log.save
        format.html { redirect_to @exercise_log, notice: 'Exercise log was successfully created.' }
        format.json { render json: @exercise_log, status: :created, location: @exercise_log }
      else
        format.html { render action: "new" }
        format.json { render json: @exercise_log.errors, status: :unprocessable_entity }
      end
    end
  end

I will really appreciate your help.

EDIT: view/exercise_logs/index.html.erb

<div>
  <div id="notice_signed_in" class="notice hide"><%= notice %></div>
  <div id="alert_signed_in" class="alert hide"><%= alert %></div>
</div>
<h1>Listing exercise logs</h1>

<% if @exercise_logs.any? %>
    <table>
      <tr>
        <th>Date</th>
        <th>Calories burned</th>
        <th>Type of activity</th>
        <th>Length in minutes</th>
        <th></th>
        <th></th>
        <th></th>
      </tr>

      <% @exercise_logs.each do |exercise_log| %>
          <tr>
            <td><%= exercise_log.exercise_date %></td>
            <td><%= exercise_log.calories_burned %></td>
            <td><%= exercise_log.activity.name %></td>
            <td><%= exercise_log.length %></td>
            <td><%= link_to 'Show', exercise_log %></td>
            <td><%= link_to 'Edit', edit_exercise_log_path(exercise_log) %></td>
            <td><%= link_to 'Destroy', exercise_log, method: :delete %></td>
          </tr>
      <% end %>
    </table>
<% else %>
    <div>You have not logged any exercise activities yet. Press the button below to log one.</div>
<% end %>
<br/>

<%= link_to 'New Exercise log', new_exercise_log_path, class: "small button radius" %>

EDIT 2: terminal log on creation of 2 records:

Started POST "/exercise_logs" for 127.0.0.1 at 2013-12-21 20:18:45 +0000
Processing by ExerciseLogsController#create as */*
  Parameters: {"utf8"=>"V", "authenticity_token"=>"bXZvZgj6gd45mqQOOXNIb0uwB0E1Q
D0ioNeXKMxAstg=", "exercise_log"=>{"exercise_date"=>"2013-12-03", "length"=>"43"
, "user_id"=>"1", "activity_id"=>"2"}, "undefined"=>"", "commit"=>"Create Exerci
se log"}
  ←[1m←[36mUser Load (1.0ms)←[0m  ←[1mSELECT "users".* FROM "users" WHERE "users
"."id" = 1 LIMIT 1←[0m
  ←[1m←[35mActivity Load (1.0ms)←[0m  SELECT "activities".* FROM "activities" WH
ERE "activities"."id" = 2 LIMIT 1
  ←[1m←[36m (0.0ms)←[0m  ←[1mbegin transaction←[0m
  ←[1m←[35mSQL (4.0ms)←[0m  INSERT INTO "exercise_logs" ("activity_id", "calorie
s_burned", "created_at", "exercise_date", "length", "updated_at", "user_id") VAL
UES (?, ?, ?, ?, ?, ?, ?)  [["activity_id", 2], ["calories_burned", 186], ["crea
ted_at", Sat, 21 Dec 2013 20:18:45 UTC +00:00], ["exercise_date", Tue, 03 Dec 20
13], ["length", 43], ["updated_at", Sat, 21 Dec 2013 20:18:45 UTC +00:00], ["use
r_id", 1]]
  ←[1m←[36m (28.0ms)←[0m  ←[1mcommit transaction←[0m
Redirected to http://localhost:3000/exercise_logs/98
Completed 302 Found in 55ms (ActiveRecord: 34.0ms)


Started POST "/exercise_logs" for 127.0.0.1 at 2013-12-21 20:18:45 +0000
Processing by ExerciseLogsController#create as JS
  Parameters: {"utf8"=>"V", "authenticity_token"=>"bXZvZgj6gd45mqQOOXNIb0uwB0E1Q
D0ioNeXKMxAstg=", "exercise_log"=>{"exercise_date"=>"2013-12-03", "length"=>"43"
, "user_id"=>"1", "activity_id"=>"2"}, "commit"=>"Create Exercise log"}
  ←[1m←[35mUser Load (1.0ms)←[0m  SELECT "users".* FROM "users" WHERE "users"."i
d" = 1 LIMIT 1
  ←[1m←[36mActivity Load (1.0ms)←[0m  ←[1mSELECT "activities".* FROM "activities
" WHERE "activities"."id" = 2 LIMIT 1←[0m
  ←[1m←[35m (0.0ms)←[0m  begin transaction
  ←[1m←[36mSQL (4.0ms)←[0m  ←[1mINSERT INTO "exercise_logs" ("activity_id", "cal
ories_burned", "created_at", "exercise_date", "length", "updated_at", "user_id")
 VALUES (?, ?, ?, ?, ?, ?, ?)←[0m  [["activity_id", 2], ["calories_burned", 186]
, ["created_at", Sat, 21 Dec 2013 20:18:45 UTC +00:00], ["exercise_date", Tue, 0
3 Dec 2013], ["length", 43], ["updated_at", Sat, 21 Dec 2013 20:18:45 UTC +00:00
], ["user_id", 1]]
  ←[1m←[35m (23.0ms)←[0m  commit transaction
Redirected to http://localhost:3000/exercise_logs/99
Completed 302 Found in 52ms (ActiveRecord: 29.0ms)


Started GET "/exercise_logs/98" for 127.0.0.1 at 2013-12-21 20:18:45 +0000
Processing by ExerciseLogsController#show as */*
  Parameters: {"id"=>"98"}
  ←[1m←[36mUser Load (1.0ms)←[0m  ←[1mSELECT "users".* FROM "users" WHERE "users
"."id" = 1 LIMIT 1←[0m
  ←[1m←[35mExerciseLog Load (1.0ms)←[0m  SELECT "exercise_logs".* FROM "exercise
_logs" WHERE "exercise_logs"."id" = ? LIMIT 1  [["id", "98"]]
  ←[1m←[36mActivity Load (1.0ms)←[0m  ←[1mSELECT "activities".* FROM "activities
" WHERE "activities"."id" = 2 LIMIT 1←[0m
  Rendered exercise_logs/show.html.erb within layouts/application (4.0ms)
  Rendered application/_main_nav.html.erb (3.0ms)
  Rendered application/_side.html.erb (5.0ms)
Completed 200 OK in 77ms (Views: 64.0ms | ActiveRecord: 3.0ms)


Started GET "/exercise_logs/99" for 127.0.0.1 at 2013-12-21 20:18:45 +0000
Processing by ExerciseLogsController#show as JS
  Parameters: {"id"=>"99"}
  ←[1m←[35mUser Load (1.0ms)←[0m  SELECT "users".* FROM "users" WHERE "users"."i
d" = 1 LIMIT 1
  ←[1m←[36mExerciseLog Load (1.0ms)←[0m  ←[1mSELECT "exercise_logs".* FROM "exer
cise_logs" WHERE "exercise_logs"."id" = ? LIMIT 1←[0m  [["id", "99"]]
  ←[1m←[35mActivity Load (1.0ms)←[0m  SELECT "activities".* FROM "activities" WH
ERE "activities"."id" = 2 LIMIT 1
  Rendered exercise_logs/show.html.erb within layouts/application (6.0ms)
  Rendered application/_main_nav.html.erb (3.0ms)
  Rendered application/_side.html.erb (7.0ms)
Completed 200 OK in 73ms (Views: 67.0ms | ActiveRecord: 3.0ms)

UPDATE

I managed to get rid of double creation of records (by trying a number of advises from Rails 3 UJS - controller gets called twice by link_to :remote and Form submitted twice, due to :remote=>true) but what i still have problem with is that when I delete a record, even though I cancel, the record still gets deleted.

this is my terminal output:

Started DELETE "/exercise_logs/138" for 127.0.0.1 at 2013-12-22 00:30:17 +0000
Processing by ExerciseLogsController#destroy as */*
  Parameters: {"id"=>"138"}
  ←[1m←[36mUser Load (0.0ms)←[0m  ←[1mSELECT "users".* FROM "users" WHERE "users
"."id" = 1 LIMIT 1←[0m
  ←[1m←[35mExerciseLog Load (0.0ms)←[0m  SELECT "exercise_logs".* FROM "exercise
_logs" WHERE "exercise_logs"."id" = ? LIMIT 1  [["id", "138"]]
  ←[1m←[36m (0.0ms)←[0m  ←[1mbegin transaction←[0m
  ←[1m←[35mSQL (2.0ms)←[0m  DELETE FROM "exercise_logs" WHERE "exercise_logs"."i
d" = ?  [["id", 138]]
  ←[1m←[36m (23.0ms)←[0m  ←[1mcommit transaction←[0m
Redirected to http://localhost:3000/exercise_logs
Completed 302 Found in 30ms (ActiveRecord: 25.0ms)


Started DELETE "/exercise_logs" for 127.0.0.1 at 2013-12-22 00:30:17 +0000

ActionController::RoutingError (No route matches [DELETE] "/exercise_logs"):
  actionpack (3.2.11) lib/action_dispatch/middleware/debug_exceptions.rb:21:in `
call'
  actionpack (3.2.11) lib/action_dispatch/middleware/show_exceptions.rb:56:in `c
all'
  railties (3.2.11) lib/rails/rack/logger.rb:32:in `call_app'
  railties (3.2.11) lib/rails/rack/logger.rb:16:in `block in call'
  activesupport (3.2.11) lib/active_support/tagged_logging.rb:22:in `tagged'
  railties (3.2.11) lib/rails/rack/logger.rb:16:in `call'
  actionpack (3.2.11) lib/action_dispatch/middleware/request_id.rb:22:in `call'
  rack (1.4.5) lib/rack/methodoverride.rb:21:in `call'
  rack (1.4.5) lib/rack/runtime.rb:17:in `call'
  activesupport (3.2.11) lib/active_support/cache/strategy/local_cache.rb:72:in
`call'
  rack (1.4.5) lib/rack/lock.rb:15:in `call'
  actionpack (3.2.11) lib/action_dispatch/middleware/static.rb:62:in `call'
  railties (3.2.11) lib/rails/engine.rb:479:in `call'
  railties (3.2.11) lib/rails/application.rb:223:in `call'
  rack (1.4.5) lib/rack/content_length.rb:14:in `call'
  railties (3.2.11) lib/rails/rack/log_tailer.rb:17:in `call'
  rack (1.4.5) lib/rack/handler/webrick.rb:59:in `service'
  C:/Ruby193/lib/ruby/1.9.1/webrick/httpserver.rb:138:in `service'
  C:/Ruby193/lib/ruby/1.9.1/webrick/httpserver.rb:94:in `run'
  C:/Ruby193/lib/ruby/1.9.1/webrick/server.rb:191:in `block in start_thread'


  Rendered C:/Ruby193/lib/ruby/gems/1.9.1/gems/actionpack-3.2.11/lib/action_disp
atch/middleware/templates/rescues/routing_error.erb within rescues/layout (1.0ms
)

*****I would like to thank all of you who tried to help :) *****

Community
  • 1
  • 1
evoo
  • 300
  • 4
  • 15

3 Answers3

0

Sounds like jquery might be loading twice? Do you have a JavaScript include for it in your application.html in addition to application.js? I've done that before.

MikeC
  • 480
  • 6
  • 14
  • I only have <%= stylesheet_link_tag "application" %> <%= javascript_include_tag "vendor/custom.modernizr" %> <%= csrf_meta_tags %> in the head and <%= javascript_include_tag "application" %> at the end of the body. This should not cause the problem. – evoo Dec 21 '13 at 19:11
0

The culprit might be Rails' Unobtrusive Javascript Handler (UJS): if for example you create a delete link with the method argument

<%= link_to 'Delete this', item, method: :delete %>

Rails produces

<a href="/items/5" data-method="delete">Delete this</a>

and the Rails UJS handler automatically attaches its own click handler that submits the DELETE AJAX request.

Try removing method: :delete from your link_to.

Alternatively if you want to keep the data-method attribute, adding this

$(".side-nav a").on('ajax:before', function(e) { return false; });

before

$(".side-nav a").on("click", loadAjax);

should stop the UJS handler.

janfoeh
  • 10,243
  • 2
  • 31
  • 56
  • I tried this now but without specifying the destroy action which I want the link to perform it redirects me to the show page. – evoo Dec 21 '13 at 19:13
  • @evoo `var method = $(this).attr("data-method") || "get"; ` your code falls back to GET if `data-method` is not specified. – janfoeh Dec 21 '13 at 19:15
  • Yes in case of delete it is passed as a data-method attribute but edit and show it is not so I had to pass get somehow. – evoo Dec 21 '13 at 19:26
  • I tried adding $(".side-nav a").on('ajax:before', function(e) { e.stopPropagation(); }); but unfortunatelly the behaviour didn't change. I already have stopPropogation in the loadAjax method.. – evoo Dec 21 '13 at 19:37
  • Yes, for the `click` event - this one is for `ajax:before`. – janfoeh Dec 21 '13 at 19:41
  • My bad - it seems it should be `$(".side-nav a").on('ajax:before', function(e) { return false; });` – janfoeh Dec 21 '13 at 19:46
  • That did not work neither :( I also tried with bind instead of on- this.. :( – evoo Dec 21 '13 at 19:58
  • That second fix didn't work for me either - is there any obvious way to keep in graceful degradation for non-js browsers while removing method: :delete? – RSid Jul 16 '14 at 14:58
0

I had the same isssue, check for a doublicate jquery loading in application.js and application.html.erb. You might be loading it twice. I had to delete this from my application.js:

**//= require jquery_ujs**
Ahmed J.
  • 484
  • 6
  • 11