I know that resources should only be nested once, but I have two models that dip into the triple nested waters, which makes things more complex than they need to be, but I do not see a way to avoid it in these two cases...so:
The nesting works like this: group>navbar>links
I am having issues getting the link form to render:
- simple_form_for new_group_navbar_link_path(#group, #navbar, #link) do |f|
%fieldset.well.pleft80.edit
= f.input :method_name
= f.input :text
= f.input :button
.form-actions
= f.submit nil, :class => 'btn btn-primary pull-right btn-large'
navbar belongs_to groups and link belongs_to navbar
Controller:
class LinksController < ApplicationController
before_filter :fetch_group
before_filter :fetch_navbar
before_filter :fetch_link, only: [:show, :edit, :update, :destroy]
def show
end
def new
#link = Link.new
end
def create
#link = #navbar.links.build(params[:link])
if #link.save
redirect_to #navbar, notice: 'link was successfully updated.'
else
render :new
end
end
def edit
#image = #link.build_image unless #link.image
end
def update
respond_to do |format|
if #link.update_attributes(params[:link])
format.html { redirect_to #navbar, notice: 'link was successfully updated.' }
format.json { head :no_content }
else
format.html { render action: "edit" }
format.json { render json: #link.errors, status: :unprocessable_entity }
end
end
end
def destroy
#link.destroy
redirect_to navbar_path(#navbar)
end
private
def fetch_group
#group = Group.find(params[:group_id])
end
def fetch_navbar
#navbar = Navbar.find(params[:navbar_id])
end
def fetch_link
#link = #navbar.links.find(params[:id])
end
end
Is there something simple I'm overlooking?
just replace
- simple_form_for new_group_navbar_link_path(#group, #navbar, #link) do |f|
with
= simple_form_for new_group_navbar_link_path(#group, #navbar, #link) do |f|
Related
I am working on user_profile_reviews and have got stuck. I have 3 models for now, and I know, that doing a separate model for a profile wasn't really a great idea, but since all my routes depend on this structure, meaning the links in all the views also, I decided not to change it.
To give you a clearer understanding:
Rails.application.routes.draw do
devise_for :user, controllers: { omniauth_callbacks: 'users/omniauth_callbacks', registrations: "users/registrations" }
resources :users do
resources :profiles do
resources :reviews, only: [:new, :create]
end
end
root 'home#index'
end
Here are my controllers:
class ProfilesController < ApplicationController
before_action :set_profile, only: [:show, :edit, :update, :destroy]
def index
#profiles = Profile.all
end
def show
#user = User.find(params[:user_id])
#profile = Profile.find(params[:id])
#reviews = Review.where("profile_id = ?", params[:id])
end
def new
#user = User.find(params[:user_id])
end
def edit
#profile = Profile.find(params[:id])
end
def create
#profile = current_user.build_profile(profile_params)
respond_to do |format|
if #profile.save
format.html { redirect_to user_profile_path(current_user.id, current_user.profile.id), notice: 'Profile was successfully created.' }
format.json { render :show, status: :created, location: #profile }
else
format.html { render :new }
format.json { render json: #profile.errors, status: :unprocessable_entity }
end
end
end
def update
respond_to do |format|
if #profile.update(profile_params)
format.html { redirect_to user_profile_path(current_user.id, current_user.profile.id), notice: 'Profile was successfully updated.' }
format.json { render :show, status: :ok, location: #profile }
else
format.html { render :edit }
format.json { render json: #profile.errors, status: :unprocessable_entity }
end
end
end
def destroy
#profile.destroy
respond_to do |format|
format.html { redirect_to profiles_url, notice: 'Profile was successfully destroyed.' }
format.json { head :no_content }
end
end
private
def set_profile
#profile = Profile.find(params[:id])
end
def profile_params
params.permit(:about, :rating, :avatar)
end
end
Reviews
class ReviewsController < ApplicationController
before_filter :set_profile
before_filter :set_review, only: [:new, :create]
def new
#review = Review.new
end
def create
#profile = Profile.find(params[:profile_id])
#review = #profile.reviews.build(review_params)
#review.user_id = current_user.id
if #review.save
redirect_to #profile
else
redirect_to #profile, notice: "Error saving"
end
end
private
def review_params
params.permit(:content, :rating)
end
def set_pfofile
#profile = Profile.find(params[:profile_id])
end
def set_review
#profile = Profile.find(params[:id])
end
end
So now, I am trying to create a form for reviews, which I am then rendering in Profiles#show, and getting the mistake above.
<div class="submit-review">
<%= form_for [#review, :url => user_profile_reviews_path(#profile)] do |f| %>
<label for="review">How was your experience?</label><br>
<%= f.label :rating %>
<%= f.select :rating, options_for_select([["Please select one", ""], 5, 4, 3, 2, 1]) %>
<%= f.input :content, placeholder:"Please enter your feedback here" %>
<%= f.submit "Submit your review", class: "btn btn-default" %> <br><br>
<% end %>
Showing ... /_form.html.erb where line #2 raised:
No route matches {:action=>"create", :controller=>"reviews", :id=>"2", :user_id=>#<Profile id: 2, about: "lena", rating: 3, created_at: "2019-11-22 21:27:03", updated_at: "2019-11-22 21:27:03", user_id: 2>}, missing required keys: [:profile_id]
But, as I see, it gets to the profile, I am onto, so I don't understand what's the issue here.
Something wrong with the syntax, try this
= form_for([#profile.user, #profile, #review], :url => user_profile_reviews_path(#profile.user, #profile)) do |f|
Since your resources are nested, you need to pass user, profile and then review as the first argument in form_for
Suggestion: Looking at your code, you don't even need user_id, you can avoid nesting profile and review under user in routes.
Hope that helps!
Ok, this worked perfectly for solving the described problem with missing id.
form_for([#profile.user, #profile, #review], :url => user_profile_reviews_path(#profile.user, #profile)) do |f|
I was getting another error though:
First argument in form cannot contain nil or be empty
I saw then, that in my Profile#show I wasn't defining #review. Only reviews. So I did it this way:
def show
#user = User.find(params[:user_id])
#profile = Profile.find(params[:id])
#review = Review.new
#reviews = Review.where("profile_id = ?", params[:id])
end
I can now finally go to profile and there is a review window, which is great! I can't save the reviews though, as another error is showing up. But that's different case. Thank you so much!
I have a problem with a simple browser in my application.
I have already looked for solutions but still displays the following error:
param is missing or the value is empty: expense.
Please help.
Index:
<%= form_tag expenses_path :method => 'get' do %>
<%= text_field_tag :search, params[:search] %>
<%= submit_tag "Search" , name: nil %>
Controller:
class ExpensesController < ApplicationController
before_action :set_expense, only: [:show, :edit, :update, :destroy]
# GET /expense expense
# GET /expense.json
def index
#expenses = Expense.search(params[:search])
end
# GET /expense/1
# GET /expense/1.json
def show
end
# GET /expense/new
def new
#expense = Expense.new
end
# GET /expense/1/edit
def edit
end
# POST /expense
# POST /expense.json
def create
#expense = Expense.new(expense_params)
respond_to do |format|
if #expense.save
format.html { redirect_to #expense, notice: 'zostały zapisane.' }
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
# PATCH/PUT /expense/1
# PATCH/PUT /expense/1.json
def update
respond_to do |format|
if #expense.update(expense_params)
format.html { redirect_to #expense, notice: 'expense was successfully updated.' }
format.json { render :show, status: :ok, location: #expense }
else
format.html { render :edit }
format.json { render json: #expense.errors, status: :unprocessable_entity }
end
end
end
# DELETE /expense/1
# DELETE /expense/1.json
def destroy
#expense.destroy
respond_to do |format|
format.html { redirect_to #expense, notice: 'Zakupy zostały usunięte.' }
format.json { head :no_content }
end
end
private
# Use callbacks to share common setup or constraints between actions.
def set_expense
#expense = Expense.find(params[:id])
end
# Never trust parameters from the scary internet, only allow the white list through.
def expense_params
params.require(:expense).permit(:date, :price, :category, :where)
end
Expense.rb:
class Expense < ApplicationRecord
def self.search(search)
if search
where (['where LIKE ?',"%#{search}%"])
else
all
end
end
permit the search in your params
def expense_params
params.require(:expense).permit(:date, :price, :category, :where, :search)
end
please correct the form_tag syntax
<%= form_tag(expenses_path,method: :get) do %>
<%= text_field_tag :search, params[:search] %>
<%= submit_tag "Search" , name: nil %>
<% end %>
The error message you report is most likely coming from your expense_params method:
params.require(:expense) means you expect the parameters to be of the form params[:expense][:date], params[:expense][:price], etc.
My best guess is that your form is not actually submitting a GET request and so it's going to the create method rather than the index method.
I think this may be occurring because you are missing a comma between expenses_path and :method => 'get' in your form_tag. It should be:
<%= form_tag expenses_path, :method => 'get' do %>
Here i have a project to which i am adding a session and for a project session i am trying to add task.
i am able to create project and add project session for project but when i am trying to add task for session using project_sessions_id i am getting error Couldn't find ProjectSession with 'id'= and 'app/controllers/tasks_controller.rb:60:in set_project_session i am able to get the project session id also project_sessions/11 in the url but when i click 'create task' i am getting this error. how can i resolve this?
here's what i have done
ProjectSessionController:
class ProjectSessionsController < ApplicationController
before_action :set_project_session, only: [:show, :edit, :update, :destroy]
before_action :authenticate_user!
before_action :set_project
def index
#project_sessions = ProjectSession.all
end
def show
#project_sessions = ProjectSession.where(project_id: #project.id).order("created_at DESC")
end
def new
#project_session = ProjectSession.new
end
def edit
end
def create
#project_session = ProjectSession.new(project_session_params)
#project_session.project_id = #project.id
#respond_to do |format|
if #project_session.save
redirect_to #project
#format.html { redirect_to #project_session, notice: 'Project session was successfully created.' }
#format.json { render :show, status: :created, location: #project_session }
else
format.html { render :new }
format.json { render json: #project_session.errors, status: :unprocessable_entity }
end
#end
end
def update
respond_to do |format|
if #project_session.update(project_session_params)
format.html { redirect_to #project_session, notice: 'Project session was successfully updated.' }
format.json { render :show, status: :ok, location: #project_session }
else
format.html { render :edit }
format.json { render json: #project_session.errors, status: :unprocessable_entity }
end
end
end
def destroy
#project_session.destroy
respond_to do |format|
format.html { redirect_to project_sessions_url, notice: 'Project session was successfully destroyed.' }
format.json { head :no_content }
end
end
private
# Use callbacks to share common setup or constraints between actions.
def set_project_session
#project_session = ProjectSession.find(params[:id])
end
def set_project
#project = Project.find(params[:project_id])
end
# Never trust parameters from the scary internet, only allow the white list through.
def project_session_params
params.require(:project_session).permit(:session_date, :session_name, :session_description, :start_time, :end_time)
end
end
Task controller:
class TasksController < ApplicationController
before_action :set_task, only: [:show, :edit, :update, :destroy]
before_action :authenticate_user!
before_action :set_project_session
def index
#tasks = Task.all
end
def show
end
def new
#task = Task.new
end
def edit
end
def create
#task = Task.new(task_params)
#task.session_id = #project_session.id
respond_to do |format|
if #task.save
redirect_to #project_session
else
format.html { render :new }
format.json { render json: #task.errors, status: :unprocessable_entity }
end
end
end
def update
respond_to do |format|
if #task.update(task_params)
format.html { redirect_to #task, notice: 'Task was successfully updated.' }
format.json { render :show, status: :ok, location: #task }
else
format.html { render :edit }
format.json { render json: #task.errors, status: :unprocessable_entity }
end
end
end
def destroy
#task.destroy
respond_to do |format|
format.html { redirect_to tasks_url, notice: 'Task was successfully destroyed.' }
format.json { head :no_content }
end
end
private
def set_task
#task = Task.find(params[:id])
end
def set_project_session
#project_session = ProjectSession.find(params[:project_session_id])
end
def task_params
params.require(:task).permit(:name, :description)
end
end
routes:
Rails.application.routes.draw do
get 'hr_dashboard/index'
resources :roles
resources :project_sessions
devise_for :users
resources :tasks
resources :projects do
resources :project_sessions, except: [:show, :index]
end
resources :project_sessions do
resources :tasks, except: [:show, :index]
end
authenticated :user do
root 'admindashboard#index', as:"authenticated_root"
end
root 'welcome#index'
get 'userdashboard/index'
get 'admindashboard/index'
get 'welcome/index'
end
View for creating new task
<div class="container">
<h1>New Task</h1>
<%= form_for(#task) do |f| %>
<% if #task.errors.any? %>
<div id="error_explanation">
<h2><%= pluralize(#task.errors.count, "error") %> prohibited this task from being saved:</h2>
<ul>
<% #task.errors.full_messages.each do |message| %>
<li><%= message %></li>
<% end %>
</ul>
</div>
<% end %>
<div class="field">
<%= f.label :name %><br>
<%= f.text_field :name %>
</div>
<div class="field">
<%= f.label :description %><br>
<%= f.text_area :description %>
</div>
<div class="actions">
<%= f.submit %>
</div>
<% end %>
<%= link_to 'Back', tasks_path %>
</div>
I figured it out!
I had forgotten to add #project_session in
<%= form_for([#project_session, #task]) do |f| %>
just added that and it worked.
In your create action of Task controller, you have to add:
#task= #project_session.tasks.build(task_params)
Because right now, you're not telling the task, to build from the project_session (or with respect to the project_session) you're just telling it to create a new task.
#task = Task.new
And in the routes.rb file, you've prepared for that to happen by nesting the resources, so it's currently looking for the ID of a task that belongs_to project_session. But can't find any.
And also, in your form_for element when creating a ProjectSssion you have let that form know which route it should belong to - I guess you could say - since you still have the un-nested resources available:
resources :tasks
If project_sessions shouldn't be creatable without a parent, there's no reasons for keeping that, so you should just remove it.
Anyways, here's what the form_for should look like:
<%= form_for([#project_session, #task]) do |f| %>
I am trying to search by keywords some titles, I need help to set my instances variables tutos.... ( It seems that I need two instances #tutos... but I don't know how to do?
I tried:
#tutos = Tuto.all.includes(:user, :category) || Tuto.search(params[:search]) but I have the same error...
I have undefined method each' for nil:NilClass even though I have the instance variable set... thanks for your help
my controller
class TutosController < ApplicationController
before_action :authenticate_user!, only: [:new, :create]
before_action :set_tuto, only: [:show, :edit, :update, :destroy, :upvote]
def index
#tutos = Tuto.all.includes(:user, :category)
keyword_search
#categories = Category.all
end
def keyword_search
#tutos = Tuto.search(params[:search])
end
def show
#tuto = Tuto.find(params[:id])
#user = User.all
end
def new
#tuto = Tuto.new
end
def edit
end
def create
#tuto = Tuto.new(tuto_params)
#tuto.user_id = current_user.id
respond_to do |format|
if #tuto.save
flash[:success] = "Test"
format.html { redirect_to #tuto, notice: 'Tuto was successfully created.' }
format.json { render :show, status: :created, location: #tuto }
else
format.html { render :new }
format.json { render json: #tuto.errors, status: :unprocessable_entity }
end
end
end
def update
respond_to do |format|
if #tuto.update(tuto_params)
format.html { redirect_to #tuto, notice: 'Tuto was successfully updated.' }
format.json { render :show, status: :ok, location: #tuto }
else
format.html { render :edit }
format.json { render json: #tuto.errors, status: :unprocessable_entity }
end
end
end
def destroy
#tuto.destroy
respond_to do |format|
format.html { redirect_to tutos_url, notice: 'Tuto was successfully destroyed.' }
format.json { head :no_content }
end
end
def upvote
#tuto.upvote_by current_user
redirect_to :back
end
private
def set_tuto
#tuto = Tuto.find(params[:id])
end
def tuto_params
params.require(:tuto).permit(:title, :content, :id, :user_id, :category_id)
end
end
my index
.container
.row
.col-xs-12
h1.text-gray Tutorials
h4 Search by Title
=form_tag tutos_path, :method => 'get' do
=text_field_tag :search, params[:search]
=submit_tag "Search", class:'btn btn-default'
= form_tag '/tutos', method: 'get' do
select[name="category.id"]
- #categories.each do |category|
| <option value="
= category.id
| ">
= category.name
input[type="submit" value="Search", class="btn btn-default"]
.col-xs-12
-if user_signed_in?
= link_to "Create a tuto", new_tuto_path, class:"btn btn-success"
br
br
#tutos.transitions-enabled
-#tutos.each do |tuto|
.box.panel-default
= link_to image_tag(image_by_category(tuto.category.try(:name))), tuto_path(tuto)
h3 = link_to tuto.title, tuto_path(tuto), class:"title-link"
h6
| Created by:
span<>
= tuto.user.full_name
br
span.glyphicon.glyphicon-heart
span<>
= tuto.get_upvotes.size
br
br
tuto model
class Tuto < ActiveRecord::Base
acts_as_votable
belongs_to :user
belongs_to :category
validates :category_id, presence: true
def self.search(search)
if search
where(["title LIKE ?","%#{search}%"])
else
all
end
end
end
Update your index like this. you don't need keyword_search.
def index
if params[:search]
#tutos = Tuto.search(params[:search]).includes(:user, :category)
else
#tutos = Tuto.all.includes(:user, :category)
end
#categories = Category.all
end
Hope this helps.
def index
#tutos = Tuto.all.includes(:user, :category)
keyword_search // **Here you are overriding #tutos... First have no meaning always**
#categories = Category.all
end
def keyword_search
#tutos = Tuto.search(params[:search])
end
2 ->
def self.search(search)
if search
where(["title LIKE ?","%#{search}%"])
else
all
end
end
It will give nil if searched data is not available, so you should handle this.. otherwise each with nil occurs
I am unable to figure out the method to pass params to a link tag. What I want to do is When someone clicks on the Join Group link, the Membership model shall have a new row with group_id as the current group id and the user id as the current user id. The Membership model currently consists of two columns : user_id and group_id that maps users to groups. Can anyone help me with the mistake I am making.
Here is the code
Groups : show.html.erb
<p id="notice"><%= notice %></p>
<p>
<strong>Title:</strong>
<%= #group.title %>
</p>
<p>
<strong>Desc:</strong>
<%= #group.desc %>
</p>
<p>
<strong>Creator:</strong>
<%= #creator.first_name %>
</p>
<%= link_to 'Join Group', memberships_path(:group_id => #group.id, :user_id => current_user.id ), method: :post %>
<%= link_to 'Edit', edit_group_path(#group) %> |
<%= link_to 'Back', groups_path %>
and here is the Memberships controller
class MembershipsController < ApplicationController
before_action :set_membership, only: [:show, :edit, :update, :destroy]
# GET /memberships
# GET /memberships.json
def index
#memberships = Membership.all
end
# GET /memberships/1
# GET /memberships/1.json
def show
#membership = Membership.find(params[:id])
#user = User.find(#membership.user_id)
#group = Group.find(#membership.group_id)
end
# GET /memberships/new
def new
#membership = Membership.new
end
# GET /memberships/1/edit
def edit
end
# POST /memberships
# POST /memberships.json
def create
#membership = Membership.new(membership_params)
respond_to do |format|
if #membership.save
format.html { redirect_to #membership, notice: 'Membership was successfully created.' }
format.json { render :show, status: :created, location: #membership }
else
format.html { render :new }
format.json { render json: #membership.errors, status: :unprocessable_entity }
end
end
end
# PATCH/PUT /memberships/1
# PATCH/PUT /memberships/1.json
def update
respond_to do |format|
if #membership.update(membership_params)
format.html { redirect_to #membership, notice: 'Membership was successfully updated.' }
format.json { render :show, status: :ok, location: #membership }
else
format.html { render :edit }
format.json { render json: #membership.errors, status: :unprocessable_entity }
end
end
end
# DELETE /memberships/1
# DELETE /memberships/1.json
def destroy
#membership.destroy
respond_to do |format|
format.html { redirect_to memberships_url, notice: 'Membership was successfully destroyed.' }
format.json { head :no_content }
end
end
private
# Use callbacks to share common setup or constraints between actions.
def set_membership
#membership = Membership.find(params[:id])
end
# Never trust parameters from the scary internet, only allow the white list through.
def membership_params
params.require(:membership).permit(:user_id, :group_id)
end
end
You should have a method in your controller that handles adding a user to a group, something like:
class MembershipsController < ApplicationController
...
def join_group
#user = User.find(params[:user]
#user.group_id = params[:group_id].to_i
end
...
end
Then routes.rb needs a path to that controller method:
get 'memberships/join_group' => 'memberships#join_group'
which will result in a path like memberships_join_group_path that you will use in your link_to:
<%= link_to 'Join Group', memberships_join_group_path(:group_id => #group.id, :user_id => current_user.id ) %>
The path you're currently using is sending params to your index method.
try
def create
#member = current_user.members.build(:group_id => params[:group_id])
if #member.save
flash[:notice] = "You have joined this group."
redirect_to members_path
else
flash[:error] = "Unable to join."
redirect_to members_path
end
end
and use memberships instead of members