Why isn't a variable recognised within my _show.html file? - html

So I have a calendar controller and a view called _show.html.erb (it's a partial which is loaded into a bigger view). I'm trying to use an attribute 'starts_at' which belongs to an 'event' model object, but my html highlights it saying "cannot find starts_at".
If I try and load my main page it gives the error:
NoMethodError in Pages#main
undefined method `each' for nil:NilClass
Here is the calendar controller with my show method:
class CalendarsController < ApplicationController
def new
#calendar = Calendar.new(calendar_params)
end
def create
#calendar = Calendar.new(calendar_params)
end
private
def calendar_params
params.require(:customer_id)
end
def show
#calendar = current_customer.calendar
#events = #calendar.events
end
end
My events controller:
class EventsController < ApplicationController
before_filter :logged_in?
def new
#event = Event.new
#calendar = current_customer.calendar
end
def create
#calendar = current_customer.calendar
#event = #calendar.events.build(event_params)
if #event.save
redirect_to '/main' #'/main/#{#calendar.id}'
else
redirect_to '/compose'
end
end
Here is the _show.html.erb snippet:
<td id = "mon900" class ="freeslot" width=75 valign=top>
<p class=MsoNormal style='margin-bottom:0cm;margin-bottom:.0001pt;line-height:
normal'> </p>
<% #events.each do |e|%>
<p><%= e.starts_at %></p> <!-- It wont pick up starts_at-->
<% end %>
</td>
Here is the code where I am rendering this partial. It is being rendered in main.html.erb which belongs to my Pages controller:
<%= render partial: '/calendars/show', locals: {} %>
Pages controller:
class PagesController < ApplicationController
#before_action :require_customer, only: [:main]
def home
end
def main
end
end
Just for clarity, here are my calendar and event models:
class Calendar < ActiveRecord::Base
belongs_to :customer
has_many :events
end
class Event < ActiveRecord::Base
belongs_to :calendar
end
The :starts_at attribute of an event is of datetime type. When I was learning how to do ruby on rails (admittedly not long ago) I was told this sort of ruby injection would work. What is the problem here? Thanks!

You'll need some validation that the user exists so something like this should work:
#controllers/pages
def main
#events = current_user ? current_user.calendar.events : []
end
Clarification; this is the above ternary operation written out:
if current_user
#events = current_user.calendar.events
else
#events = [] # an empty array, which will address "undefined method `each' for nil:NilClass"
end
This way if there is no user logged in it will return an empty array of "events". You could just as easily replace the array with a scope of actual events, such as:
#events = current_user ? current_user.calendar.events : Event.all

Related

Nested object's form input not displaying any errors despite of nested_object.errors returning a hash with errors?

I have a Plan model that has_many Versions. I'm nesting Version inputs inside a Plan form, and even though the validations seem to be working, the form inputs don't display any errors.
I don't know if this is relevant, but because the nested inputs are scattered around the form, I open the nested input blocks with simple_fields_for :versions_attributes[0] instead of simple_fields_for :versions because I want to be specific that all the inputs around the form correspond to the same object. Otherwise the hash would be built with a different key for each different block (eg: versions_attributes: { 0: {amount: value}, 1: {another_field: another_value} } instead of versions_attributes: { 0: {amount: value, another_field: another_value}}.
plan.rb:
class Plan < ApplicationRecord
has_many :versions
accepts_nested_attributes_for :versions
validates_associated :versions
end
version.rb
class Version < ApplicationRecord
belongs_to :plan
validates :amount, presence: true
end
plans_controller.rb
class PlansController < ApplicationController
def new
#plan = current_account.plans.build
#version = #plan.versions.build
end
def create
#plan = current_account.plans.build(plan_params)
if #plan.save
redirect_to plan_path(#plan)
else
#plan = #base_plan.plans[0]
render :new
end
end
def plan_params
params[:plan][:versions_attributes]["0"]&.merge!(
account_id: current_account.id,
)
params.require(:plan).permit(
:name,
:short_description,
versions_attributes: %i[id account_id amount],
)
end
end
form.html.erb:
<%= simple_form_for [#plan] do |f| %>
<%= f.input :name %>
<%= f.input :short_description %>
<%= f.simple_fields_for "versions_attributes[0]" do |v| %>
<%= v.input :amount %>
<% end %>
<% end %>
Problem:
#version.errors contains a hash with the object's validation errors. However, the related inputs don't render validation errors nor are the form-group-invalid has-error CSS classes added (provided by the simple_form gem).
My first guess is that it's got something to do with the create action. My second guess is that it's got something to do with the way I'm opening the nested input's blocks (described above).
Either way, I'm confused because #version.errors contains the nested object's errors.

Getting a button to add current user's id to a field

I'm trying to get a simple button press that will store current user's id into a field but getting an error that says
ActionController::ParameterMissing (param is missing or the value is empty: request):
Here's my code.
The button code
<%= form_for(request.accept, remote: true) do |f| %>
<%= f.submit "Accept", class: "btn btn-primary" %>
<% end %>
request_controller
def accept
#request.ssp_id = current_user.id
#request.save
flash[:success] = "The request have been accepted!"
end
Thanks in advance.
The ParameterMissing error is probably because you have specified to require request model in your parameters through strong_params.
Since you are trying to update an existing record with the current_user you don't need a form.
Update your accept action in the RequestsController:
def accept
#request = Request.find params[:id]
if #request.update_attribute(:ssp, current_user)
redirect_to requests_path
flash[:success] = "The request have been accepted!"
end
end
Request model
class Request < ApplicationRecord
belongs_to :ssp, class_name: "User"
end
And your routes:
resources :requests do
member do
get "accept"
end
end
<%= link_to 'Accept request', accept_request_path(request) %>
Also as a recommendation try to use a different name for your model since the word request is wide use in Rails. I don't know if this could be a problem latter on.

Nested Attributes With JSON Rails One to Many

I'm figuring a problem with a nested_attribute.
team.rb:
class Team < ApplicationRecord
has_many :players, dependent: :destroy
accepts_nested_attributes_for :players, allow_destroy: true
end
console output:
Processing by TeamsController#create as JSON
Parameters: {"team"=>{"id"=>nil, "name"=>"testes",
"players_attributes"=>[{"id"=>nil, "name"=>"dadada", "_destroy"=>false, "team_id"=>nil}]}}
Unpermitted parameter: id
So, i'm ignoring team_id in controller for create and sending it as null same to player_id. What rails is getting in controller after permit is:
team: {name:'testes team', players_attributes: [{ name: 'testes'}]}
In my opinion (prob my mistake) rails should feed this relation in exactly this way. I tested it removing the nested attribute id and team_id but doesn't works.
Rails return:
bodyText: "{"players.team":["must exist"]}
controller:
def create
#team = Team.create(team_params)
#team.players.each do |player|
player.team_id = 1
end
respond_to do |format|
if #team.save
format.html { redirect_to #team, notice: 'Team was successfully created.' }
format.json { render :show, status: :created, location: #team }
else
format.html { render :new }
format.json { render json: #team.errors, status: :unprocessable_entity }
end
end
end
def team_params
params.require(:team).permit(:name, players_attributes: [:name, :positions, :_destroy])
end
gambiarra:
#team.players.each do |player|
player.team_id = 1
end
If i do this to the nested attribute BEFORE save team it works, team 1 must exists for it to work. If i save only the team and after create the relation it DOESN'T works either, only if I set the "gambiarra" solution.
How to solve this relation? As mentioned, my controller is filtering for only attributes for nested data. If i submit with HTML, works fine, if i use a JSON as nested objects, it doesn't work unless i force the relation to find a team_id for my player before save and so on, rails will save and commit the right player as is expected to do without a team_id in my player.
The structure of the params you are sending is incorrect, rails expects something like this in order to work with nested attributes:
{
"computer": {
"speakers_attributes": {
"0": {
"power": "1"
}
}
}
}
Notice three things:
computer: null was removed; you don't need to specify computer attribute since its value will be set with the id of the new computer to be created.
"0": was added; because of the has_many :speakers associations, you can create more than one Speaker (you will use 1: { ... }, 2: { ... }, and so on).
speaker: was changed to speakers_attributes; that's the way rails recognizes nested attributes values.
Now that the parameters had been set correctly, you need to do two more things:
Confirm that your associations are set correctly
class Computer < ApplicationRecord
has_many :speakers, dependent: :destroy
accepts_nested_attributes_for :speakers, allow_destroy: true
end
class Speaker < ApplicationRecord
belongs_to :computer
end
Properly setup your controller
class ComputersController < ApplicationController
def new
#computer = Computer.new
#computer.speakers.build
end
def create
#computer = Computer.create(computer_params)
if #computer.save
# handle success
else
# handle error
end
end
# other actions
private
def computer_params
params.require(:computer).permit(speakers_attributes: [:power])
end
end
Here #computer.speakers.build is neessary only if you will be creating nested forms using form helpers.

how to find all the records in has and belong to many join table in rails

this is the show method in my controllers i want to find all the list related to this car, and the relation between them is has and belong to many
def show
#car = Car.find(params[:id])
end
If you have
class Car
has_and_belongs_to_many :lists
end
class List
has_and_belongs_to_many :cars
end
then you can just call
#car = Car.find(params[:id])
#lists = #car.lists
Update:
To create an unordered list of your lists you could do like this in your cars/show.html.erb:
<ul>
<% #car.lists.each do |list| %>
<li><%= link_to(list, list_path(list)) %></li>
<% end %>
</ul>
you can just do it like this,
def show
#car = Car.find(params[:id])
#lists = #car.lists
end
class SomeController < ApplicationController
def show
#car = Car.find(params[:id])
#lists = #car.lists
end
end

how to disable submit button after being used 1 time

What I am trying to do is the following.
A user.rb can answer.rb several application.rb's created by a company.rb. However the user can only answer once per unique application.
I've already disabled this in the model but can't figure out how to do it in the view.
My answer controller:
class AnswersController < ApplicationController
before_action :authenticate_user!
def show
#application = Application.find(params[:id])
#answer = Answer.new
end
def create
#answer = Answer.new(answer_params.merge(:user_id => current_user.id))
if #answer.save
flash[:notice] = "You've successfully applied"
redirect_to root_url
else
flash[:alert] = "You've already applied"
redirect_to root_url
end
end
private
def answer_params
params.require(:answer).permit(:answer_1, :answer_2, :answer_3, :application_id)
end
end
in the answer model I have a user_id that is stored.
Now my thinking is that we look at the current answer :id and check if current_user.id is present in it, if so we disable the button. But I haven't been able to do anything that turned out successfully.
The show.html.erb looks like this:
<%= form_for #answer do |f| %>
<%= f.hidden_field :application_id, value: #application.id %>
<p>Question 1: <%= #application.question_1 %></p>
<%= f.text_area :answer_1 %>
.......
<%= f.submit "Submit" %>
<% end %>
If use of jQuery is possible in Ruby, you can use the .one() method
.one( events [, selector ] [, data ], handler ) [jQuery 1.7+]
events
Type: String
One or more space-separated event types and optional namespaces, such as "click"
or "keydown.myPlugin".
selector
Type: String
A selector string to filter the descendants of the selected elements that trigger
the event. If the selector is null or omitted, the event is always triggered when
it reaches the selected element.
data
Type: Anything
Data to be passed to the handler in event.data when an event is triggered.
handler
Type: Function( Event eventObject )
A function to execute when the event is triggered. The value false is also allowed as
a shorthand for a function that simply does return false.