Rails - Problem with passing local variables - ruby-on-rails

I'm running Rails 6.0.3.2 and I want to render a partial passing a local variable to another controller view:
My routes:
Rails.application.routes.draw do
devise_for :users
root to: 'notebooks#index'
resources :notebooks, only: [:index, :new, :show, :create, :edit, :update, :destroy] do
# For details on the DSL available within this file, see https://guides.rubyonrails.org/routing.html
collection do
get "list"
end
end
resources :tags
end
Notebook Model:
class Notebook < ApplicationRecord
has_one_attached :photo
validates :asin, presence: true, uniqueness: true
after_initialize :init
acts_as_list column: :position, add_new_at: :bottom
has_many :taggings
has_many :tags, through: :taggings
def init
self.edited ||= false
end
end
Tag Model
class Tag < ApplicationRecord
has_many :taggings
has_many :notebooks, through: :taggings
end
In Tag controller:
def index
#tags = Tag.all.order(created_at: :asc)
end
I tried to follow this guide and render the "index view" from Tags Controller on the "list view". The application finds the tags/_index.html file, but return the error undefined method `each' for nil:NilClass. Bellow my views code:
In app/views/notebooks/list.html.erb:
<%= render :partial => "tags/index" , locals: {tags: #tags}%>
In app/views/tags/_index.html.erb
<% tags.each do |tag| %>
<div>
<div class="d-flex">
<p><%= tag.id %></p>
<p><%= tag.name %></p>
</div>
<p>tag.taggings.count</p>
</div>
<% end %>
Anyone can point me what i'm doing wrong? I read the Layouts and Rendering in Rails documentation, but i don't have a clue why the instructions won't work on my project...
Thanks in advance!

The rails way to do this is just to create a partial for a single record:
# app/views/tags/_tag.html.erb
<div>
<div class="d-flex">
<p><%= tag.id %></p>
<p><%= tag.name %></p>
</div>
<p>tag.taggings.count</p>
</div>
You can then pass the collection to render and Rails will look up the partial and render it for each member in the collection:
<%= render #tags %>
This is short for:
<%= render partial: 'tag', collection: #tags %>
See Rendering Collections.

Related

How to create a Child before associating it to its Parent in rails?

I have Categories (Parents) within which are listed Products (Children).
I want to be able to create a new Product directly from the navbar, anywhere in the app and then, during the creation, assign it to a Category.
However, I get the present error:
NoMethodError in Products#new
Showing /Users/istvanlazar/Mobily/app/views/products/new.html.erb where line #9 raised:
undefined method `products_path' for #<#<Class:0x00007febaa5aec98>:0x00007febae0f9e38>
Did you mean? product_show_path
## product_show_path is a custom controller that has nothing to do with this one,
enabling show and clean redirection from newest_products index bypassing categories nesting.
Extracted source (around line #9):
9 <%= form_for [#product] do |f| %>
10 <div class="form-styling">
11 <div>
12 <%= f.label :name %>
My App works as such:
Models
class Category < ApplicationRecord
has_many :products, inverse_of: :category
accepts_nested_attributes_for :products
validates :name, presence: true
end
class Product < ApplicationRecord
belongs_to :user, optional: true
belongs_to :category, inverse_of: :products
validates :category, presence: true
end
Routes
get 'products/new', to: 'products#new', as: 'new_product'
resources :categories, only: [:index, :show, :new, :edit] do
resources :products, only: [:index, :show, :edit]
# resources :products, only: [:index, :show, :new, :edit]
end
Controllers
class ProductsController < ApplicationController
before_action :set_category, except: :new
def index
#products = Product.all
#products = policy_scope(#category.products).order(created_at: :desc)
end
def show
#product = Product.find(params[:id])
end
def new
#product = Product.new
#product.user = current_user
end
def create
#product = Product.new(product_params)
#product.user = current_user
if #product.save!
redirect_to category_product_path(#category, #product), notice: "Product has been successfully added to our database"
else
render :new
end
end
private
def set_category
#category = Category.find(params[:category_id])
end
def product_params
params.require(:product).permit(:name, :price, :description, :category_id, :user, :id)
end
end
class CategoriesController < ApplicationController
def index
#categories = Category.all
end
def show
#category = Category.find(params[:id])
end
def new
# Non-existant created in seeds.rb
end
def create
# idem
end
def edit
# idem
end
def update
# idem
end
def destroy
# idem
end
private
def category_params
params.require(:category).permit(:name, :id)
end
end
Views
# In shared/navbar.html.erb:
<nav>
<ul>
<li>Some Link</li>
<li>Another Link</li>
<li><%= link_to "Create", new_product_path %></li>
</ul>
</nav>
# In products/new.html.erb:
<%= form_for [#product] do |f| %>
<div class="form-styling">
<div>
<%= f.label :name %>
<%= f.text_field :name, required: true, placeholder: "Enter product name" %>
<%= f.label :price %>
<%= f.number_field :price, required: true %>
</div>
<div>
<%= f.label :description %>
<%= f.text_field :description %>
</div>
<div>
<%= f.label :category %>
<%= f.collection_select :category_id, Category.order(:name), :id, :name, {prompt: 'Select a Category'}, required: true %>
</div>
<div>
<%= f.submit %>
</div>
</div>
<% end %>
Do you have any idea of where it went wrong? Or is it impossible to Create a Child before assigning it to a Parent..?
Thanks in advance.
Best,
Ist
You haven't defined any route to handle your new product form's POST. You've defined the new_product path, but this arrangement is breaking Rails' conventions and you're not providing a work-around.
You could define another custom route, e.g. post 'products', to: 'products#create', as: 'create_new_product' and then set that in your form like form_for #product, url: create_new_product_path do |f|.
However, you should consider changing the structure so that product routes are not nested under categories. Think twice before breaking conventions this way.
Edit: I misread the intention, ignore this. Go with Jeremy Weather's answer.
Or is it impossible to Create a Child before assigning it to a Parent..?
With the way you have your relationships and validations set up: yes, it is. A Product requires a Category, through the validates :category, presence: true. And if you're on Rails 5+, the association will already be required by default (meaning that validates is actually redundant). Meaning if you try to create a Product without a Category, it will fail, and the Product will not be saved to the database.
With that constraint in place, here's what I recommend trying:
Remove the custom /products/new route
remove get 'products/new', to: 'products#new', as: 'new_product'
Add :new and :create routes to allow product creation nested under categories
Add :create and :new, to the :only array to the resources :products nested under resources :categories
Move the file views/products/new.html.erb to views/categories/products/new.html.erb (creating a new categories directory in views if necessary).
In that file, alter the form_for so that the form is POSTing to the nested :create route:
form_for [#category, #product] do |f|
Visit the URL /categories/[insert some Category ID here]/products/new, filling out some data, and see if that works.
Your routes.rb should look like this:
resources :categories, only: [:index, :show, :new, :edit] do
resources :products, only: [:index, :show, :new, :create, :edit]
end

ActiveRecord RecordNotFound for other class name

I'm trying to implement an index view for questions where you can select a tag link to filter the questions to those with that tagging. I have a class method in Question where it returns only the tagged posts. Rails is giving me an error on the Tag class name in that method, although it works in the console.
None of the RecordNotFound questions on StackOverflow seem to be about referencing another class. Any advice on debugging this type of stuff or what could be going on?
I'm using Rails 5.2.0 and Ruby 2.4.2.
Error
ActiveRecord::RecordNotFound (Couldn't find Tag):
app/models/question.rb:13:in `tagged'
app/controllers/questions_controller.rb:6:in `index'
NameError: uninitialized constant Mime::HTML
Questions index.html.erb
<h2>Questions</h2>
<div class="row">
<div class = "tags">
<% Tag.all.each do |t| %>
<%= link_to t.name, questions_path(tag: t.name), class: 'badge badge-primary'%>
<% end %>
</div>
<% if current_user.customer? %>
<%= render "question" %>
<% else %>
<%= render "admin_question" %>
<% end %>
</div>
<div id="paginator">
<% #questions = #questions.page(params[:page]).per(10) %>
<%= paginate #questions, remote: true %>
</div>
Questions controller
class QuestionsController < ApplicationController
before_action :set_question, only: [:show, :edit, :update, :destroy]
def index
if params[:tag]
#questions = Question.tagged(:tag).page(params[:page]).per(10)
else
#questions = Question.page(params[:page]).per(10)
end
end
Models
class Question < ActiveRecord::Base
validates_presence_of :body
has_many :answers
has_many :users, through: :answers
has_many :taggings
has_many :tags, through: :taggings
def to_s
self.body
end
def self.tagged(tag)
Tag.find_by_name!(tag).questions
end
end
class Tagging < ApplicationRecord
belongs_to :question
belongs_to :tag
end
class Tag < ApplicationRecord
has_many :taggings
has_many :questions, through: :taggings
end
If you look at the error you can see that it occurred in your controller on line 6.
The problem is with Question.tagged(:tag). Here you are filtering questions that are tagged with the tag :tag and you probably haven't created a tag with name :tag. I believe you wanted to filter questions that are tagged with a tag that is passed in params, so you should use Question.tagged(params[:tag]).

Filter results on index page based on has_many through

I have already tried to go through solutions like this, but I'm a Rails beginner and just building my first app after going through Pragmatic Studio Rails I & II Course
Basically, I want to build a simple listing site like BookYogaRetreats.com
I'm trying to figure out how to implement the filter logic. So far I have a listing model, categories & categorizations.
Listing Model:
has_many :categorizations, dependent: :destroy
has_many :categories, through: :categorizations
has_attached_file :image, styles: { :medium => "200x200#" }
validates_attachment_content_type :image, content_type: /\Aimage\/.*\Z/
validates :title, presence: true, uniqueness: true
paginates_per 15
scope :by_category, lambda { |category| includes(:categories).where(:categories => { :name => category }) }
Categorization Model:
belongs_to :listing
belongs_to :category
Categories Model:
has_many :categorizations, dependent: :destroy
has_many :listings, through: :categorizations
My Listings Controller index action looks like this (I've been experimenting a lot...)
class ListingsController < ApplicationController
before_action :find_listing, only: [:show, :edit, :update, :destroy]
before_action :authenticate_user!, except: [:index, :show]
before_filter :load_data, only: [:index]
def index
#listings = #listings.includes(:categories).where(categories: { id: #category}) if #category_ids
#listings = #listings.page(params[:page])
respond_to do |format|
format.js
format.html
end
end
And in my jumbotron partial, which includes the filter dropdowns I have the following:
<div class="col-sm-6 col-md-4">
<%= select_tag("listing[category_id]", options_from_collection_for_select(#categories, "id", "name") , { :prompt => 'Select a Category' }) %>
<%= submit_tag nil, class: "form_submit", method: "get" %>
</div>
My listings/index.html.erb looks like this:
<% #listings.each_slice(3) do |listings|%>
<div id="listings">
<div class="row">
<% listings.each do |listing|%>
<div class="col-md-4">
<div class="listing">
<div class="image_wrapper">
<%= link_to listing do %>
<%= image_tag listing.image.url(:medium) %>
<% end %>
<h2><%= link_to listing.title, listing %></h2>
<p><%= listing.start_date %> - <%= listing.end_date %></p>
</div>
</div>
</div>
<% end %>
</div>
</div>
I've tried to figure something out for the past two days, been experimenting with Filterific Gem as well as Ransack. I really don't want to use gems and at the moment I don't understand what I need to do. I would be fine with a a category filter, which upon clicking the submit button reloads the page showing all listings that belong to that particular category? Having a has_many through relationship probably doesn't make it easier.
I'd love to be able to have something like listings?by_category=seminars, which then reloads the listings view index.html.erb and only shows the listings that include the category seminars.
I might be completely on the wrong track here. Any pointers would be greatly appreciated.Many thanks.
Managed to find a solution here:
My view:
<%= form_tag listings_path, :controller => 'listings', :action => 'index', :method => 'get', :id => "category_select" do %>
<%= collection_select( nil, :category_id, Category.all, :id, :name, { :include_blank => 'All Categories'} , { :class => 'form-control' }) %>
<%= submit_tag nil, class: "form_submit" %>
<% end %>
My controller index action:
#listings = Listing.includes(:categorizations).page(params[:page])
if params[:category_id].blank?
#listings
else
#category_id = Category.find_by(id: params[:category_id])
#listings = #category_id.listings
end
respond_to do |format|
format.js
format.html
end
This will then allow me to use /listings?category_id=5 and solves the problem. If you have any more elegant tips, please let me know.

Trouble with form for nested models in Rails 4 app

Okay, I have been banging my head against the wall for the past few hours trying to figure this out. I have a rails ecommerce app... the store lists products objects, and when you add the product to your cart (another object) it is added by creating a line_item object. When you are ready to checkout the line_items in your cart become an order. At this point, I am trying to allow the customer to provide a page_url (an outside website the product/service will be used on) [via a form_for text_field] to each line_item. To achieve this I created a new model called Page_url which belongs_to the order. Here is what I have that has been failing miserably (I keep getting Controller errors "undefined method `page_url'"):
here is the form:
<%= form_for(#order) do |f| %>
......
<% #items.each do |item| %>
<td><a> Provide the URL the <%= item.product.title %> will be added to</a></td>
<div class="field">
<%= f.fields_for #order, :page_url do |q| -%>
<td> <%= q.label :page_url %>
<%= q.text_field :page_url, size: 80 %>
</td>
<% end %>
</div>
<% end %>
</div>
<div class="actions">
<%= f.submit t('.submit') %>
</div>
<% end %>
orders_controller:
class OrdersController < ApplicationController
skip_before_action :authorize, only: [:new, :create]
include CurrentCart
before_action :set_cart, only: [:new, :create]
skip_before_action :set_order, only: [:create, :update, :destroy]
......
# GET /orders/new
def new
if #cart.line_items.empty?
redirect_to store_url, notice: "Your cart is empty"
return
end
#items = #cart.line_items
#order = Order.new
#page_urls = #order.page_urls
end
.....
def order_params
params.require(:order).permit(:name, :address, :email, :pay_type,
:page_urls_attributes => [:page_url, :product_id,])
end
The Pages_url controller is the standard controller generated by Rails 4
Order.rb:
class Order < ActiveRecord::Base
has_many :line_items, dependent: :destroy
has_many :page_urls, dependent: :destroy
# ...
validates :name, :address, :email, presence: true
validates :pay_type, inclusion: PAYMENT_TYPES
accepts_nested_attributes_for :page_urls
Page_url.rb:
class PageUrl < ActiveRecord::Base
belongs_to :order
I appreciate any help, thanks
I usually use simple_form to create associations and it works great.
That way you can do <%= f.association :page_urls %>
They have great documentation, check here: https://github.com/plataformatec/simple_form under Associations.
Also another point:
In your Order model, I would specify the class.
has_many :page_urls, dependent: :destroy, :class_name => PageUrl
And to create nested forms with simple_form see:
https://github.com/plataformatec/simple_form/wiki/Nested-Models
Hope this helps!

route issue menu.29 instead of menu/29

first i listed all the menu that the guest added inside the package that he also added i listed them with this
_menuadded.html.erb
<h1>menu you added</h1>
<% #reservation_package.package_line_items.each do |menu|%>
<p><%= link_to menu.menu.name, menu_reservation_reservation_package_reservation_package_pages_path(#reservation,#reservation_package,menu) %></p>
<p><%= link_to "delete item" ,reservation_package_package_line_item_path(#reservation_package,menu), :method => :delete%></p>
<%end%>
then i try to route to the next static page with this <p><%= link_to menu.menu.name, menu_reservation_reservation_package_reservation_package_pages_path(#reservation,#reservation_package,menu) %></p>
and the it produce this URL http://localhost:3000/reservations/10/reservation_packages/39/reservation_package_pages/menu.29
im just wondering if how can i catch the menu that he opened i mean how to catch this localhost:3000/reservations/10/reservation_packages/39/reservation_package_pages/menu.29
in my menu semi static page controller where this route to,i tried this
#reservation_package = ReservationPackage.find(params[:reservation_package_id])
#menu = Menu.find(params[:id])
but didn't work at all im just wondering if im doing it right or if im wrong can you give me a advice how to implement this kind of module? thanks more power
ROUTES:
resources :services
resources :reservations do
resources :pages do
collection do
get :functionroomlist
get :packagelist
get :package
# get :menu
end
end
resources :reservation_packages do
resources :reservation_package_pages do
collection do
get :menulist
get :menu
end
end
resources :package_line_items
end
resources :reservation_function_rooms
end
We had an similar issue I hope i can help you a little bit
my user can create an productlist and he can directly add new products on it. On the show-form i set an link like this
<%= link_to 'Create', new_product_path(:procuctlist => #productlist.id) %>
In the controller of products i definded the new-Action so
def new
#product_entry = ProductEntry.new
#product_entry.productlist_id = params[:productlist]
respond_to do |format|
format.html # new.html.erb
format.json { render json: #wishlist_entry }
end
end
The Models are defined so
class Product < ActiveRecord::Base
belongs_to :productlists
has_many :registrations, :dependent => :destroy
validates :entry_name, :description, :presence => true
end
class Productlist < ActiveRecord::Base
has_many :products, :dependent => :destroy
end
the table products contains the field productlist_id.
The view of products in our first alpha version contains the productlist_id.
<div class="field">
<%= f.label :productlist_id %><br />
<%= f.number_field :productlist_id %>
</div>
We hope we could help you.

Resources