Home > Back-end >  Rails drop-down list without form_for
Rails drop-down list without form_for

Time:02-07

In my Rails 5 app I've got a view in which I display information about users in a table.

#views/user_authorizations/pending.html.erb

<% if user_authorizations.any? %>
    <table >
      <thead>
        <tr>
          <th>Name</th>
          <th>Status</th>
          <th></th>
        </tr>
      </thead>
      <tbody>
        <% user_authorizations.each do |authorization| %>
          <tr>
            <td><%= authorization.registrant.full_name %></td>
            <td><%= UserAuthorization.statuses[authorization.status].upcase %></td>
          </tr>
        <% end %>
      </tbody>
    </table>
<% else %>
  <h5>No pending authorizations</h5>
<% end %>

One of the information is the account status:

<td><%= UserAuthorization.statuses[authorization.status].upcase %></td>

Which I would like to change to dropdown that allows the admin to change that status from pending to in review and save it right after the admin selects that status. How to achieve this if my view does not have a form_for tag?

Here is the controller responsible for that view

#user_authorizations_controller.rb

class UserAuthorizationsController < ApplicationController
  def pending
    authorize UserAuthorization
    find_pending
  end

  private

  def find_pending
    @pending_user_authorizations = UserAuthorization.pending
  end
end

CodePudding user response:

There are essentially a couple of ways to communicate from the browser to your rails application (and you want to do that, because the action of the administrator should change the state of your application).

One way is with a form. It does not need to be generated with form_for, you can use form_tag or just a plain HTML form tag (but in the latter case you will need to add all the CSRF extra fields yourself).

The other way is through javascript. You capture an event (in this case it would be a "change" event on your <select> tag. It does not need to be in a form for the event to be captured. Once you capture the event, you make an appropriate call (for instance with fetch, which is available in most browsers and also through polyfills) to the application.

From the receiving side, you will need to route a request to a corresponding action. For example

# in routes
resources :user_authorizations do
  post :mark_in_review  # will route /user_authorization/:id/mark_in_review
end

# controller
class UserAuthorizationsController
  def mark_in_review
    ...
  end
end

If you only want to allow changing from "pending" to "in review" (instead of both ways), you might just display a button instead of a select.

Youy might want to get the help of some small js framework like alpineJS or StimulusJS.

Something like:

import { Controller } from "stimulus"

class AuthorizationStatusChangeController extends Controller {
  static values = {ident: Number}

  markInReview() {
    fetch(`/user_authorizations/${this.identValue}/mark_in_review`, {
      credentials: "same-origin",
      method: "POST"
    })
  }
}
<td data-controller="authorization-status-change"
    data-authorization-status-change-ident-value="<%= authorization.id %>" >
  <button data-action="change->authorization-status-change#markInReview">
    Mark as "in review"
  </button>
</td>
  •  Tags:  
  • Related