Rails pass additional parameter to a model - ruby-on-rails

in a controller I have a create action
def create
params[:note]
#note = current_user.notes.new(params[:note])
if #note.save
respond_with #note, status: :created
else
respond_with #note.errors, status: :unprocessable_entity
end
end
I want to pass to the model another param called current_user, how to do that and how to retrive the passed param in a model method?

#note = Note.new(params[:note].merge(:user_id => current_user.id))
But perhaps this is not the best way how you do it, look at this: Adding variable to params in rails
If you want access to current_user in model, see: Rails 3 devise, current_user is not accessible in a Model ?

Typically you do that with a hidden_field.
So in your create view, you'd add current_user as a hidden field.
<%= form_for #note do |f| %>
# all your real fields
<%= f.hidden_field :current_user current_user %>
<% end %>
Then in the create controller params[:note][:current_user] would be defined, and added to your model, assuming your model has an attribute called 'current_user'

Related

Render a partial that uses helper methods to determine view

What Currently Works:
In my BookingsController#Index method, I have this
def index
#user = current_user
if #user.user_type == 'host'
#bookings = Booking.where(host_id:#user.id, status: "#{params[:status]}")
else #user.user_type == 'cleaner'
#bookings = Booking.where(cleaner_id:#user.id, status: "#{params[:status]}")
end
end
This sets the current user and determines which #bookings to display depending on the user_id and params.
In the views, in my index.html.erb file, I render bookings <%= render #bookings %> which i defined in the booking#index. I have a _booking.html.erb partial that gets rendered... Inside the _booking.html.erb partial, I am able to successfully return booking details , like this for instance,
<div id='booking-time'><b>Unit Name:</b> <%= booking.unit.name %></div>
will return the unit name. it recognizes the local variable booking and the fields that belong to it.
I'd Like to Refactor:
I want to refactor this and render a partial <%= render 'bookings/booking' %> in my index.html.erb file. This partial will render different helper methods that each route to different partials.
Currently my _booking.html.erb file has a bunch of conditional logic that is better suited for a helper... I've tried a bunch of different ways to accomplish this to no avail, usually due to undefined variables..what's an elegant way to accomplish this?
What I've Tried:
Here's a method from my bookings_helper.rb` file:
def incoming_to_cleaner_partial_path(bookings)
return unless booking.status=='pending' && current_user.user_type =='cleaner' && booking.requested_by_id != current_user.id
'bookings/booking_requests/incoming_to_cleaner'
end
I added logic to these methods to determine which partials to render.
in my modified _booking.html.erb file, I call these methods, I pass them a #bookings instance variable param.
<%= render incoming_to_cleaner_partial_path(#bookings) %>
I've also tried this without params.. Neither work..
lastly, I've tried calling a method determine_bookings_to_show inside the helper methods. This method contains the same logic from the controller...
def incoming_to_cleaner_partial_path
determine_bookings_to_show
return unless booking.status=='pending' && current_user.user_type =='cleaner' && booking.requested_by_id != current_user.id
'bookings/booking_requests/incoming_to_cleaner'
end
private
def determine_bookings_to_show
#user = current_user
if #user.user_type == 'host'
#bookings = Booking.where(host_id:#user.id, status: "#{params[:status]}")
else #user.user_type == 'cleaner'
#bookings = Booking.where(cleaner_id:#user.id, status: "#{params[:status]}")
end
end
My main issue is i'm having scope troubles with these partials...
.. in my index [I want to] render different partials [depending on attributes of the booking] ..
I have used render(locals:) for this in the past.
# index.html.erb
#bookings.each do |b|
render_booking(b)
end
# bookings_helper.rb
def render_booking(booking)
if booking.status == 'pending'
render partial: 'path/to/bookings/_pending', locals: { booking: booking }
elsif booking.foobar?
render partial: 'path/to/bookings/_foobar', locals: { booking: booking }
else
# etc.
end
end
Conditionals like this can get complicated, but until then, I think this is an appropriate, practical use of a helper method.
# bookings/_pending.html.erb
<h1>Pending Booking <%= booking.id %></h1>
You will likely need to pass other locals:. I'd avoid using any instance variables in your partials.

How can I modify a variable in the controller from the view (ruby on rails)

I am making a portfolio page in rails. On the front page I have an "About" section where I have a personal description of myself. I want to be able to change this dynamically (not hard-coded html).
I would like to have the description be a text variable or string that I can modify through a form in the view section.
Questions
1. How should I declare this variable in the controller?
2. How do I access and change it from the form in the view?
3. Is there a better solution to my problem?
The only way to do this is to send the updated values to your controller. You need to have a form on your portfolio page:
#config/routes.rb
resources :users do
resources :portfolios #-> url.com/users/:user_id/portfolios/:id
end
#app/controllers/portfolios_controller.rb
class PortfoliosController < ApplicationController
def show
#user = User.find params[:user_id]
end
end
#app/controllers/users_controller.rb
class UsersController < ApplicationController
def update
#user = User.find params[:id]
#user.update user_params
end
private
def user_params
params.require(:user).permit(:about)
end
end
#app/views/portfolios/show.html.erb
<%= form_for #user do |f| %>
<%= f.text_field :about %>
<%= f.submit %>
<% end %>
Without any more context, that's the best I can give.
You will need to connect a database to store and retrieve dynamic stuff. You can access a variable in views if you define it with # like;
#about= Me.last.about
where Me could be your model that the information is saved in and Me.last would be the instance of that model. You can update this information by updating the model like
Me.last.update_attributes :about=> params[:about]
where params[:about] would be the params from the field in the form.
I would recommend following a guide so you get a complete solution. Below I have the major steps a user would take to update some content. This code is not a complete solution.
Your form would submit the updated content to another controller action (through another route).
<%= form_for #user do |f| %>
In that controller, you would store the content (usually in the database).
def update
#user = User.find(params[:id])
#user.update(user_params)
redirect_to :back
end
The original controller action would have a variable that got the content from the place you stored it
def show
#user = User.find(params[:id])
end

Render simple_form from partial view on application.html.erb

I want to create a partial view for my registration form and add it to the application layout file because it will be shown in the navigation bar in a dropdown menu.
How can I create this form in a partial view using simple_form gem and render it on application.html.erb?
<%= simple_form_for(#user, url: account_register_path) do |f| %>
Considering that the code above is the way to create the form and I don't know where I should define #user to be used in the application layout nor if I really need it.
Can someone please clarify this?
Don't put it in a partial, just have the registration view on its own, called with render...
#app/views/layouts/application.html.erb
<%= render "accounts/new" %>
#app/views/accounts/new.html.erb
<%= simple_form_for :user, url: account_register_path do |f| %>
...
Whilst you can use a symbol to populate form_for, it won't include the attributes of the model, or the various hidden methods which give context (such as id etc).
If you wanted to populate the accounts#new view/action with a variable, you'll have to set it in your ApplicationController:
#app/controllers/application_controller.rb
class ApplicationController < ActionController::Base
before_action :set_user
private
def set_user
#user = User.new
end
end
This would allow you to use:
#app/views/accounts/new.html.erb
<%= simple_form_for #user, url: account_register_path do |f| %>
--
The best way to implement this would be with Ajax -- this way, the #user object is only created when you need it (resolves bloat).
We've done this before:
You'd just need the following:
#app/views/layouts/application.html.erb
<%= link_to "New User", account_register_path, remote: true %>
#app/controllers/accounts_controller.rb
class AccountsController < ApplicationController
respond_to :js, :html
def new
#user = User.new
respond_with #user
end
end
#app/views/accounts/new.js.erb
$("<%=j render 'accounts/new' %>").appendTo("body");
You have to define #user in the corresponding controller action (I would assume from your code, that controller is called account and action is register), it would be something like:
#user = User.new
In order to initialize instance being created/updated. Then, attributes used in all fields should match users database table.

URL parameter not preserved after validation fails and 'new' action is rendered

When I initially call the new function all variables load up correctly. The params[:code] is a URL param defined in routes. However, when validation fails on create and new is rendered, the #m variable is not loaded (this causes a nomethoderror when an attribute of #m is called in the 'new' template). So, the :code parameter is not obtained after rendering new. However can the :code parameter be preserved after validation fails?
class AffiliatesController < ApplicationController
def new
#m = Merchant.find_by_code(params[:code])
#affiliate = Affiliate.new
end
def create
a = Affiliate.new(params[:affiliate])
if a.save
redirect_to 'http://' + params[:ref]
else
render 'new'
end
end
end
Another way to preserve params[:code], aside from using the session, is to add a hidden field in the form.
<%= form_for #affiliate do |f| %>
<%= hidden_field_tag :code, #m.code %>
Then change the create action to
def create
#affiliate = Affiliate.new(params[:affiliate])
if #affiliate.save
redirect_to 'http://' + params[:ref]
else
#m = Merchant.find_by_code(params[:code])
render :new
end
end
before you call render you should fill in all the variables that will be used by the view.
So in your case you need to instantiate #m = ... before you render 'new' from within the create as well.
If you need an extra parameter to accomplish this (the param[:code] in your case) I would not recomment you to configure routes and pass this information over the URI, it's complicated.
Use the session instead, it's a lot easier!
For example:
in index (or wherever you have the merchant code available) add session[:merchant_code] = the_code.
in new change #m = Merchant.find_by_code(session[:merchant_code])
Cheers,

Rendering new form for a nested Ruby on Rails Resource

In my Ruby on Rails application, each group has_many :expenses. I have nested my routes, so expenses are entered only as child entities of their parent groups. Here's an excerpt from routes.rb.
resources :groups do
resources :expenses
end
I cannot figure out how to render the 'new' action in the case of an expense not saving when it is submitted through /groups/:group_id/expenses/new. In my expenses_controller.rb, here is how the create action is defined:
def create
#expense = Expense.new(params[:expense])
#expense.group_id = params[:group_id]
if #expense.save
redirect_to group_expense_path(#expense.group.id, #expense.id)
else
render 'new'
end
end
Everything works fine if I satisty expense validation and #expense.save winds up working. However, when it fails and the code tries to render 'new' I get:
undefined method `expenses_path' for #<#<Class:0x007fd408b1fd58>:0x007fd408f21ca8>
So, I am assuming I have something about my nested routing wrong. How do I return the user to the new form but still display to him/her through the flash[] params the errors with the data they originally attempted to submit?
The problem is that #group is not initialized
So in your controller just do
#expense = Expense.new(params[:expense])
#group = Group.find(params[:group_id])
#expense.group_id = #group.id
Looks like you need to explicitly specify the url for form_for in your view.
Something likeā€¦
<%= form_for #expense, :url => group_expenses_path(#group.id) do |f| %>
...
<% end %>
In your <%= form_for %> you have used #group for url, because expenses belongs_to groups. But inside your create action in the controller you have not defined what is #group, so first you should define it as:
#expense = Expense.new(params[:expense])
#group = Group.find(params[:group_id])
#expense.group_id = #group.id
Also I would suggest to use respond_to in your controller:
respond_to do |format|
if #expense.save
format.html { redirect_to group_expense_path(#group.id, #expense.id), :notice => "Any msg you want" }
else
format.html { render :action => "new" }
end
end
All of these are in your create action inside the controller.
Also for different rendering methods look up: http://guides.rubyonrails.org/layouts_and_rendering.html
Hope this helps!

Resources