Home > Enterprise >  Rails 7: Turbo calls not existing create method
Rails 7: Turbo calls not existing create method

Time:12-31

I have a problem. In my Rails app, I am trying to load a list of data using given select-option elements. When I hit submit, I am getting an error that the create method doesn't exist in my controller (which is true). I am new with this turbo-rails package, but this is what I got so far:

index.html.erb

<!DOCTYPE html>
<html>
    <head>
        <%= stylesheet_link_tag 'users/main', 'data-turbolinks-track': 'reload' %>
        <%= csrf_meta_tags %>
    </head>
    <body>
        <main>
            <div >
                <div >
                    <div >
                        <form action="/users" method="POST">
                            <input type="hidden" name="authenticity_token" value="<%= form_authenticity_token %>">
                            <%= render partial: "shared/select", locals: {
                                placeholder: 'Gender', 
                                width: '90px', 
                                options: $genders,
                                classes: 'filter',
                                name: 'gender'
                            } %>

                            <button type="submit">submit</button>

                        </form>
                    </div>

                    <div id="user-data-list">
                        <%= render partial: "users/user_list", locals: {
                            users: @users
                        } %>
                    </div>  
                </div>
            </div>
        </main>
    </body>
</html>

users.controller.rb

class UsersController < ApplicationController

    before_action :require_user

    USERS_PER_PAGE = 15

    def index
        @users = User.limit(USERS_PER_PAGE).order(:creation_date).reverse_order
    end


    def load
        gender = if params[:gender] then params[:gender] else '' end

        @users = User.limit(USERS_PER_PAGE).where(
            "gender LIKE ?", "%"   gender   "%"
        ).order(:creation_date).reverse_order

        respond_to do |format|
            format.turbo_stream
            format.html { redirect_to users_url() }
        end
    end
end

load.turbo_stream.erb

<%= turbo_stream.replace "user-data-list" do %>
    <%= render partial: "users/user_list", locals: {
        users: @users
    } %>
<% end %>

routes.rb

Rails.application.routes.draw do
  # System check
  get '/health', to: 'application#health'

  get '/users' => "users#index"
  post '/users' => "users#load"
  resources :users

  post '/login' => 'auth#login'
  get '/logout' => 'auth#logout'
end

_select.html.erb

<!DOCTYPE html>
<html>
    <head>
        <%= stylesheet_link_tag 'components/select', media: 'all', 'data-turbolinks-track': 'reload' %>
    </head>
    <body>
        <select
            style="<%= 'width: '   width   ';' if width %>"
            <% if defined?(id) %>
                id="<%= id %>"
            <% end %>
            <% if defined?(classes) %>
                
            <% end %>
            <% if defined?(name) %>
                name="<%= name %>"
            <% end %>
            <% if defined?(required) %>
                required
            <% end %>
        >
            <option 
                value="" 
                disabled 
                selected 
                hidden
            >
                <%= placeholder %>
            </option>

            <% options.each do |option| %>
                <option><%= option %></option>
            <% end %>
        </select>
    </body>
</html>

But when I run this code and hit the submit button I get the following error: enter image description here

I don't have a create method in my controller, because there is no possible option to create a user. I have a custom load method, and I can't seem to figure out why it is trying to call the create method. Can someone explain this to me?

CodePudding user response:

Routes have priority in the order they are declared.

resources :users already declares a POST /users route. While you could fix it by moving your strange route up you don't actually even need this in the first place.

What you're doing looks like you're simply adding a bunch of filters to a resource. You can do that by just sending a GET request to the collection path (the index) and having it use the optional query string parameters to apply conditions.

Rails.application.routes.draw do
  # System check
  get '/health', to: 'application#health'

  # Only declare the routes you're actually using!
  resources :users, only: [:index]

  post '/login' => 'auth#login'
  get '/logout' => 'auth#logout'
end
<%= form_with(url: users_path, method: :get,  data: { turbo_frame: "user-data-list" }) do |form| %>
  <%= render partial: "shared/select", locals: {
                                placeholder: 'Gender', 
                                width: '90px', 
                                # why is this a global?
                                options: $genders,
                                classes: 'filter',
                                name: 'gender',
                                form: form # pass the form builder along
                            } %> 

  <%= form.submit %>
<% end %>
# /app/users/index.turbo_stream.erb
<%= turbo_stream.replace "user-data-list" do %>
    <%= render partial: "users/user_list", locals: {
        users: @users
    } %>
<% end %>
class UsersController < ApplicationController
  before_action :require_user
  USERS_PER_PAGE = 15

  def index
    @users = User.limit(USERS_PER_PAGE)
                 .order(:creation_date: :desc)
    if params[:gender] 
      # and why is this pattern matching? 
      @users.where("gender LIKE ?", "%#{gender}%")
    end

    respond_to do |format|
      format.turbo_stream
      format.html
    end
  end
end

Conceptually performing a search, adding pagination or any other kinds of filters via query strings parameters are idempotent actions as its not creating or altering anything and the resource will look the same for any visitor using the same URI (in theory at least).

You should thus use GET and not POST which lets the request be saved in the browsers history - be cached, and be directly linkable.

  • Related