Reputation: 721
I have 2 versions of an same index page which display a list of products. Regular view - Displays List Admin view - Displays List with options to edit, delete etc.
The index action just makes the database query and returns that instance variable. Currently, I have the same index action for rendering the index page. I want to reuse the index action form product into the admin controller.
def index
@products = Product.all
end
routes:
/product/index => product#index
/admin/product/index => product#index
I tried the prepend_view_path technique given [here | How can I intercept rails's template rendering, but then this always ends up rendering the admin/product/index.html.erb file in both cases.
I want the Product#index controller#action to render:
/index.html.erb for /product/index
and
/admin/product/index.html.erb for /admin/product/index
Can someone help me with how this could be done in an elegant way instead of just writing the same action for admin controller class and product controller class.
PS: I'm just one week into ruby and ruby on rails. Any help is much appreciated.
Upvotes: 1
Views: 1449
Reputation: 28810
I faced this challenge when working on a Rails 6 application.
I had two types of users: Customers and Admins. I was using the Devise gem for authentication. I wanted the Products views for the Customer to be different from the Admins View.
I already had an app/controllers/admins
directory in the controllers for the Devise configuration for Admins.
Here's how I did it:
First, define a new route for the Admins products
view using the admins
namespace.
namespace :admins do
resources :products do
end
end
Note: This will affect the Paths/URLs for the Admins. Say, the products_path
will not be admins_products_path
.
Then, in the controllers add the products_controller.rb
to the app/controllers/admins
directory:
class Admins::ProductsController < ApplicationController
before_action :set_product, only: [:show, :edit, :update, :destroy]
# GET /products
# GET /products.json
def index
@products = Product.all
end
# GET /products/1
# GET /products/1.json
def show
end
# GET /products/new
def new
@product = Product.new
end
# GET /products/1/edit
def edit
end
# POST /products
# POST /products.json
def create
@product = Product.new(product_params)
respond_to do |format|
if @product.save
format.html { redirect_to admins_product_path(@product), notice: 'Product was successfully created.' }
format.json { render :show, status: :created, location: admins_product_path(@product) }
else
format.html { render :new }
format.json { render json: @product.errors, status: :unprocessable_entity }
end
end
end
# PATCH/PUT /products/1
# PATCH/PUT /products/1.json
def update
respond_to do |format|
if @product.update(product_params)
format.html { redirect_to admins_product_path(@product), notice: 'Product was successfully updated.' }
format.json { render :show, status: :ok, location: admins_product_path(@product) }
else
format.html { render :edit }
format.json { render json: @product.errors, status: :unprocessable_entity }
end
end
end
# DELETE /products/1
# DELETE /products/1.json
def destroy
@product.destroy
respond_to do |format|
format.html { redirect_to admins_products_url, notice: 'Product was successfully destroyed.' }
format.json { head :no_content }
end
end
private
# Use callbacks to share common setup or constraints between actions.
def set_product
@product = Product.find(params[:id])
end
# Only allow a list of trusted parameters through.
def product_params
params.require(:product).permit(:name, :sku, :short_description, :full_description)
end
end
Note: Take note of the ProductsController namespacing using the Admins Module and the modified paths in the create
, update
and destroy
actions od the
And finally, in the views, add the views associated with the Admins Products in the app/views/admins/products
directory.
Note: You may have to modify the paths in the views to correspond with the routes for the admins products. Say, the show
view for admins products will be admins_product_path(product)
and not product
or product_path(product)
.
There is a cleaner approach using the cells gem. This eliminates the need to duplicate code, and it comes in handy when you need to define views for up to 3 or more roles. You can read up more about how to use it here: Object-Oriented Views in Rails.
That's all.
I hope this helps
Upvotes: 0
Reputation: 465
There is a Mistake in routes .
/product/index => product#index
/admin/product/index => product#index
Both pointing to the same contoller names as product.
I would suggest these routes
resources :admin do
resources :product do
end
end
resources :product do
end
By doing so you make sure that you have two products Controller. One with Admin::ProductController Second ProductController
admin_product_path for admin view product_path for normal view
Upvotes: 2
Reputation: 3243
From within your controllers, just render view you want to serve for user, I mean:
ProductsController
def index
# ...
render 'index'
end
Admin::ProductsController
def index
# ...
render 'products/index'
end
Upvotes: 1