In my app, I can call a view ('edit.html.erb') using:
<%= link_to( tag.content, [object, tag], method: :patch) %>
What would be the equivalent if I would want to render it within an other view ?
How would I pass it the params?
Edit
In effect I have a view of the parent, in which I want to list the children (editable). These I could edit separately too. Simple solution is that I, create a _partial which I render from the parent's view as well as from edit.html.erb.
I can call this edit.html.erb (or update) using the link:
<%= link_to( tag.content, [object, tag], method: :patch) %>
This works. Now question is how to render that partial (let's call it _update.html.erb) using render? Still need to pass the parent (i.e. object) and child (tag) params. How do I do that using <% render partial "tags/update" PARAMS %>?
Edit for full example
What I am trying to achieve (in short) - allow editing of tags (child) in annotate view (parent).
For an existing record of model/ object "Annotation", I want to add tags for an attached PDF. I do this in a page/view named "Annotate", that I open from the Annotation edit view (using 'link_to'). This "Annotate" page has 2 columns (see screenshot below):
left pane: 2 sections - 1 to quickly add the tag(s) and - 2 to edit existing tags (listed in a table)
right pane: the PDF
Tags and Annotation have polymorphic relationship (set up as per this question)
_form.html.erb for Annotation
<div class="row">
<div class="col-md-6">
<%= simple_form_for #annotation, html: { class: 'form-horizontal', multipart: true },
wrapper: :horizontal_form,
wrapper_mappings: {
check_boxes: :horizontal_radio_and_checkboxes,
radio_buttons: :horizontal_radio_and_checkboxes,
file: :horizontal_file_input,
boolean: :horizontal_boolean
} do |f| %>
<div class="btn-toolbar btn-group", role="toolbar">
<%= f.button :submit, 'Save', :class => "btn btn-xs btn-default" %> <%= link_to 'List' , annotations_path, :class => "btn btn-xs btn-default" %> <% unless #annotation.file.blank? %>
<%= link_to 'Annotate', annotate_path(#annotation), :class => "btn btn-xs btn-default" %>
<% end -%>
</div>
<h4>Annotation</h4>
<%= f.error_notification %>
<% if #annotation.file.blank? %>
<%= f.input :file, as: :file, input_html: { accept: ('application/pdf') } %>
<% else %>
<% end -%>
<%= f.input :name, placeholder: 'Enter name' %>
<%= f.input :description, placeholder: 'Description', :input_html => { :rows => 3 } %>
<%= f.association :documenttype, :collection => Documenttype.active.order(:name), prompt: 'Select document type' %>
<%= f.association :sender, label: 'Submitted by' , prompt: 'Select sender' %>
<%= f.association :receiver, label: 'Specific to', prompt: 'Select recipient' %>
<%= f.input :active, as: :boolean %>
<% end -%>
</div>
<% unless #annotation.file.blank? %>
<div class="col-md-6">
<%= content_tag :iframe, nil, src: pdf_annotation_path(#annotation), width: "100%", height: "770px", frameBorder: "0" %>
</div>
<% end %>
</div>
<% unless #annotation.new_record? %>
<div class="row">
<hr>
<div class="col-md-6">
<%= render #annotation.comments %>
</div>
<div class="col-md-6">
<%= render 'comments/form', :object => #annotation %>
</div>
</div>
<% end -%>
Annotate view annotate.html.erb
<div class="row">
<div class="col-lg-5">
<div class="btn-toolbar btn-group" role="toolbar">
<%= link_to 'Close', annotation_path(#annotation), :class => "btn btn-xs btn-default" %> <%= link_to 'List' , annotations_path, :class => "btn btn-xs btn-default" %>
</div>
<h4>Annotate document</h4>
<div data-spy="affix">
<%= render 'tags/form', :object => #annotation %>
<br>
<div class="panel panel-default" id="annotationResults">
<%= render 'tags/tag_list', :object => #annotation %>
</div>
</div>
</div>
<div class="col-lg-7" id="file">
<%= content_tag :iframe, nil, src: pdf_annotation_path(#annotation), width: "100%", height: "875px", frameBorder: "0" %>
</div>
</div>
tag list _tag_list.html.erb
<table id="tags" class="table table-hover" style="background-color: white; word-wrap: break-word; font-size: 0.9em;" >
<thead>
<tr>
<th>Tagged content</th>
<th>as</th>
<th>in</th>
<th></th>
</tr>
</thead>
<tbody>
<% object.tags.each do |tag| %>
<% unless tag.content.blank? %>
<tr data-tag-id='<%= tag.id %>', class='show-tag'>
<td style="word-wrap: break-word;"><%= link_to( tag.content, [object, tag], method: :patch) %>
<td><%= tag.tagtype.name %></td>
<td><%= tag.tagtype.typeoftag %></td>
<td><%= link_to '', [object, tag], method: :delete, data: { confirm: 'Please confirm!' }, :class => "glyphicon glyphicon-trash" %></td>
</tr>
<tr data-tag-id='<%= tag.id %>', class='edit-tag'>
<td colspan="4"><%= render partial: 'shared/tester' %><%#= render partial: 'tags/update', object: #tag.tagable, tag: #tag %></td>
</tr>
<% end -%>
<% end -%>
</tbody>
</table>
tag update form _update.html.erb
<%= simple_form_for [object, tag], html: { class: 'form-horizontal', multipart: true },
wrapper: :horizontal_form,
wrapper_mappings: {
check_boxes: :horizontal_radio_and_checkboxes,
radio_buttons: :horizontal_radio_and_checkboxes,
boolean: :horizontal_boolean
} do |f| %>
<div class="btn-toolbar btn-group" role="toolbar">
<%= f.button :submit, 'Save', :class => "btn btn-xs btn-default" %>
</div>
<%= f.error_notification %>
tag id = <%= #tag.id %>
<%= f.input :content, placeholder: 'Tagged content'%>
<%= f.association :tagtype, prompt: 'Select tag type', :collection => Tagtype.active.order(:name).where(:documenttype => #tag.tagable.documenttype_id) %>
<%= f.input :location, prompt: 'add as: x1, y1, x2, y2' %>
<% end -%>
Routes
Rails.application.routes.draw do
root 'dashboard#index'
devise_for :users
concern :tagable do
resources :tags, only: [:new, :index, :create, :edit, :update]
end
resources :users, :documenttypes, :tagtypes, :business_partners
resources :tags, only: [:show, :edit, :destroy, :index]
resources :documents do
resources :comments
resources :tags, concerns: :tagable
get "pdf", on: :member
end
resources :annotations do
resources :comments
resources :tags
get "pdf", on: :member
end
get "annotations/:id/annotate" => "annotations#annotate", as: 'annotate'
Tags controller
class TagsController < ApplicationController
def index
#tags = Tag.all.order(:tagable)
end
def show
tagable = detect_tagable
#tag = tagable.tags.find(params[:id])
end
def edit
tagable = detect_tagable
#tag = tagable.tags.find(params[:id])
#tag.update(tag_params)
end
def create
tagable = detect_tagable
tagable.tags.create(tag_params)
redirect_to tagable_path(tagable)
end
def update
tagable = detect_tagable
#tag = tagable.tags.find(params[:id])
render 'edit'
# #tag.save
#tag.update(tag_params)
end
def destroy
tagable = detect_tagable
#tag = tagable.tags.find(params[:id])
#tag.destroy
redirect_to tagable_path(tagable)
end
private
def tagable_path(tagable)
case tagable
when Document
document_path(tagable)
when Annotation
annotate_path(tagable)
else
fail 'Unknown tagable'
end
end
def detect_tagable
if params[:annotation_id]
Annotation.find(params[:annotation_id])
elsif params[:document_id]
Document.find(params[:document_id])
else
fail 'Tagable not found'
end
end
def tag_params
params.require(:tag).permit(:content, :location, :tagtype_id, annotation_attributes: { annotation_ids:[] }, document_attributes: { document_ids:[] })
end
end
This is what I have made - if there is a better way in Rails (which I can image) more then willing to learn.
The approach taken here should be to use nested forms, accepts_nested_attributes_for with allow_destroy: true and reject_if for the validations.
to destroy nested records, use accepts_nested_attributes_for :tags, allow_destroy: true and ensure that (besides :id) also :_destroy
is added to strongparams
to use validations for nested fields, use
accepts_nested_attributes_for :tags, allow_destroy: true,
reject_if: :something_to_check
Related
I'm using Mailboxer as my messaging gem in my rails app.
So the site's about classifieds. Every classified has a contact seller link , which i have implemented to route to the new_message form and write the message to the recipient fine!
Now i want to add some info about the classified that the contact form is made for!
class MessagesController < ApplicationController
before_action :authenticate_user!
def new
#user = User.find_by(id: params[:recipient_id])
end
def name
first_name
end
def mailboxer_email(object)
email
end
def create
recipients = User.find_by(id: params[:recipient_id])
conversation = current_user.send_message(recipients, params[:message][:body], params[:message][:subject]).conversation
flash[:success] = "Message has been sent!"
redirect_to conversation_path(conversation)
end
end
This is the view where i call new_message_path and then retrieve the user_id to define the recipient.
<div class="center-div">
<div class="col-lg-12" id="image">
<% #classified.photos.each do |p| %>
<h3 class="dark-grey"><%= #classified.title %></h3>
<h5 class="dark-grey">Loved by: <%= #classified.favorited_by.count %> </h5>
<%= image_tag p.image %>
<%end%>
</div>
<%= link_to "", new_message_path(:recipient_id => #classified.user_id), :class => "glyphicon glyphicon-envelope" , :style => "color:#EFCE7B" %>
<%if current_user.favorite_classifieds.collect(&:classified_id).include?(#classified.id) %>
<%= link_to "", favorite_classified_path(#classified, type: "unfavorite") , :class => "glyphicon glyphicon-heart" , :style => "color:#FF0000", method: :put %>
<%else%>
<%= link_to "", favorite_classified_path(#classified, type: "favorite") , :class => "glyphicon glyphicon-heart" , :style => "color:#000000", method: :put %>
<%end%>
<%= link_to "", editlisting_path(#classified) , :class => "glyphicon glyphicon-flag" , :style => "color:#EB573B" %>
<ul>
<h3 class="dark-grey">Model</h3>
<li><%= #classified.model %></li>
<h3 class="dark-grey">Description</h3>
<li><%= #classified.description %></li>
<li><%= link_to "Back", '/' , :class => "link" %></li>
<li><%= link_to #classified.user.first_name, profile_path(#classified.user_id) %></li>
</ul>
</div>
and this is my message_new view where i want to have the data of the classified
<div class="center-div">
<div class="messages-box">
<% page_header "Αποστολή μηνύματος" %>
<%= form_tag messages_path, method: :post do %>
<div class="form-group">
<%= label_tag 'message[subject]', 'Subject' %>
<%= text_field_tag 'message[subject]', nil, class: 'form-control', required: true %>
</div>
<div class="form-group">
<%= label_tag 'message[body]', 'Message' %>
<%= text_area_tag 'message[body]', nil, cols: 3, class: 'form-control', required: true %>
</div>
<div class="form-group">
<%= hidden_field_tag(:recipient_id, "#{#user.id}") %>
</div>
<%= submit_tag 'Send', class: 'btn btn-primary' %>
<% end %>
</div>
</div>
also for any solution a good explanation would help me a lot.
Thank you.
I ended up using nested routes.
resources :classifieds do
put :favorite, on: :member
resources :messages do
end
end
this is my view
<div class="center-div">
<div class="col-lg-12" id="image">
<% #classified.photos.each do |p| %>
<h3 class="dark-grey"><%= #classified.title %></h3>
<h5 class="dark-grey">Loved by: <%= #classified.favorited_by.count %> </h5>
<%= image_tag p.image %>
<%end%>
</div>
<%= link_to "", new_classified_message_path(:recipient_id => #classified.user_id , :classified_id => #classified.id), :class => "glyphicon glyphicon-envelope" , :style => "color:#EFCE7B" %>
<%if current_user.favorite_classifieds.collect(&:classified_id).include?(#classified.id) %>
<%= link_to "", favorite_classified_path(#classified, type: "unfavorite") , :class => "glyphicon glyphicon-heart" , :style => "color:#FF0000", method: :put %>
<%else%>
<%= link_to "", favorite_classified_path(#classified, type: "favorite") , :class => "glyphicon glyphicon-heart" , :style => "color:#000000", method: :put %>
<%end%>
<%= link_to "", editlisting_path(#classified) , :class => "glyphicon glyphicon-flag" , :style => "color:#EB573B" %>
<ul>
<h3 class="dark-grey">Model</h3>
<li><%= #classified.model %></li>
<h3 class="dark-grey">Description</h3>
<li><%= #classified.description %></li>
<li><%= link_to "Back", '/' , :class => "link" %></li>
<li><%= link_to #classified.user.first_name, profile_path(#classified.user_id) %></li>
</ul>
</div>
Got the value from the controller
class MessagesController < ApplicationController
before_action :authenticate_user!
def new
#user = User.find_by(id: params[:recipient_id])
#classified = Classified.find_by(id: params[:classified_id])
end
def name
first_name
end
def mailboxer_email(object)
email
end
def create
classifieds = Classified.find_by(id: params[:classified_id])
recipients = User.find_by(id: params[:recipient_id])
conversation = current_user.send_message(recipients, params[:message][:body], params[:message][:subject]).conversation
flash[:success] = "Message has been sent!"
redirect_to conversation_path(conversation)
end
end
and last rendered it in my messages_new view
<div class="center-div">
<div class="messages-box">
<% page_header "Αποστολή μηνύματος" %>
<%= form_tag messages_path, method: :post do %>
<div class="form-group">
<%= label_tag 'message[subject]', 'Αποστολή μηνύματος για την αγγελία : ' %>
<%= label_tag(:classified_id, "#{#classified.title}") %> <!-- integrate this -->
<%= text_field_tag 'message[subject]', nil, class: 'form-control', required: true %> <!-- integrate here -->
</div>
<div class="form-group">
<%= label_tag 'message[body]', 'Μύνημα στον χρήστη : ' %>
<%= label_tag(:recipient_id, "#{#user.first_name}") %>
<%= text_area_tag 'message[body]', nil,rows: 15, cols: 3, class: 'form-control', required: true %> <!-- integrate here -->
</div>
<div class="form-group">
<%= hidden_field_tag(:recipient_id, "#{#user.id}") %>
</div>
<%= submit_tag 'Send', class: 'btn btn-primary' %>
<% end %>
</div>
</div>
I am trying to use the fields_for helper method on a project I am working on. The original form works and saves just fine. The new attributes do not save and I get a NoMethodError and a undefined method. What am I missing?!
Here is my listing model:
class Listing < ActiveRecord::Base
has_one :listing_commerical_attribute
accepts_nested_attributes_for :listing_commerical_attribute, :allow_destroy => true
Here is my listing_commercial_attribute model:
class ListingCommercialAttribute < ActiveRecord::Base
belongs_to :listing
accepts_nested_attributes_for :listing
end
Here is my controller:
def new
#listing.build_listing_commercial_attribute
respond_to do |format|
format.html # new.html.erb
format.json { render json: #listing }
end
end
private
def commercial_params
params.require(:commerical_listing_attribute)
.permit(:gas_pipe_size,
:amperage,
:basement_ceiling_height,
:ceiling_height,
:door_size,
:zoning,
:previous_use,
:community_board,
:delivery_date,
:key_money,
:security_deposit,
:price_per_sq_ft,
:did_size)
end
Here is my _form.html.erb:
<h2 class="text-center">Commercial</h2>
<%= f.fields_for :listing_commerical_attributes do |ff| %>
<div class="field">
<%= ff.label :gas_pipe_size, "Gas Pipe Size", class: "general-text-label" %>
<%= ff.number_field :gas_pipe_size, class: "general-text-field" %>
</div>
<div class="field">
<%= ff.label :amperage, "Amperage", class: "general-text-label" %>
<%= ff.number_field :amperage, class: "general-text-field" %>
</div>
<div class="field">
<%= ff.label :ceiling_height, "Ceiling Height", class: "general-text-label" %>
<%= ff.number_field :ceiling_height, class: "general-text-field" %>
</div>
<div class="field">
<%= ff.label :basement_ceiling_height, "Basement Ceiling Height", class: "general-text-label" %>
<%= ff.number_field :basement_ceiling_height, class: "general-text-field" %>
</div>
<div class="field">
<%= ff.label :door_size, "Door Size", class: "general-text-label" %>
<%= ff.number_field :door_size, class: "general-text-field" %>
</div>
<div class="field">
<%= ff.label :zoning, "Zoning", class: "general-text-label" %>
<%= ff.text_field :zoning, class: "general-text-field" %>
</div>
<div class="field">
<label for="tenant_improvements" class="general-text-label">Tenant Improvements <small>(If Applicable)</small></label>
<%= ff.text_area :tenant_improvements, :rows => "4", class: "general-text-area" %>
</div>
<div class="field">
<label for="previous_use" class="general-text-label">Previous Use <small>(If Applicable)</small></label>
<%= ff.text_area :previous_use, :rows => "4", class: "general-text-area" %>
</div>
<div class= "field">
<%= ff.label :community_board, "Community Board", class: "general-text-label" %>
<%= ff.text_field :community_board, class: "general-text-field" %>
</div>
<div class="field">
<%= ff.label :delivery_date, "Delivery Date", class: "general-text-label" %>
<div class="input-group">
<span class="input-group-addon"><i class="nklyn-icon-calendar"></i></span>
<%= ff.text_field :delivery_date, :class => "datepicker general-text-field" %>
</div>
<div class="field">
<%= ff.label :key_money, "Key Money", class: "general-text-label" %>
<div class="input-group">
<span class="input-group-addon"><i class="nklyn-icon-money-bills"></i></span>
<%= f.text_field :key_money, class: "general-text-field", value: number_with_precision(f.object.price, delimiter: ',', precision: 0) %>
</div>
</div>
<div class="field">
<%= ff.label :security_deposit, "Security Deposit", class: "general-text-label" %>
<div class="input-group">
<span class="input-group-addon"><i class="nklyn-icon-money-bills"></i></span>
<%= f.text_field :security_deposit, class: "general-text-field", value: number_with_precision(f.object.price, delimiter: ',', precision: 0) %>
</div>
</div>
<div class="field">
<%= ff.label :price_per_sq_ft, "Price Per Sq Ft", class: "general-text-label" %>
<div class="input-group">
<span class="input-group-addon"><i class="nklyn-icon-money-bills"></i></span>
<%= f.text_field :price_per_sq_ft, class: "general-text-field", value: number_with_precision(f.object.price, delimiter: ',', precision: 0) %>
</div>
</div>
<div class="field">
<%= ff.label :did_size, "Drive In Doors Size", class: "general-text-label" %>
<%= ff.number_field :did_size, class: "general-text-field" %>
</div>
<% end %>
Update
I made the change to the ListingCommercialAttribute model and removed the accepts nested attributes for.
I changed the f.fields_for to singular instead of plural.
I added in the nested attributes after the parent (see below)
def listing_params
params.require(:listing)
.permit(:access,
:address,
:apartment,
:cats_ok,
:cross_streets,
:dogs_ok,
:latitude,
:longitude,
:amenities,
:date_available,
:bathrooms,
:bedrooms,
:description,
:fee,
:exclusive,
:featured,
:rental,
:residential,
:landlord_contact,
:listing_agent_id,
:sales_agent_id,
:neighborhood_id,
:pets,
:photo,
:photo_tag,
:primaryphoto,
:price,
:square_feet,
:station,
:status,
:subway_line,
:term,
:title,
:utilities,
:move_in_cost,
:owner_pays,
:private,
:office_id,
:full_address,
:zip,
:convertible,
:landlord_llc,
:pinned,
:image,
listing_commercial_attribute_attributes: [
:gas_pipe_size,
:amperage,
:basement_ceiling_height,
:ceiling_height,
:door_size,
:zoning,
:previous_use,
:community_board,
:delivery_date,
:key_money,
:security_deposit,
:price_per_sq_ft,
:did_size])
end
Here are my new controller actions:
def edit
#listing.attributes = listing_params
end
def create
#listing.attributes = listing_params
respond_to do |format|
if #listing.save
format.html { redirect_to #listing, notice: 'Listing was successfully created.' }
format.json { render json: #listing, status: :created, location: #listing }
else
format.html { render action: "new", notice: "Correct the mistakes below to create the new listing" }
format.json { render json: #listing.errors, status: :unprocessable_entity }
end
end
end
But now I am getting a NoMethodError in Listings#show error. I created a partial for the commercial attributes. Shouldn't they be included now that they are in the strong params, or am I totally misunderstanding that?!
Here is the partial:
Gas Pipe Size: <%= listing_commercial_attributes.gas_pipe_size(#listing) %>
Amperage: <%= listing_commercial_attribute.amperage(#listing) %>
Basement Ceiling Height: <%= listing_commercial_attribute.basement_celing_height(#listing) %>
Ceiling Height: <%= listing_commercial_attribute.ceiling_height(#listing) %>
Door Size: <%= listing_commercial_attribute.door_size(#listing) %>
Zoning: <%= listing_commercial_attribute.zoning(#listing) %>
Build to Suit: <%= listing_commercial_attribute.build_to_suit(#listing) %>
Previous Use: <%= listing_commercial_attribute.previous_use(#listing) %>
Community Board: <%= listing_commercial_attribute.community_board(#listing) %>
Delivery Date: <%= listing_commercial_attribute.delivery_date(#listing) %>
Key Money: <%= listing_commercial_attribute.key_money(#listing) %>
Update #2
I changed it to singular.
Here is the complete error.
NameError in Listings#show
Showing /Users/Code/app/views/listings/_commercial_attributes.html.erb where line #1 raised:
undefined local variable or method `listing_commercial_attribute' for #<#:0x007f86606f6a10>
Did you mean? listing_collection_url
Gas Pipe Size: <%= listing_commercial_attribute.gas_pipe_size(#listing) %>
Amperage: <%= listing_commercial_attribute.amperage(#listing) %>
Basement Ceiling Height: <%= listing_commercial_attribute.basement_celing_height(#listing) %>
Ceiling Height: <%= listing_commercial_attribute.ceiling_height(#listing) %>
Door Size: <%= listing_commercial_attribute.door_size(#listing) %>
Zoning: <%= listing_commercial_attribute.zoning(#listing) %>
Trace of template inclusion: app/views/listings/_listing_content_area.html.erb, app/views/listings/show.html.erb
Update #3
def show
#my_listing_collections = ListingCollection.with_agent(current_agent).order("created_at DESC")
#listing_commercial_attributes = ListingCommercialAttribute.find(params[:id])
#regions = Region.order(name: :asc)
#listing = Listing.includes(:photos, :likes, :interested_agents).find(params[:id])
if #listing.private && cannot?(:create, Listing)
redirect_to listings_path, notice: 'This listing is no longer available'
else
agent = Agent.where(id: params[:agent_id]).first
#page = Listings::ShowView.new(#listing, agent)
respond_to do |format|
format.html
end
end
end
I keep getting this error:
ActiveRecord::RecordNotFound in ListingsController#show
Couldn't find ListingCommercialAttribute with 'id'=5755
It is searching for the commercial attribute with an id of 5755, but that is the listing id. I'm not sure what to pass in there...
Do not define accepts_nested_attributes_for on both models. Only on the parent model. Otherwise you'll run into circular dependency issues. In this case the parent model looks like it's a Listing, so remove accepts_nested_attributes_for :listing from ListingCommercialAttribute.
The first argument to f.fields_for should be the name of the association and yours is slightly off. You have has_one : listing_commerical_attribute so you want f.fields_for : listing_commerical_attribute.
The Strong Parameters should require your parent object first and include nested objects second. Also, you must append _attributes to the end of your nested attribute name.
So, for 3:
def listing_params
params.require(:listing)
.permit(:id,
# ...
listing_commercial_attribute_attributes: [ # Note: _attributes
:gas_pipe_size,
# ...
])
end
In the create/edit actions, be sure to set the params from the strong parameters method: #listing.attributes = listing_params.
Read more in the docs on accepts_nested_attributes_for and Strong Parameters.
I have an app with SalesOpportunities - beneath these there are SaleQualifiers (which belong_to SalesOpportunity) which fit into 4 stages ("Need", "Budget", "Fit", "Negotiation"). I'm displaying these 4 stages as tabs on the SalesOpportunity show page, and when the user clicks a tab it will call a custom controller action to select the relevant SaleQualifiers to display in the pane beneath. So far this works well, but I'm trying to ensure the tab updates with the selected stage as the "active" tab - and this isn't working. The pane holds the right info, but the tabs are not correct:
SalesOpportunity show Controller action:
def show
#sales_opportunity = SalesOpportunity.find(params[:id])
session[:sales_opportunity_id] = #sales_opportunity.id
#set the SaleQualifier stages to show on the tabs
#stages = Question.order(:id).pluck(:sale_stage).uniq
#stage = #sales_opportunity.sale_status
#questions = Question.where(sale_stage: #stage)
#find any answered sale_qualifiers in this sale_stage
#sale_qualifiers = SaleQualifier.find_by_sql(["SELECT sale_qualifiers.* FROM sale_qualifiers INNER JOIN questions ON questions.id = sale_qualifiers.question_id WHERE sales_opportunity_id = ? AND questions.sale_stage = ? ", #sales_opportunity.id, #stage])
#find some unanswered sale_qualifiers and build them
#sale_qualifier = SaleQualifier.new(sales_opportunity_id: params[#sales_opportunity.id])
#answer = #sale_qualifier.build_answer
#find the right question to render and link to the sale_qualifier
#question = Question.find(#sales_opportunity.next_question)
end
This action just ensures the right pane is set on first loading the SalesOpportunity show page.
Here's the custom action to find the right SaleQualifiers according to their actions:
def prospect_qualifiers
#sales_opportunity = SalesOpportunity.find_by(id: session[:sales_opportunity_id])
#stage = params[:sale_stage]
#questions = Question.where(sale_stage: #stage)
#stages = Question.order(:id).pluck(:sale_stage).uniq
#sale_qualifiers = SaleQualifier.find_by_sql(["SELECT sale_qualifiers.* FROM sale_qualifiers INNER JOIN questions ON questions.id = sale_qualifiers.question_id WHERE has_answer = 'true' AND sales_opportunity_id = ? AND questions.sale_stage = ? ", #sales_opportunity.id, #stage])
#when there's no sale_qualifiers from this sale_stage we can just select the first one according to the question and build that
if #sale_qualifiers.blank?
#question = Question.order(:id).where(sale_stage: #stage).first
#sale_qualifier = SaleQualifier.new(sales_opportunity_id: params[#sales_opportunity.id], question_id: #question.id)
#answer = #sale_qualifier.build_answer
render :modal_form
else
#when some of the sale_qualifiers of this sale_stage exist we'll need to be more specific about which question is next
#sale_qualifier = SaleQualifier.new(sales_opportunity_id: params[#sales_opportunity.id])
#answer = #sale_qualifier.build_answer
#find the right question to render and link to the sale_qualifier
#question = Question.find(#sales_opportunity.next_question)
render :modal_form
end
end
This takes the sale_stage as a parameter passed from the link to this action, and uses that to find the right SaleQualifiers.
The offending part of my view code is below:
<div id="qualifier-tabs">
<ul class="nav nav-tabs">
<%= render partial: 'shared/tabs', collection: #stages, as: :stage %>
</ul>
</div>
<div class="tab-content", id="qualifier_panel">
<%= render partial: 'shared/panel', collection: #stages, as: :stage %>
</div>
</div>
shared/tabs partial:
<% if stage == #stage %>
<li class="active"><%= link_to stage, prospect_qualifiers_sale_qualifiers_path(:sale_stage => stage), :remote => true, data: { disable_with: "Loading...", toggle: 'tab' } %></li>
<% else %>
<li><%= link_to stage, prospect_qualifiers_sale_qualifiers_path(:sale_stage => stage), :remote => true, data: { disable_with: "Loading...", toggle: 'tab' } %></li>
<% end %>
As you can see this compares the collection: #stages as: stage to the #stage variable passed from the controller action, determining whether they match - if so we make that the active tab.
The shared/panel partial:
<% if stage == #stage %>
<div class="tab-pane active" id='<%=stage %>'>
<table class="table table-striped display responsive no-wrap">
<thead>
<tr>
<th>Question</th>
<th>Answer</th>
<th>Action</th>
</tr>
</thead>
<tbody>
<!-- if a sale_qualifier exists with an answer show the following -->
<% #sale_qualifiers.each do |qual| %>
<tr>
<td><%= qual.question.question_text %></td>
<% if qual.question.answer_type == "Datetime"%>
<td><%= qual.answer.answer_text.to_date.readable_inspect %></td>
<% elsif qual.question.answer_type == "Boolean" %>
<% if qual.answer.answer_text == 'true'%>
<td>Yes</td>
<% elsif qual.answer.answer_text == 'false' %>
<td>No</td>
<% end %>
<% else %>
<td><%= qual.answer.answer_text %></td>
<% end %>
<td><%= link_to('edit', edit_sale_qualifier_path(qual), class: "btn btn-sm btn-warning", data: { disable_with: "Loading...", dismiss: 'modal' }, :remote => true )%></td>
</tr>
<% end %>
<!-- if there's no sale_qualifier with the question id then build one -->
<%= form_tag('/sale_qualifiers', :id => 'new_sale_qualifier', :class => 'form', method: :post, remote: true, data: { model: "sale_qualifier" }) do -%>
<%= fields_for :sale_qualifier do |ff| %>
<%= ff.hidden_field :sales_opportunity_id, :value => #sales_opportunity.id %>
<%= ff.hidden_field :question_id, :value => #question.id %>
<tr>
<td><%= #question.question_text %></td>
<td>
<%= ff.fields_for :answer_attributes do |answer| %>
<div class="form-group">
<% if #question.answer_type == 'Text Field' %>
<%= answer.text_area :answer_text, :placeholder => "Enter your answer"%>
<% end %>
<% if #question.answer_type == 'Datetime' %>
<div class='input-group date' id='datetimepicker' data-date-format="YY.MM.DD">
<%= answer.text_field :answer_text, class: "form-control", data: { date_format: 'YYYY/MM/DD' }, :placeholder => "YYYY/MM/DD" %>
<span class="input-group-addon">
<span class="glyphicon glyphicon-calendar"></span>
</span>
</div>
<% end %>
<% if #question.answer_type == 'Boolean' %>
<%= answer.select :answer_text, [['Yes', true], ['No', false]] %>
<% end %>
<span class="warning-block"></span>
<span class="help-block"></span>
</div>
<% end %>
</td>
<td>
<%= submit_tag "Submit", class: "btn btn-large btn-success", id: "sale_qualifier_submit", data: { disable_with: "Submitting..." }, autocomplete: 'off' %>
</td>
</tr>
<% end %>
<% end %>
</tbody>
</table>
</div>
<% else %>
<div class="tab-pane" id='<%=stage %>'>
</div>
<% end %>
The pane does the same equivalence test and decides how to show the SaleQualifiers.
Here's the modal_form code:
$('#qualifier_tabs').html("<%= j render :partial => 'shared/tabs', collection: #stages, as: :stage %>");
$('#qualifier_panel').html("<%= j render :partial => 'shared/panel', collection: #stages, as: :stage %>");
The problem is that the pane shows the right SaleQualifiers but the active tab remains on "Need". If I put in <%= #stage %> and <%= stage %> outputs into the tab partial and also into the panel partial I get the reason - the tab panel always shows "Need" "Need" as the output from those erb snippets, whereas the pane panel shows whatever sale_stage is actually coming from my prospect_qualifiers action.
How can the prospect_qualifiers action (which calls the modal_form file and therefore renders both partials into the view) be passing different values for #stage and stage to these two partials when they're being rendered by the same controller action?
Edit - adding the show view
Per Chloe's request - here's the show view (or the relevant part of it) from the SalesOpportunity show page:
<div class="panel panel-default">
<div id="qualifier-tabs">
<ul class="nav nav-tabs">
<%= render partial: 'shared/tabs', collection: #stages, as: :stage %>
</ul>
</div>
<div class="tab-content", id="qualifier_panel">
<%= render partial: 'shared/panel', collection: #stages, as: :stage %>
</div>
</div>
I see now that you have <div id="qualifier-tabs"> in your view, but $('#qualifier_tabs') in your JQuery.
I'm trying to build a small expense tracking app. Using the nested_form gem to add line items. There is an Expense model which accepts nested attributes. Items belong to expenses and there is a foreign key association. Here's the expense controller:
class ExpensesController < ApplicationController
def new
#expense = Expense.new
#item = #expense.items.build
end
def index
#expenses = Expense.all
##items = Item.where(:expense_id => #expense.id)
end
def show
#expense = Expense.find(params[:id])
#items = Item.where(:expense_id => #expense.id)
end
def create
#expense = Expense.new(expense_params)
respond_to do |format|
if #expense.save
format.html { redirect_to #expense, notice: 'Expense Report Submitted.' }
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
def edit
#expense = Expense.find(params[:id])
end
def update
#expense = Expense.find(params[:id])
if #expense.save(expense_params)
flash[:notice] = "Expense Report Updated"
redirect_to #expense
else
render 'edit'
end
end
def destroy
#expense = Expense.find(params[:id])
#expense.destroy
redirect_to 'root'
end
private
def expense_params
params.require(:expense).permit(:department_id, :expense_type_id, :notes, items_attributes: [:id, :description, :amount, :issue_date, :_destroy])
end
end
The form looks like:
<%= nested_form_for (#expense) do |f| %>
<% if #expense.errors.any? %>
<div id="error_explanation">
<h2><%= pluralize(#expense.errors.count, "error") %> prohibited
this expense from being saved:</h2>
<ul>
<% #expense.errors.full_messages.each do |msg| %>
<li><%= msg %></li>
<% end %>
</ul>
</div>
<% end %>
<div class"row">
<div class="col-md-8">
<div class="form-group">
<%= f.label :department_id %><br>
<%= f.collection_select(:department_id, Department.all, :id, :department_name, prompt: true, class: "dropdown-menu") %>
</div>
<div class="form-group">
<%= f.label :expense_type_id %><br>
<%= f.collection_select(:expense_type_id, ExpenseType.all, :id, :expense_name, prompt: true, class: "form-control") %>
</div>
<%= f.fields_for :items do |i| %>
<div class="form-group">
<%= i.label :description%>
<%= i.text_field :description, class: "form-control" %>
</div>
<div class="form-group">
<%= i.label :amount%>
<%= i.text_field :amount, class: "form-control" %>
</div>
<div class="form-group">
<%= i.label :issue_date%>
<%= i.date_select :issue_date, class: "form-control" %>
</div>
<%= i.link_to_remove "Remove", class: "btn btn-default" %>
<% end %>
<div><p><%= f.link_to_add "Add Expense", :items, class: "btn btn-default" %></p></div>
<div class="form-group">
<%= f.label :notes %>
<%= f.text_area :notes, class: "form-control" %>
</div>
<%= f.submit "Submit", class: "btn btn-primary" %>
<% end %>
</div>
</div>
When I hit submit, the expense and items are saved. I checked with Sqlite browser and the foreign key values are captured for each item.
The index file looks like:
<% #expenses.each do |expense| %>
<tr>
<td><%= expense.item.description %></td>
<td><%= number_to_currency(expense.item.amount) %></td>
<td><%= expense.item.issue_date %></td>
<% end %>
I tried the usual combinations (like expense.item.description) by passing through a block, but they aren't working. I would like to know how to display the expense and the associated items in the show and index pages.
Looking at your new action, in your models you would have
class Expense < ActiveRecord::Base
has_many :items
accepts_nested_attributes_for :items
end
So when you are doing expense.items in your view it gives you an active record relation(since expense has many items), try this:
<% #expenses.each do |expense| %>
<% expense.items.each do |item| %>
<tr>
<td><%= item.description %></td>
<td><%= number_to_currency(item.amount) %></td>
<td><%= item.issue_date %></td>
</tr>
<% end %>
<% end %>
my controller:
class SetupsController < ApplicationController
def new
#setup = Setup.new
#todays_rate = #setup.todays_rates.build
end
def create
#setup = Setup.new(params[:setup])
if #setup.save
redirect_to setup_path(#setup)
else
render 'new'
end
end
end
My view code: setup/new.html.erb
<%= form_for #setup, :html => {:multipart => true} do |f| %>
<% if #setup.errors.any? %>
<ul>
<% #setup.errors.full_messages.each do |error| %>
<li><strong><%= error %></strong></li>
<% end %>
</ul>
<% end %>
<h4><%= f.label :effective_from, class:'required' %>
<%= f.text_field :effective_from %></h4>
<h4><%= f.label :effective_to, class:'required' %>
<%= f.text_field :effective_to %></h4>
<%= f.fields_for(:todays_rate) do |i| %> ##**combining two model with one view**
<h1>Interest Rate</h1>
<table cellpadding = "5px" cellspacing = "5px" width = "100%" class="table condensed-table"><tr>
<h4><th>Days From</th>
<th>Days To</th>
<th>Rate</th>
<th>Senior increment</th>
<th>Super Senior increment</th>
<th>Widow increment</th></h4>
</tr>
<h4><td><%= i.text_field :days_from, :class => 'input-mini' %></td></h4>
<h4> <td><%= i.text_field :days_to, :class => 'input-mini' %></td></h4>
<h4><td><%= i.text_field :rate, :class => 'input-mini' %></td></h4>
<h4> <td><%= i.text_field :senior_increment, :class => 'input-mini' %></td></h4>
<h4> <td><%= i.text_field :super_senior_increment,class:"input-mini" %></td></h4>
<h4><td><%= i.text_field :widow_incrtement,class: "input-mini" %></td></h4>
</table>
<% end %>
<fieldset class="form-actions"> <%= f.submit "Create Customer", class: "btn btn-primary" %></field>
setup.rb mmodel:
class Setup < ActiveRecord::Base
has_many :todays_rates
accepts_nested_attributes_for :todays_rates
attr_accessible :effective_from, :effective_to, :todays_rate
end
i'm combining two model in one view but i'm getting the above error. i don't know where i missed the keyword_end.can any one help me
I think your problem is that you haven't closed the form, ie you need a <% end %> at the end of your template.
The error sort of tells you that, though the tIDENTIFIER stuff can throw one off the scent a bit.