目標物がこちら
お気に入り機能と言いつつ、このアプリ内では、「プロジェクト登録機能」になっていますので、favoriteという名前にはなっていない点にご注意ください。
非同期でないお気に入り機能を比較対象に、変更前のコードとして記載しています。
routes
変更前
Rails.application.routes.draw do
resources :products, except: [:index]
resources :user_products, only: %i[create destroy]
end
変更後
productsにネストして、ルーティングはshallowにしています。
Rails.application.routes.draw do
resources :products, except: [:index] do
resources :user_products, only: %i[create destroy], shallow: true
end
end
view
変更前
<% if @user_product.present? %>
<%= link_to user_product_path(id: @user_product.id), method: :delete do %>
<div class="project_cancel">
<div class="project_cancel_icon">
<i class="fas fa-star fa-lg"></i>
</div>
<% end %>
<div class="project_cancel_text">
プロジェクトから解除する
</div>
</div>
<% else %>
<%= link_to user_products_path(product_id: @product.id), method: :post do %>
<div class="project_registration">
<div class="project_registration_icon">
<i class="far fa-star fa-lg"></i>
</div>
<% end %>
<div class="project_registration_text">
プロジェクトに登録する
</div>
</div>
<% end %>
変更後
user_products/registerファイルにレンダリングさせ、id=”registered-btn-<%= @product.id %>”の部分を書き換える。
<div id="registered-btn-<%= @product.id %>">
<%= render partial: 'user_products/register', locals: { product: @product } %>
</div>
変更前にこの位置に記述していたコードはレンダリングファイルに移動する。
<% if current_user.registered?(product) %>
<%= link_to user_product_path(product), method: :delete, remote: true do %>
<div class="project_cancel">
<div class="project_cancel_icon">
<i class="fas fa-star fa-lg"></i>
</div>
<% end %>
<div class="project_cancel_text">
プロジェクトから解除する
</div>
</div>
<% else %>
<%= link_to product_user_products_path(product), method: :post, remote: true do %>
<div class="project_registration">
<div class="project_registration_icon">
<i class="far fa-star fa-lg"></i>
</div>
<% end %>
<div class="project_registration_text">
プロジェクトに登録する
</div>
</div>
<% end %>
ifの条件式以外はもともと、products/show.html.erbに記述していたコード
ceateとdestroyそれぞれのjsファイルを作成
$('#registered-btn-<%= @product.id %>').html("<%= j(render partial: 'user_products/register', locals: {product: @product}) %>");
$('#registered-btn-<%= @product.id %>').html("<%= j(render partial: 'user_products/register', locals: {product: @product}) %>");
model
registered?(product)はmodelで定義
class User < ApplicationRecord
has_many :products, dependent: :destroy
has_many :user_products, dependent: :destroy
has_many :registered_products, through: :user_products, source: :product
def registered(product)
registered_products << product
end
def unregistered(product)
registered_products.destroy(product)
end
def registered?(product)
user_products.where(product_id: product.id).exists?
end
end
これで、「登録プロジェクト」に登録されているかどうか判定する。
1productに対し、userは1回しか登録ができないようにする制約を設ける。
正直、今回の場合は不要。
いいね!機能一人一回しかいいね!ができないようにするときなどに必要となってくる。
class UserProduct < ApplicationRecord
belongs_to :user
belongs_to :product
validates :user_id, uniqueness: { scope: :product_id }
end
controller
変更前
class ProductsController < ApplicationController
def show
@user_product = current_user.user_products.find_by(product_id: @product.id)
end
end
非同期処理により上記の記述が不要となる。
class UserProductsController < ApplicationController
def create
user_product = current_user.user_products.create(product_id: params[:product_id])
redirect_to registered_projects_path, notice: "#{user_product.product.name}を「★登録プロジェクト」に登録しました"
end
def destroy
user_product = current_user.user_products.find_by(id: params[:id]).destroy
redirect_to homes_path, notice: "#{user_product.product.name}を「★登録プロジェクト」から解除しました"
end
end
変更後
class UserProductsController < ApplicationController
def create
@product = Product.find(params[:product_id])
current_user.registered(@product)
end
def destroy
@product = current_user.registered_products.find(params[:id])
current_user.unregistered(@product)
end
end
参考にした記事がこちら
【Rails】remote:true形式でAjax通信を行う(お気に入り登録機能のajax化) - Qiita
AjaxとはAjaxとは、Webブラウザ上で非同期通信を行い、ページ全体の再読み込み無しにページを更新する方法のことです。#####同期通信について同期通信では、クライアントはwebページ全体…
コメント