rails 4.0 undefined method - ruby-on-rails

I have an Opportunity model that has Activity as a nested resource. on my opportunities/show page, I have a list of activities for that opportunity and a form to add new activities. When I click "add activity" I get:
undefined method `activities' for nil:NilClass
Here is the error source:
# POST /activities.json
def create
#activity = #opportunity.activities.new(activity_params)
if #activity.save
redirect_to #opportunity, notice: 'Activity has been added'
else
I defined my Opportunity model as having many Activities and that my Activities belongs to an Opportunity. Here are the relevant parts of my Activity controller:
def create
#activity = #opportunity.activities.new(activity_params)
if #activity.save
redirect_to #opportunity, notice: 'Activity has been added'
else
redirect_to #opportunity, alert: 'Unable to add Activty'
end
end
And here is my views/activities/new code
<%= form_for ([#opportunity, #opportunity.activities.new]) do |f| %>
<div class="field">
<%= f.label "Date Assigned" %> <br />
<%= f.text_field :date_assigned %>
</div>
<div class="field">
<%= f.label "Date Due" %> <br />
<%= f.text_field :date_due %>
</div>
<div class="field">
<%= f.label "Description" %> <br />
<%= f.text_field :description %>
</div>
<div class="field">
<%= f.label "Status" %> <br />
<%= f.text_field :status %>
</div>
<div class="actions">
<%= f.submit 'Add' %>
</div>
<% end %>
My routes:
resources :opportunities do
resources :activities
end
thank you!!

Your #opportunity is undefined(nil) in the block.
You must get #opportunity prior to building activities on it as :
#opportunity = Opportunity.find(params[:opportunity_id])
(Reason for :opportunity_id : Since this is ActivityController and your model is nested, by conventional nested RESTful resources (as specified in your routes), the parameter is automatically assigned as model_id => opportunity_id)
Changed code:
def create
#opportunity = Opportunity.find(params[:opportunity_id])
#activity = #opportunity.activities.new(activity_params)
if #activity.save
redirect_to #opportunity, notice: 'Activity has been added'
else
Also, it is recommend to use build instead of new while building object for relations.

Try using build instead of new.
#activities = #oportunities.activities.build(activity_params)
That should work
Edit:
You didn't find for #oportunities before the build :P
def create
#oportunities.find(params[:id])
#activity = #opportunity.activities.new(activity_params)
if #activity.save
redirect_to #opportunity, notice: 'Activity has been added'
else
redirect_to #opportunity, alert: 'Unable to add Activty'
end
end

Related

Cannot create an new interface when it belongs to project

I try to create a new interface object. After clicking create button, it still remains new.html.erb, it should go to project_interfaces_path(main page). Also, the data has not saved yet.
I have tried many ways such as change URL, but it does not work and it reports NoMethodError in InterfacesController#create
undefined method `interfaces' for nil:NilClass
The interface/new.html.erb
<div class="card-body">
<%= form_for #interface, url:project_interfaces_path,method: :post do |f| %>
<div class="form-group">
<%= f.label :name %>
<%= f.text_area :name,class: 'form-control'%>
</div>
<div class="form-group">
<%= f.label :desp %>
<%= f.text_field :desp,class:'form-control'%>
</div>
<div class="form-group">
<%= f.label :request_url %>
<%= f.text_field :request_url,class:'form-control'%>
</div>
<div class="form-group">
<%= f.label :request_eg %>
<%= f.text_field :request_eg,class:'form-control'%>
</div>
<div class="form-group">
<%= f.label :response_eg %>
<%= f.text_field :response_eg,class:'form-control'%>
</div>
<%=link_to project_interfaces_path do%>
<button type="button" class="btn btn-primary">返回列表</button>
<% end %>
<%=f.submit "创建",class: 'btn btn-primary' %>
<% end %>
The interface controller:
def new
#interface = Interface.new
end
def create
#interface = #project.interfaces.new(interface_params)
if #interface.save
redirect_to project_interfaces_path
else
render :new
end
end
private
def interface_params
params.require(:interface).permit(:id, :name, :desp,:request_url,:request_eg,:response_eg)
end
The interface belongs to project:
class Interface < ApplicationRecord
belongs_to :method_type
has_many :get_fields, dependent: :destroy
has_many :put_fields, dependent: :destroy
belongs_to :project
end
You're working with nested resources, it means you can't create an interface without project_id, since Interface belongs_to :project. How it should be:
def new
#project = Project.find(params[:project_id])
#interface = #project.interfaces.new
end
def create
#project = Project.find(params[:project_id])
#interface = #project.interfaces.build(interface_params)
if #interface.save
redirect_to project_interfaces_path(#project)
else
render :new
end
end
private
def interface_params
params.require(:interface).permit(:id, :name, :desp,:request_url,:request_eg,:response_eg)
end
And remove url and method options from form, it works automagically
<%= form_for #interface do |f| %>
Indeed, you are redirecting to new instead of project_interfaces_path:
def create
#interface = Interface.new(interface_params)
if #interface.save
#redirect_to new_project_interface_path(project) <- wrong path
redirect_to project_interfaces_path # Good path
else
render :new
end
end
Also, add a space between url: and project_interfaces_path in <%= form_for #interface, url:project_interfaces_path,method: :post do |f| %>.
UPDATE: It seems you are trying to save an Interface without associate a Project to it.
You need to retrieve a project and build the interface with it:
def new
project = Project.find(params[:id]) # Assuming you are sending it
#interface = project.interfaces.build
end
def create
project = Project.find(params[:id]) # Assuming you are sending it
#interface = project.interfaces.build(interface_params)
if #interface.save
redirect_to project_interfaces_path
else
render :new
end
end
Taking a look on your routes would help.

Ruby: Prefill New Model Form From Previous Page's Partial Form

Ruby on Rails, Simple Form.
I have a contact/Message partial on several pages (product#index#show, static ‘services’ page) — when someone fills out the partial form and submits, I want the Message#new page to load with partial data prefilling the larger contact form without creating a new message and sending the action mailer.
Other SO solutions are for multi-step forms, wizards, or passing data through URL.
Here's my code:
_Shortcontact (the partial)
<div class="short_contact_wrapper">
<h4 id="short_container_title">Contact Us</h4>
<div class="contact-container">
<%= simple_form_for(message) do |f| %>
<div class="form-row-2">
<%= f.input :contactfirstname, label: false, placeholder: "* First Name", presence: true, :input_html => { :class => 'input_string' } %>
<%= f.input :contactlastname, label: false, placeholder: "* Last Name" %>
</div>
<div class="form-row-2">
<%= f.input :email, label: false, placeholder:"* Email" %>
<%= f.input :phone, label: false, placeholder: "* Phone" %>
</div>
</div>
<div class="contact-container">
<%= f.input :product_app, label: "Product Application Category", collection: ["Drones", "5G Telecom", "Thin Battery", "Auto Warehouse", "Robotics", "Wearables", "Electric Vehicles", "Industrial Personal Computers (Military)", "Industrial Personal Computers(Medical)", "Consumer Electronics", "Uninterruptible Power System", "Internet of Things", "Power Bank", "Other" ] %>
<%= f.input :batteryapp, label: false, placeholder: "Please describe the product and how you\'ll use the lithium ion battery or cell." %>
<div class="bottom_container" style="display: flex; justify-content: center;">
<%= f.button :submit, "Submit", class: 'contactbutton' %>
</div>
<% end %>
</div>
</div>
In my Product controller #Edit and #Show:
before_action :build_message, only: [:index, :new, :show]
def index
#products = Product.all
end
def new
#product = Product.new
# build nested models
#product.product_specs.build
#product.specifications.build
end
def create
#product = Product.new(product_parameters)
#product.category_id = product_parameters[:category_id]
# setting product stuff for TESTING
#product.picture = "products/IPC_tablet.png"
#product.iconblack = "icons/products/icon_IPC_black.png"
if #product.save!
flash[:success] = "You have saved the #{#product.name} product"
redirect_to product_path(#product)
else
flash.now[:error] = "Product was not saved"
render "new"
end
end
def show
#product_specs = #product.product_specs
end
def edit
# build nested models
#product.product_specs.build
#product.specifications.build
end
def build_message
#message = Message.new
end
Message Controller
def new
#message = Message.new
#message.build_company
puts "#{params[:message].inspect}"
puts "are there parameters?"
puts "#{#message.inspect}"
end
def create
#message = Message.new(message_parameters)
respond_to do |format|
if #message.save!
#send through mailer
MessageMailer.send_message(#message).deliver_now
format.html {redirect_to message_path(#message)}
else
format.html {render action: 'new'}
format.json {render json: #message.errors, status: :unprocessable_entity }
end
end
end
**Edit: Example of where I call _shortform: Layout/products **
<%= render '/partials/head' %>
<%= render '/partials/header' %>
<%= render 'layouts/messages' %>
<div class="product_layout_container">
<div class="layout_sidebar">
<%= link_to products_path do %>
<h4>Products</h4>
<% end %>
<%= render 'products/productsidebar', locals: {products: #products, categories: #categories } %>
</div>
<div class="product_yield">
<%= yield %>
</div>
</div>
<div class="page_container bottom_container">
<% unless user_signed_in? %>
<%= render partial: 'messages/shortcontact', locals: { message: #message }%>
<% end %>
</div>
<%= render '/partials/footer' %>
<%= render '/partials/foot' %>
I need help figuring out how to structure my partial form, or how I can prefill the Message#new form.... Thanks!
Start by adding an additional route (POST /messages/new):
resources :messages do
post :new, on: :collection
end
This is somewhat optional. Its possible to just setup this up with just GET as well but the parameters will be sent in the URL.
You can then simply bind the parameters to the model instance via strong the strong parameters.
class MessagesController
# ...
def new
#message = Message.new
# We need a condition here to avoid a parameter missing error
#message.assign_attributes(message_parameters) if params[:message]
end
end
You also have to manually override the path of the form:
simple_form_for(message, url: new_message_path)

Creating new registration and new order at the same time

I'm trying to add an event registration to a current or new order. Question at the end of the post.
Event model: Contains the basic event information like title, date, description. This event model has many event options.
Event Option: Contains a description and a price. This event option has many registrations.
Registration: Allows the user to register and it takes the price from the event option price. This registration belong to an event option and to the order model.
Order: Calculates the total of the order based on the sum of all the registrations associated with it.
Creating a new registration:
In the event option show page, I have a form that creates a new registration using remote: true.
Here's the form:
<%= form_for(#registration, remote: true) do |f| %>
<div class="field">
<%= f.label :name %><br>
<%= f.text_field :name %>
</div>
<div class="field">
<%= f.label :lastname %><br>
<%= f.text_field :lastname %>
</div>
<div class="field">
<%= f.label :event_option_id %><br>
<%= f.text_field :event_option_id, value: #event_option.id %>
</div>
<div class="field">
<%= f.label :order_item_id %><br>
<%= f.text_field :order_item_id%>
</div>
<div class="field">
<%= f.label :price %><br>
<%= f.text_field :price, value: #event_option.price%>
</div>
<div class="actions">
<%= f.submit %>
</div>
<% end %>
When the form is submitted, it creates the event and also creates a new order if the order does not exist. To create the new order I have this helper method in the application controller:
def current_order
if !session[:order_id].nil?
Order.find(session[:order_id])
else
Order.new
end
end
Here's the create method in the registrations controller:
def create
#order = current_order
#registration = Registration.new(registration_params)
#order_id = current_order.id
respond_to do |format|
if #registration.save
format.html { redirect_to #registration, notice: 'Registration was successfully created.' }
format.json { render :show, status: :created, location: #registration }
format.js {}
#order.save
session[:order_id] = #order.id
else
format.html { render :new }
format.json { render json: #registration.errors, status: :unprocessable_entity }
end
end
end
The problem is that I'm not able add the registration to the order. I'm guessing that this is happening because the registration is created before the order. The last two line of the if #registration.save in the respond_to block are saving the order. How can I add the registration to the order? Can both, the new registration and new order be created at the same time?
A simple way to get around this is to assign the registration to the order before saving the order...
#order.registrations << #registration
#order.save
Alternatively you could create the association at the time you're creating the #registration record.
Instead of...
#registration = Registration.new(registration_params)
do....
#registration = #order.registrations.build(registration_params)

Rails form is not saving input to the database

I making a very simple RESTful app and when I input a string into the form field and submit it, the database hold NULL instead of the input string.
Here is my controller:
def create
#song = Song.create(params[:title])
flash[:success] = "You have successfully created a new project!"
redirect_to "/songs/#{#song.id}"
end
Here is my form in the new.html.erb file:
<%= form_for(#song) do |f| %>
<div class="field">
<%= f.label :title %><br>
<%= f.text_field :title %>
</div>
<div class="actions">
<%= f.submit %>
</div>
<br>
<% end %>
If you are using rails < 4 then you should have
def create
#song = Song.create(params[:song])
flash[:success] = "You have successfully created a new project!"
redirect_to #song
end
and if you are using rails > 4 then you should have
def create
#song = Song.create(song_params)
flash[:success] = "You have successfully created a new project!"
redirect_to #song
end
private
def song_params
params.require(:song).permit(:title)
end

Rails: Adding to model after user submits form

In my Rails 3.2 project, I have a form to create a new post in new.html.erb in app/views/posts/
<%= form_for(#post) do |post_form| %>
...
<div class="field">
<%= post_form.label :title %><br />
<%= post_form.text_field :title %>
</div>
<div class="field">
<%= post_form.label :content %><br />
<%= post_form.text_field :content %>
</div>
<div class="actions">
<%= post_form.submit %>
</div>
<% end %>
Then the create function in posts_controller.rb
def create
#post = Post.new(params[:post])
if #post.save
format.html { redirect_to #post }
else
format.html { render action: "new" }
end
end
When the user submits a post, the title and content of the post are added to the Post model. However, I also want to add to another field of that post. For the field random_hash (which the user doesn't get to specify), I want to make it a string of 8 lowercase letters, the first 2 of which are the first 2 letters of the title, and the last 6 are random lowercase letters. How can I do that?
def create
#post = Post.new(params[:post])
#post.random_hash = generate_random_hash(params[:post][:title])
if #post.save
format.html { redirect_to #post }
else
format.html { render action: "new" }
end
end
def generate_random_hash(title)
first_two_letters = title[0..1]
next_six_letters = (0...6).map{65.+(rand(25)).chr}.join
(first_two_letters + next_six_letters).downcase
end
Put that in your controller. You obviously have to have random_hash attribute for Post model to work.
I am using Kent Fredric's solution to generate six random letters.

Resources