Setting a controller action inside link_to - ruby-on-rails

I need help with allowing users to click "Follow/Unfollow" link and have the action take place on the user show page. Currently I have the action sitting inside a popup and I don't know how I can set it inside a link action so there would be no need for a popup.
I tried rendering the forms on the show page but that does not show as I expected.
show.html.slim:
- if current_user.following?(#user)
= link_to follow_popup_user_path(#user), class: 'profile_btn save_btn', :'data-mfp-src'=>'#follow_div', remote: true do
= image_tag 'start_icon.png'
span Unfollow
- else
= link_to follow_popup_user_path(#user), class: 'profile_btn save_btn', :'data-mfp-src'=>'#follow_div', remote: true do
= image_tag 'start_icon.png'
_follow.html.slim:
= form_for(current_user.relationships.build(followed_id: #user.id)) do |f|
div
= f.hidden_field :followed_id
= f.submit "Follow", class: "btn btn-large btn-primary"
_unfollow.html.slim:
= form_for(current_user.relationships.find_by(followed_id: #user),
html: { method: :delete }) do |f|
= f.submit "Unfollow", class: "btn btn-large"
Relationships controller:
def create
#user = User.find(params[:relationship] [:followed_id] )
current_user.follow!(#user)
redirect_to #user
end
def destroy
#user = Relationship.find(params[:id]).followed
current_user.unfollow!(#user)
redirect_to #user
end
end

You shouldn't need a form for this, just pass the correct params to a nested route for relationships, then handle the specifics in the user model follow and unfollow methods. Try something like this:
users/show.slim
- if current_user.following?(#user)
= link_to new_user_relationship_path(id: current_user.id, followed_id: #user.id), class: 'profile_btn save_btn' do
= image_tag 'start_icon.png'
span Unfollow
- else
= link_to user_relationship_path(id: current_user.id, followed_id: #user.id), method: delete, class: 'profile_btn save_btn' do
= image_tag 'start_icon.png'
routes
resources :users do
resources :relationships
relationships_controller
def create
#user = User.find(user_params(:id))
#followed_user = User.find(user_params(:followed_id))
#user.follow #followed_user
redirect_to #user
end
def destroy
#user = User.find(user_params(:id))
#followed_user = User.find(user_params(:followed_id))
#user.unfollow #followed_user
redirect_to #user
end
private
def user_params
params.require(:user).permit(:followed_id, )
end

Related

Ruby 2.7.0 on Rails 6.1, 2 separate post forms one view

hope your having a wonderful day drinking some coffee and responding to some forms.
Problem:
As my title states, I am trying to create 2 forms on one view. I am new to ruby on rails.
My controller functions:
Controller name is border_rotation:
def create
if params[:export_submit]
#border_rotation_export = BorderRotationExport.new(border_rotation_export_params)
respond_to do |format|
if #border_rotation_export.save
flash[:success] = "Export successfully created"
format.html { render :new }
else
flash[:error] = "Export was not created."
end
end
else
#border_rotation_import = BorderRotationImport.new(border_rotation_import_params)
respond_to do |format|
if #border_rotation_import.save
flash[:success] = "Export successfully created"
format.html { render :new }
else
flash[:error] = "Export was not created."
end
end
end
end
def new
#border_rotation_export = BorderRotationExport.new
#border_rotation_import = BorderRotationImport.new
end
private
def border_rotation_export_params
params.require(:border_rotation_export).permit(:exporter_name,:vehicle_color,:rot_num,:current_date,:current_time,:goods_description,:license_num,:entry)
end
def border_rotation_import_params
params.require(:border_rotation_import).permit(:importer_name,:vehicle_color,:rot_num,:current_date,:current_time,:goods_description,:license_num,:entry)
end
My new View form:
It has 2 forms and is enclosed in bootstrap tabs
<%= form_for #border_rotation_export, url: rotation_create_path, method: :post do |f|%>
<lable>Importer Name: </lable><%= f.text_field :importer_name, class: "form-control", placeholder: "Importer Name"%>
<lable>Vehicle Color: </lable><%= f.text_field :vehicle_color, class: "form-control", placeholder: "Vehicle Color"%>
**its fields**
<% end %>
and
<%= form_for #border_rotation_import, url: rotation_create_path, method: :post do |f|%>
<lable>Exporter Name: </lable><%= f.text_field :exporter_name, class: "form-control", placeholder: "Exporter Name"%>
<lable>Vehicle Color: </lable><%= f.text_field :vehicle_color, class: "form-control", placeholder: "Vehicle Color"%>
**its fields**
<% end %>
The error in my new.html.rb
First argument in form cannot contain nil or be empty
Displays this in red highlighted
<%= form_for #border_rotation_export, url: rotation_create_path, method: :post do |f|%>
My guess is that it submits both forms but only has the parameters for one form with the input data. Once I submit, it saves to the database but it gives me the error
**Routes **
get 'rotation/create', to: 'border_rotation#create'
post 'rotation/create', to: 'border_rotation#create'
Request
Parameters:
{"utf8"=>"✓",
"authenticity_token"=>"Cu52CIDgrY0b7Yk6edkd7+RTl5yR4qSEqPPrqWtM0nIQVDvw7eYDF36zduJPLjI+vVNqCfgtLcMDUEkW6qDOdQ==",
"border_rotation_import"=>
{"importer_name"=>"john",
"vehicle_color"=>"red",
"rot_num"=>"11sssfeeea",
"current_date"=>"2021-09-22",
"current_time"=>"09:37",
"goods_description"=>"yogurt",
"license_num"=>"c-11223",
"entry"=>"c1223"},
"import_submit"=>"Submit"}
Thank you in advance
You can setup the controller with a lot less redundancy:
# config/routes.rb
resources :rotations, only: [:new, :create]
class BorderRotationsController < ApplicationController
# GET /rotations/new
def new
populate_forms
end
# POST /rotations
def create
resource = model_class.new(create_params)
set_ivar(resource) # sets #border_rotation_export or #border_rotation_import
if resource.save
flash[:success] = "#{humanized} successfully created"
redirect_to action: :new
else
populate_forms
flash[:error] = "#{humanized} could not be created - please try again."
render :new
end
end
private
# gets the model class via params[:subtype]
def model_class
#model_class ||= begin do
if params[:border_rotation_export].present?
BorderRotationExport
else
BorderRotationImport
end
end
end
def humanized
model_class == BorderRotationExport ? 'Export' : 'Import'
end
def set_ivar(value)
instance_variable_set(
"##{param_key}",
value
) ​
​end
# just sets up the instance variables for the form
def populate_forms
#border_rotation_export ||= BorderRotationExport.new
#border_rotation_import ||= BorderRotationImport.new
end
# border_rotation_export or border_rotation_import
def param_key
model_class.model_name.param_key
end
def create_params
require(param_key).permit(
:exporter_name, :vehicle_color, :rot_num,
:current_date,:current_time, :goods_description,
:license_num, :entry
)
end
And then use partials so that you can resuse the same form:
# app/views/border_rotations/new.html.erb
<%= render partial: 'form',
locals: { border_rotation: #border_rotation_export } %>
<%= render partial: 'form',
locals: { border_rotation: #border_rotation_import } %>
# app/views/border_rotations/new.html.erb
<%= form_with model: border_rotation, url: rotations_path do |f| %>
<div class="field">
<%= f.label :importer_name %>
<%= f.text_field :importer_name, class: "form-control" %>
</div>
<div class="field">
<%= f.label :importer_name %>
<%= f.text_field :importer_name, class: "form-control" %>
</div>
# ...
<% end %>
If the requirements diverge use two separate routes/controllers and inheritance instead of blooming out in tons of new code paths.

Why my form don't work?

Here is my tournaments_controller file:
class TournamentsController < ApplicationController
before_action :authenticate_user!, only: [:new, :create, :destroy]
def index
end
def show
end
def new
render action: 'new'
end
def create
self.tournament = Tournament.new(tournament_params)
if tournament.save
flash[:info] = "Tournament created successfully!"
redirect_to root_url
else
render action: 'new'
end
end
def destroy
Tournament.find(params[:id]).destroy
flash[:success] = "Tournament deleted!"
redirect_to root_url
end
private
def tournament_params
params.require(:tournament).permit(:name, :maxplayers)
end
end
This is my form file:
= form_for tournament do |f|
- if tournament.errors.any?
#error_explanation
%h2= "#{pluralize(tournament.errors.count, "error")} prohibited this tournament from being saved:"
%ul
- tournament.errors.full_messages.each do |msg|
%li= msg
.form-group
= f.label :name
= f.text_field :name, class: 'form-control'
= f.submit 'Save', class: 'btn btn-primary'
When my app runs, console display this error:
undefined local variable or method `tournament' for #<#<Class:0x007f0e414fa2d0>:0x007f0e418d9a90>
I'm using simple-form and Haml if it's important.
Can anyone explain to me why I am getting this error?
You have to use an instance variable:
def new
#tournament = Tournament.new
render action: 'new'
end
def create
#tournament = Tournament.new(tournament_params)
if #tournament.save
flash[:info] = "Tournament created successfully!"
redirect_to root_url
else
render action: 'new'
end
end
and
= form_for #tournament do |f|
- if #tournament.errors.any?
#error_explanation
%h2= "#{pluralize(#tournament.errors.count, "error")} prohibited this tournament from being saved:"
%ul
- #tournament.errors.full_messages.each do |msg|
%li= msg
.form-group
= f.label :name
= f.text_field :name, class: 'form-control'
= f.submit 'Save', class: 'btn btn-primary'
= link_to 'Back', categories_path, class: 'btn btn-default'

How to add post via ajax, to loop in rails

i have home page, which render all posts
= render 'posts/post'
welcome_controller.rb
def home
#user = User.all
if user_signed_in?
#posts = current_user.posts.all.page(params[:page]).per(25)
#post = current_user.posts.build
else
hello
end
end
and _post.html.haml
= div_for(#posts.each) do |p|
.content-wrap
%hr
%small= "Отправлено #{p.user.first_name} #{p.user.last_name}"
= image_tag(p.user.img_url, class: 'img-responsive thumbnail user-image-home')
%p.text-content= p.content
%small.text-muted= "Добавлено #{time_ago_in_words(p.created_at)} назад"
%br
- if user_signed_in?
= link_to "Удалить", p, method: :delete, remote: true, data: { confirm: 'Удалить?', 'confirm-button-text' => 'Удалить', 'cancel-button-text' => 'Нет', 'sweet-alert-type' => 'warning' }, class: 'btn btn-danger btn-xs', id: 'wow' if current_user.id == p.user_id
%hr
= paginate #posts
and create.js.erb
$("<%= escape_javascript(render #post) %>").appendTo(".content-wrap");
But it didnt work, in console i have a error like "undefined method each", i have no idea how to add a post, and i need to add it with all divs and styles to posts loop, not only text. Help.
I think for starters user_signed_in? is not returning true so #posts is nil. Try this to make sure #posts is always defined, it will be populated when your conditional is true.
def home
#user = User.all
#posts = []
if user_signed_in?
#posts = current_user.posts.all.page(params[:page]).per(25)
#post = current_user.posts.build
else
hello
end
end
There is a lot of unconventional code here, I'd recommend wizzing through this tutorial: http://railsforzombies.org/

undefined method `has_role?' for nil:NilClass

Trying to apply a rolify method to my user, I get the following error:
undefined method `has_role?' for nil:NilClass
I don't understand why, because current_user should be a global method accessible in every view right? How can it be nil?
Thanks!
Controller
def show
#photo = Photo.friendly.find(params[:id])
impressionist(#photo)
#photos = Photo.latest_photos
#user = #photo.user
#pin = Pin.new
#pin.photo_id = #photo.id
#category = Category.all
#commentable = #photo
#comments = #commentable.comments
#comment = Comment.new
#sponsors = #photo.sponsors
#zone = Zone.all
respond_to do |format|
format.html #show.html.erb
format.json {render json: #photo}
end
end
My view
<% if current_user.has_role? :admin %>
<%= link_to "Eliminar", user_photo_pin_path(user_id: #user.id, photo_id: #photo.id, id: pin.id) , method: :delete, data: { confirm: 'Quieres borrar esto?'}, class: "delete right" %>
<%= link_to 'Editar', edit_user_photo_pin_path(user_id: #user.id, photo_id: #photo.id, id: pin.id), :class=> "link", class: "edit quarter_margin_right right" %>
<% end %>
<% end %>
You need to check if there is a current_user in your views:
<% if current_user && current_user.has_role?(:admin) %>
<%= link_to "Eliminar", user_photo_pin_path(user_id: #user.id, photo_id: #photo.id, id: pin.id) , method: :delete, data: { confirm: 'Quieres borrar esto?'}, class: "delete right" %>
<%= link_to 'Editar', edit_user_photo_pin_path(user_id: #user.id, photo_id: #photo.id, id: pin.id), :class=> "link", class: "edit quarter_margin_right right" %>
<% end %>
<% end %>
This uses boolean short-circuiting - if current_user is nil then current_user.has_role? :admin is never evaluated.
If you are using Devise you can also use the user_signed_in? helper method.
Added.
If you find a yourself doing this often you can create a helper method:
# app/helpers/roles_helper.rb
module RolesHelper
def has_role?(role)
current_user && current_user.has_role?(role)
end
end
You can then simplefy your view:
<% if has_role?(:admin) %>
<%= link_to "Eliminar", user_photo_pin_path(user_id: #user.id, photo_id: #photo.id, id: pin.id) , method: :delete, data: { confirm: 'Quieres borrar esto?'}, class: "delete right" %>
<%= link_to 'Editar', edit_user_photo_pin_path(user_id: #user.id, photo_id: #photo.id, id: pin.id), :class=> "link", class: "edit quarter_margin_right right" %>
<% end %>
Note that we are calling has_role? on the view context.
For Devise to authenticate and set the current_user, you can include the following before_action in your controller:
before_action :authenticate_user!
If you only want to authenticate user for a specific action:
before_action :authenticate_user!, only: [:new, :update]
If you want to authenticate the user for all your controllers you can just put it in your ApplicationController

undefined method `picture' for nil:NilClass using carrierwave

I am making a form in my rails application where people have the option of adding images and I am using 'carrierwave' but I am getting an undefined method error on the edit page. Here is the code for the form:
<%= title "Add Item to #{#todo_list.title}" %>
<%= form_for [#todo_list, #todo_item], builder: FoundationFormBuilder do |form| %>
<%= render partial: 'form', locals: { form: form } %>
<%= form.file_field :picture %>
<% end %>
Here I can see the upload button and it is working fine but on the edit page I get the above stated error. Code for my edit page:
<%= title "Editing Todo Item" %>
<%= form_for [#todo_list, #todo_item], builder: FoundationFormBuilder do |form| %>
<%= render partial: 'form', locals: { form: form } %>
<% end %>
<div class="row">
<div class="small-12 columns">
<%= link_to "Delete", todo_list_todo_item_path(#todo_list, #todo_item), method: :delete, data: { confirm: "Are you sure?" }, class: "button radius expand alert" %>
</div>
<%= #todo_item.picture %>
</div>
Why is this showing an undefined method error. I tried creating a method in my todo_item model but its still showing the above error.
Controller for todo_item:
class TodoItemsController < ApplicationController
before_action :require_user
before_action :find_todo_list
before_action :set_back_link, except: [:index]
def index
go_back_link_to todo_lists_path
end
def new
#todo_item = #todo_list.todo_items.new
end
def create
#todo_item = #todo_list.todo_items.new(todo_item_params)
if #todo_item.save
flash[:success] = "Added todo list item."
redirect_to todo_list_todo_items_path
else
flash[:error] = "There was a problem adding that todo list item."
render action: :new
end
end
def edit
#todo_item = #todo_list.todo_items.find(params[:id])
end
def update
#todo_item = #todo_list.todo_items.find(params[:id])
if #todo_item.update_attributes(todo_item_params)
flash[:success] = "Saved todo list item."
redirect_to todo_list_todo_items_path
else
flash[:error] = "That todo item could not be saved."
render action: :edit
end
end
def destroy
#todo_item = #todo_list.todo_items.find(params[:id])
if #todo_item.destroy
flash[:success] = "Todo list item was deleted."
else
flash[:error] = "Todo list item could not be deleted."
end
redirect_to todo_list_todo_items_path
end
def complete
#todo_item = #todo_list.todo_items.find(params[:id])
#todo_item.toggle_completion!
redirect_to todo_list_todo_items_path, notice: "Todo item updated."
end
def url_options
{ todo_list_id: params[:todo_list_id] }.merge(super)
end
private
def set_back_link
go_back_link_to todo_list_todo_items_path(#todo_list)
end
def find_todo_list
#todo_list = current_user.todo_lists.find(params[:todo_list_id])
end
def todo_item_params
params[:todo_item].permit(:content)
end
end
To display your image you should change
<%= #todo_item.picture %>
to
<%= image_tag(#todo_item.picture_url) %>

Resources