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:
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.