how can i set value from dropdown list on rails? - ruby-on-rails

i am beginner on rails. I have product - brands list.
routes rb
Rails.application.routes.draw do
get 'welcome/index'
# For details on the DSL available within this file, see https://guides.rubyonrails.org/routing.html
resources :brands do
resources :products
end
root 'welcome#index'
end
Brand.rb
class Brand < ApplicationRecord
has_many :products, dependent: :destroy
validates :title, presence: true,
length: { minimum: 2 }
end
Product.rb
class Product < ApplicationRecord
belongs_to :brand
end
products.controller
class ProductsController < ApplicationController
#before_action :set_brand
skip_before_action :verify_authenticity_token
def new
if params[:brand_id]
#brand = Brand.find(params[:brand_id])
end
#product = Product.new
end
def edit
#brand = #product.brand
#product = Product.find(params[:id])
end
def update
#brand = Brand.find(params[:brand_id])
#product = Product.find(params[:id])
#product.update(product_params)
redirect_to brand_path(#brand)
end
def create
#brand = Brand.find(params[:brand_id])
#product = #brand.products.create(product_params)
redirect_to brand_path(#brand)
end
def destroy
#brand = Brand.find(params[:brand_id])
#product = #brand.products.find(params[:id])
#product.destroy
redirect_to brand_path(#brand)
end
def update
#brand = Brand.find(params[:brand_id])
#product = #brand.products.find(params[:id])
#product.destroy
end
helper_method :update
private
def product_params
params.require(:product).permit(:name)
end
def set_brand
#brand = Brand.find(params[:brand_id])
end
end
.../products/new.html.erb
<h1>Add a new product</h1>
<%= form_with(model: [ #brand, #brand.products.build ], local: true) do |form| %>
<p>
<%= form.label :name,"Product name: " %><br>
<%= form.text_field :name %>
</p>
<p>
</p>
<%= form.label :title,"Select a Brand" %>
<%= form.collection_select(:brand_id, Brand.all, :id, :title,{selected: #brand.id}) %>
<p>
<%= form.submit "Add a product" %>
</p>
<% end %>
new.html.erb picture
so i want to set brand_id from selected item on dropdown list. This case, i select first item for brand_id but i cant change the brand_id. How can i set brand_id which is selected from dropdown list ? and how can i save it.

You forgot to permit the parameter in product_params. Which should be:
params.require(:product).permit(:name, :brand_id)
Unpermitted parameters are ignored by the create and update methods.

Since you already have setup products as a nested resource you neither need or want that select. The brand_id param will be passed through the path (the action attribute of the form). The user would choose what brand he wants to add a product to by what link he clicked to get to the new form.
While you could add a select to the form its a major WTF moment when you send:
POST /brands/1/products, { products: { brand_id: 5 }}
And it ends up creating a product that does not belong to Brand 1. If the param from the form was blank you would also get a really strange result.
If really wanted a form where the user selects the "target" brand on the form itself you would create a non-nested respresentation:
<%= form_with(model: #product) do |f| %>
<%= f.collection_select :brand_id %>
<% end %>
class ProductsController < ApplicationController
# GET /products/new
def new
#product = Product.new
end
# POST /products
def create
#product = Product.new(product_params)
if #product.save
redirect_to #product
else
render :new
end
end
def product_params
params.require(:product).permit(:foo, :bar, :brand_id)
end
end

Related

How to implement Update and destroy methods with a has many through association? Rails 5

Hi I'm having trouble by making the update and destroy method in my posts_controller, I'm able to create new Posts but I'm not able to update and I want to know how to destroy the model while destroying all the associations with the other model it.
My models:
Post Model
class Post < ApplicationRecord
has_many :comments, dependent: :destroy
has_many :has_categories
has_many :categories, through: :has_categories
validates :title, presence: true,
length: { minimum: 5 }
after_create :save_categories
def categories=(value)
#categories = value
end
private
def save_categories
#categories.each do |category_id|
HasCategory.create(category_id: category_id, post_id: self.id)
end
end
end
Has_Category model
class HasCategory < ApplicationRecord
belongs_to :post
belongs_to :category
end
Category Model
class Category < ApplicationRecord
validates :name, presence: true
has_many :has_categories
has_many :posts, through: :has_categories
end
So in my partial form for the new and the edit actions is like this
<%= form_with model: #post, local: true do |form| %>
<!--Inputs before the categories-->
<div>
<label>Categories</label>
<% #categories.each do |category| %>
<div>
<%= check_box_tag "categories[]", category.id %> <%= category.name %>
</div>
<% end %>
</div>
<div>
<%= form.submit %>
</div>
<% end %>
My posts_controller create and update method
def create
#post = Post.new(post_params)
#post.categories = params[:categories]
if #post.save
redirect_to #post
else
render :new
end
end
def update
#post = Post.find(params[:id])
#post.categories = params[:categories]
if #post.update(post_params)
redirect_to #post
else
render :edit
end
end
My create action is working but the update action is just updating the inputs before the check_box_tag.
I know that the save_categories method on my Post model is the one who is taking the array I'm receiving from the form and creating the HasCategory association, How should I make the update action or even the destroy action given the situation that Is a many to many association?
The line has_many :categories, through: :has_categories gives you category_ids for post. So you can change your form:
<%= form_with model: #post, local: true do |form| %>
<!--Inputs before the categories-->
<div>
<label>Categories</label>
<%= f.collection_check_boxes(:category_ids, #categories, :id, :name)
</div>
<div>
<%= form.submit %>
</div>
<% end %>
and controller:
def create
# you need it here for correct rerendering `new` on validation error
#categories = Category.all # or maybe you have here more complicated query
#post = Post.new(post_params)
if #post.save
redirect_to #post
else
render :new
end
end
def update
# you need it here for correct rerendering `edit` on validation error
#categories = Category.all # or maybe you have here more complicated query
#post = Post.find(params[:id])
if #post.update(post_params)
redirect_to #post
else
render :edit
end
end
private
def post_params
params.require(:post).permit(:name, :some_other_post_params, category_ids: [])
end
You need to remove callback and categories=(value) method from the Post model. And define #categories in the new and edit actions. If it equals Category.all you can just put it to the form: f.collection_check_boxes(:category_ids, Category.all, :id, :name), without defining #variable

How can I create a form without using resources (action :new, :create) in Rails?

This is my controller
class SchoolsController < ApplicationController
def teacher
#teacher = Teacher.new
end
def form_create
#teacher = Teacher.new(teacher_params)
if teacher.save
redirect_to schools_teacher_path
else
flash[:notice] = "error"
end
end
private
def teacher_params
params.require(:teacher).permit(:name)
end
end
This is my views/schools/teacher.html.erb
<%= form_for :teacher do |f| %>
<%= f.text_field :name %>
<%= f.submit %>
<% end %>
I am new to Ruby on Rails, and not sure how to proceed.
You should move this to a TeachersController let me show you how:
First you need to create the controller, you can get this done by typing this on the terminal at the project root directory:
$ rails g controller teachers new
Then into your route file (config/routes.rb):
resources :teachers, only: [:new, :create]
After that go to the teachers_controller.rb file and add the following:
class TeachersController < ApplicationController
def new
#teacher = Teacher.new
end
def reate
#teacher = Teacher.new(teacher_params)
if #teacher.save
redirect_to schools_teacher_path
else
redirect_to schools_teacher_path, notice: "error"
end
end
private
def teacher_params
params.require(:teacher).permit(:name)
end
end
Then you can have the form at views/teachers/new.html.erb:
<%= form_for :teacher do |f| %>
<%= f.text_field :name %>
<%= f.submit %>
<% end %>
Please let me know how it goes!

No route matches while creating nested resource in Rails

I am trying to create a new teacher for a specific school in my project and I got this error:
No route matches [POST] "/schools/3/teachers/new"
Here is my teachers_controller.rb:
class TeachersController < ApplicationController
def new
#teacher = Teacher.new
end
def create
#teacher = Teacher.new(teacher_params)
#teacher.save
redirect_to school_path(school)
end
private
def teacher_params
params.require(:teacher).permit(:firstName, :lastName, :middleName)
end
end
schools_controller.rb:
class SchoolsController < ApplicationController
def show
#school = School.find(params[:id])
end
def new
#school = School.new
end
def edit
#school = School.find(params[:id])
end
def update
#school = School.find(params[:id])
if #school.update(school_params)
redirect_to #school
else
render 'edit'
end
end
def index
#schools = School.all
end
def create
#school = School.new(school_params)
if #school.save
redirect_to schools_path
else
render 'new'
end
end
def destroy
#school = School.find(params[:id])
#school.destroy
redirect_to schools_path
end
private
def school_params
params.require(:school).permit(:name)
end
end
routes.rb:
Rails.application.routes.draw do
resources :schools do
resources :teachers
end
# The priority is based upon order of creation: first created -> highest priority.
# See how all your routes lay out with "rake routes".
# You can have the root of your site routed with "root"
root 'welcome#index'
And teachers/new.html.erb:
<%= form_for :teacher, url: school_teachers_path(school) do |f| %>
<p>
<%= f.label :firstName %><br>
<%= f.text_field :firstName %>
</p>
<p>
<%= f.label :lastName %><br>
<%= f.text_field :lastName %>
</p>
<p>
<%= f.label :middleName %><br>
<%= f.text_field :middleName %>
</p>
<p>
<%= f.submit %>
</p>
<% end %>
As your teacher resource is nested under the school resource, so you need to pass the school when you try to create a teacher.
Try changing your new and create actions in teachers_controller.rb to something like this:
def new
#school = School.find(params[:school_id])
#teacher = #school.teachers.build
end
def create
#school = School.find(params[:school_id])
#teacher = #school.teachers.build(params[:teacher])
#teacher.save
redirect_to school_path(#school)
end
And, then change your form to this:
<%= form_for([#school, #teacher]) do %>
. . .
. . .
<% end %>
Try this in your form:
<%= form_for [school, Teacher.new] do |f| %>
The path you are posting to is for the index of teachers at a school:
school_teachers GET /schools/:school_id/teachers(.:format) teachers#index
I believe that it's a has_many belongs_to association. So you need to first change your teacher controller create action and new action.
class TeachersController < ApplicationController
def new
get_school
#teacher = #school.teachers.build
end
def create
get_school
#teacher = #school.teachers.build(teacher_params)
If #teacher.save
redirect_to school_path(school)
else
render 'new'
end
private
def teacher_params
params.require(:teacher).permit(:firstName, :lastName, :middleName)
end
def get_school
#school = School.find (params [:school_id])
end
end
Then in your form you 'll do :
<%= form_for([#school,#teacher]) do |f| %>
Hope this will help

Associating nested attributes to user

I'm trying to build a small expense tracking app using Rails 4.1. Using devise for authorization. Expense and it's nested attribute, comments belong to a user. The associations are set up in the model and expenses are getting associated with the user. Here's the Expense controller:
class ExpensesController < ApplicationController
def new
#expense = Expense.new
#item = #expense.items.build
##comment = #expense.comments.build
end
def index
#expenses = Expense.all
##items = Item.where(:expense_id => #expense.id)
end
def show
#expense = Expense.find(params[:id])
#items = Item.where(:expense_id => #expense.id)
end
def create
#expense = current_user.expenses.new(expense_params)
respond_to do |format|
if #expense.save
ExpenseMailer.expense_submission(#expense).deliver
format.html { redirect_to #expense, notice: 'Expense Report Submitted.' }
format.json { render :show, status: :created, location: #expense }
else
format.html { render :new }
format.json { render json: #expense.errors, status: :unprocessable_entity }
end
end
end
def edit
#expense = Expense.find(params[:id])
end
def update
#expense = Expense.find(params[:id])
##comment = #expense.comments.build
if #expense.update(expense_params)
#if #comment.save
#ExpenseMailer.comments_added(#expense).deliver
flash[:notice] = "Expense Report Updated"
redirect_to expenses_path
#else
# flash[:notice] = "Expense Report Updated"
#redirect_to expenses_path
##end
else
render 'edit'
end
end
The form from where the comment attributes are built looks like:
<%= nested_form_for (#expense) do |f| %>
<div class="form-group">
<%= f.label :state %><br />
<%= f.select :state, Expense.states, :include_blank => false, class: "form-control" %>
</div>
<%= f.fields_for :comments, #expense.comments.build do |comment| %>
<div class="form-group">
<%= comment.label :comment%>
<%= comment.text_area :comment, class: "form-control" %>
</div>
<%= comment.hidden_field :commenter %>
<% end %>
<%= f.submit "Submit", class: "btn btn-primary" %>
<% end %>
</div>
</div>
The #comment.commenter = current_user isn't adding the current user id to the database. Should I include it in the expense controller somewhere?
You have to add:
#comment.commenter = current_user
below that if statement. Like this:
def create
#article = Expense.find(params[:expense_id])
if #comment = #expense.comments.create(comment_params)
#comment.commenter = current_user
#comment.save
ExpenseMailer.comments_added(#expense).deliver
redirect_to expenses_path
end
end
And then save the comment again. In your current code you're overwriting the #comment object with the newly created object by doing:
#comment = #expense.comments.create(comment_params)
but you haven't set the commenter on that new object anywhere yet.
Model
I just tried to create better code for your strong params, but I couldn't work out how to include the param in your nested attributes
I would therefore recommend using the inverse_of: method in your Comment model to get it sorted properly:
#app/models/expense.rb
Class Expense < ActiveRecord::Base
belongs_to :user
has_many :comments, inverse_of: :expense
accepts_nested_attributes_for :comments
end
#app/models/comment.rb
Class Comment < ActiveRecord::Base
belongs_to :expense, inverse_of: :comments
before_create :populate_expense, on: :create
private
def populate_expense
self.commenter_id = self.expense.user_id
end
end
This should work if you're populating the comments from the accepts_nested_attributes_for directive
Comments
I don't understand why you've created two create actions for both your expenses and comments controllers - the controller action is meant to be independent of the Model
What I'm trying to say is that if you think the comments#create controller action will be invoked by your nested attribute creation, you'd be mistaken - it is only invoked when you send a request to it through the Rails router :)
If you're creating Comments and Expenses separately, you'll be able to use these two different actions; but they won't be invoked by each other. Only Model methods can be invoked by the controller (you shouldn't be calling other controller methods)
If you wanted to create a Comment from the expenses#show page, here's how you'd set it up:
#config/routes.rb
resources :expenses do
resources :comments #-> domain.com/expenses/:expense_id/comments/new
end
#app/controllers/expenses_controller.rb
Class CommentsController < ApplicationController
def new
#expense = Expense.find params[:expense_id]
#comment = #expense.comments.new
end
def create
#expense = Expense.find params[:expense_id]
#comment = #expense.comments.new(comment_params)
#comment.save
end
private
def comment_params
params.require(:comment).permit(:comment, :params).merge(commenter_id: current_user.id)
end
end
This will work if you wanted to create a comment from the expenses#show page. If you do this, you need to ensure you are calling the comments#new / comments#create actions, rather than those of the expenses controller

has_may for a vote app

I'm new on rails and I'm trying to make my own simple vote application. I have two models:
class Product < ActiveRecord::Base
attr_accessible :description, :title, :photo
has_many :votes
has_attached_file :photo, :styles => { :medium => "300x300" }
before_save { |product| product.title = title.titlecase }
validates :title, presence: true, uniqueness: { case_sensitive: false }
validates :photo, :attachment_presence => true
end
class Vote < ActiveRecord::Base
belongs_to :product
attr_accessible :user_id
end
here is the product controller
class ProductsController < ApplicationController
http_basic_authenticate_with :name => "dhh", :password => "secret", :except => [:index]
def index
#products = Product.all
end
def indexprv
#products = Product.all
end
def show
#product = Product.find(params[:id])
end
def edit
#product = Product.find(params[:id])
end
def new
#product = Product.new
end
def create
#product = Product.new(params[:product])
if #product.save
redirect_to #product
else
render 'new'
end
end
def update
#product = Product.find(params[:id])
if #product.update_attributes(params[:product])
flash[:success] = "Producto Actualizado"
redirect_to root_path
else
render 'edit'
end
end
def destroy
Product.find(params[:id]).destroy
flash[:success] = "Producto Eliminado."
redirect_to root_path
end
end
I have many questions.
How can I show the total votes per products on my index page of products?
How can I create a button on my index product page to add a vote for a product?
I don't know how to do this and I couldn't find any tutorial o blog with a similar example.
Thanks for your help.
Your index view probably already loops through every product you have (either by a partial or by a loop). In your loop/partial do something like: (assuming the variable product has a instance of a product)
product.votes.count
to get the number of votes. To get a button to add a vote do something along the lines of:
link_to "Add Vote", new_product_vote_path(product), action: :new
A good tutorial that covers many aspects of rails is: http://ruby.railstutorial.org/chapters/beginning#top
Edit:
Your index method in your controller gives you an array of every product you have. So if you do something like this in your index view (if your using erb):
<ul>
<% #products.each do |product| %>
<li>
<%= product.title %>: <br />
Votes: <%= product.votes.count %> <br />
<%= link_to "Add Vote", new_product_vote_path(product), action: :new %>
</li>
<% end %>
</ul>
it should do what you want

Resources