Belay Board App Part 5: Add AJAX to Calendar
Published: 2023-11-15 23:00:34

Right now all my actions use the `redirect_back` method. Now I want to be able to request, accept, and reject without redirecting or refreshing the entire page.

## Improve this experience with Ajax
- Change the default behavior of links and forms.
- When the user clicks on a link/form, keep them where they are instead of sending them to a different URL.
- Make the same GET/PATCH/POST/DELETE request as before, to the same route and with the same parameters; but it will be in the background, using JavaScript.
- In the action triggered by the request, instead of responding with a .html.erb template or redirecting, respond with a .js.erb template containing some jQuery.
- The jQuery will run in the user’s browser and update just the part of the page that needs it (sliding in the comment, updating the like count, etc).

At the bash prompt run these two commands to modify `config/importmap.rb`:
```bash
% ./bin/importmap pin @rails/ujs
% ./bin/importmap pin jquery
```
Then in app/javascripts/application.js, I added these lines:
```js
import "@hotwired/turbo-rails"
import "controllers"

import jquery from "jquery";
window.jQuery = jquery;
window.$ = jquery;
import Rails from "@rails/ujs"
Rails.start();
```
Now change anywhere in the app that was using a Turbo-type request.

### Change the link from submitting an HTML request to a JS request
Make it so that when the user clicks on the delete link, it doesn’t navigate them to the URL specified by the `href=""` attribute. Instead, keep them right where they are.

Attach a custom JavaScript event handler to the link that triggers the action so the appropriate CRUD occurs.

The request that is placed must be of the format `.js`, rather than `.html`. Then `respond_to` it accordingly.

The `link_to` and `form_with` methods accomplish all of this.

## In the calendar
```html
<!-- views/availabilities/index.html.erb -->

 <% availability.requests.pending.each do |request| %>

  <div id="request_<%= request.id %>" class="request-section">

    <p><%= request.sender.username %> wants to join</p>

      <%= form_with(model: request, url: accept_availability_request_path(request.availability, request), method: :post, local: false) do |form| %>
      <%= form.submit 'Accept', class: "btn btn-success btn-sm" %>
      <% end %>

      <%= form_with(model: request, url: reject_availability_request_path(request.availability, request), method: :post, local: false) do |form| %>
      <%= form.submit 'Reject', class: "btn btn-danger btn-sm" %>
  <% end %>
  </div>
 <% end %>
```

Use jQuery to remove the comment from the DOM. Use a unique `id=""` or the helper method `dom_id` to easily select with `$()`:


### respond_to JS
Edit the accept and reject action to handle requests for format JS:

```rb
class RequestsController < ApplicationController
  before_action :set_request, only: %i[ show edit update destroy ]

  def accept
    @request = Request.find(params[:id])
    @request.accepted!
  
    respond_to do |format|
      format.html { redirect_to availability_requests_path(@request.availability), notice: 'Request accepted.' }
      format.js   # This will look for a file named `accept.js.erb` in `app/views/requests/`
    end
  end

  def reject
    @request = Request.find(params[:id])
    @request.rejected!
  
    respond_to do |format|
      format.html { redirect_to availability_requests_path(@request.availability), notice: 'Request rejected.' }
      format.js
    end
  end
  # ...
  ```

### Create template files
```js
// views/requests/accept.js.erb

console.log("accepted");

$("#request_<%= @request.id %>").fadeOut("slow", function() { $(this).remove(); });
```

```js
// views/requests/reject.js.erb

console.log("rejected");

$("#request_<%= @request.id %>").fadeOut("slow", function() { $(this).remove(); });
```

## Next Steps
Now that AJAX is working for accept and reject, I will add AJAX to the other buttons on the calendar.