I'm trying to create a new location and a devise user in the same form and link them. The user and the location are created, but the location_id is not saving to the user. There is a location_id column in the user table.
My form
<% resource.build_location %>
<%= simple_form_for(resource, :as => resource_name, :url => registration_path(resource_name), :html => {:class => 'form-vertical' }) do |f| %>
<%= f.error_notification %>
<!-- capture location details hidden values -->
<%= f.fields_for :location do |location_form| %>
<%= location_form.text_field :name, :name => "name", :type => "hidden" %>
<%= location_form.text_field :street_address, :name => "formatted_address", :type => "hidden" %>
<%= location_form.text_field :lat, :name => "lat", :type => "hidden" %>
<%= location_form.text_field :long, :name => "lng", :type => "hidden" %>
<% end %>
<!-- devise user authenticate -->
<%= f.input :name, :autofocus => true %>
<%= f.input :email, :required => true %>
<%= f.input :password, :required => true %>
<%= f.input :password_confirmation, :required => true %>
<%= f.button :submit, 'Sign up', :class => 'btn-primary' %>
<% end %>
<%= render "devise/shared/links" %>
Location model
class Location < ActiveRecord::Base
has_many :users, :dependent => :destroy
accepts_nested_attributes_for :users, :allow_destroy => true
attr_accessible :lat, :long, :name, :street_address
attr_accessible :user_attributes
end
Location controller
def new
#location = Location.new
#location.user.build
respond_to do |format|
format.html # new.html.erb
format.json { render json: #location }
end
end
# GET /locations/1/edit
def edit
#location = Location.find(params[:id])
end
# POST /locations
# POST /locations.json
def create
#location = #user.location.build(params[:location])
respond_to do |format|
if #location.save
format.html { redirect_to #location, notice: 'Location was successfully created.' }
format.json { render json: #location, status: :created, location: #location }
else
format.html { render action: "new" }
format.json { render json: #location.errors, status: :unprocessable_entity }
end
end
end
User Model
class User < ActiveRecord::Base
belongs_to :location
accepts_nested_attributes_for :location
rolify
# Include default devise modules. Others available are:
# :token_authenticatable, :confirmable,
# :lockable, :timeoutable and :omniauthable
devise :database_authenticatable, :registerable,
:recoverable, :rememberable, :trackable, :validatable
# Setup accessible (or protected) attributes for your model
attr_accessible :role_ids, :as => :admin
attr_accessible :name, :email, :password, :password_confirmation, :remember_me, :location, :location_id, :location_attributes
end
User controller
class UsersController < ApplicationController
before_filter :authenticate_user!
def index
authorize! :index, #user, :message => 'Not authorized as an administrator.'
#users = User.all
end
def show
#user = User.find(params[:id])
end
def update
authorize! :update, #user, :message => 'Not authorized as an administrator.'
#user = User.find(params[:id])
if #user.update_attributes(params[:user], :as => :admin)
redirect_to users_path, :notice => "User updated."
else
redirect_to users_path, :alert => "Unable to update user."
end
end
def destroy
authorize! :destroy, #user, :message => 'Not authorized as an administrator.'
user = User.find(params[:id])
unless user == current_user
user.destroy
redirect_to users_path, :notice => "User deleted."
else
redirect_to users_path, :notice => "Can't delete yourself."
end
end
end
No errors when creating the user, just getting a location_id="nil" when creating the user. I can access the location and a location_id is created but not linked to the user. Any ideas on how to save the location_id to the user?
I am populating the location info with json returned from a google location api autocomplete and assigning to an element with name="". It seems that everything works fine when I manually enter location info, but fails when the fields are populated from the autocomplete.
The first thing to note is that you do not need accepts_nested_attributes_for on both models, only the one that has the has_many association. Also, it looks like the pluralization is wrong in the Location model for users attributes
class Location < ActiveRecord::Base
has_many :users, :dependent => :destroy
accepts_nested_attributes_for :users, :allow_destroy => true
attr_accessible :lat, :long, :name, :street_address
attr_accessible :users_attributes # <- This should be plural
end
Remove the accepts_nested_attributes_for in the user model
class User < ActiveRecord::Base
belongs_to :location
accepts_nested_attributes_for :location # <- This should be removed
rolify
# Include default devise modules. Others available are:
# :token_authenticatable, :confirmable,
# :lockable, :timeoutable and :omniauthable
devise :database_authenticatable, :registerable,
:recoverable, :rememberable, :trackable, :validatable
# Setup accessible (or protected) attributes for your model
attr_accessible :role_ids, :as => :admin
attr_accessible :name, :email, :password, :password_confirmation, :remember_me, :location, :location_id # Also remove location_atributes
end
Also, this line
#location = #user.location.build(params[:location])
Should be
#location = Location.new(params[:location)
since the way your models are setup now a location has a user so you do not need to build a location from a user. That being said, I would recommend you create the association in the opposite direction where a user has_many locations, but this of course may be contrary to your function so take it with a grain of salt :).
http://api.rubyonrails.org/classes/ActiveRecord/NestedAttributes/ClassMethods.html
Related
In my blog app, there are posts and each post can optionally have tags. When the user is on the new or edit post form, they currently have the means to select or deselect tags to associate with the post. There is a many-to-many relationship between the post and tag models and I am using bootstrap, bootstrap_form and bootstrap-select. This all works seemingly very well. The problem is, when the user is assigning tags to their post, these tags currently have to already exist (otherwise they would have to abort their post and go add tags...bad user experience). I am trying to devise a way to give the user the option to create new tags and/or select already-existing tags and apply them to the post, all on the same post form in the collection_select field, all at the same time.
Apparently I'm asking the wrong questions on google...wouldn't this be a common need, already solved?
I am asking for guidance in providing an 'add new tag' functionality to an otherwise functional collection_select field. How best to go about this?
Posts Controller:
class PostsController < ApplicationController
before_action :set_post, only: %i[edit update interim destroy]
# GET /posts
# GET /posts.json
def index
if user_signed_in? && current_user.admin_role
if params[:tag]
#posts = Post.tagged_with(params[:tag]).all.order('updated_at DESC').page params[:page]
else
#posts = Post.all.order('updated_at DESC').page params[:page]
end
else
if params[:tag]
#posts = Post.tagged_with(params[:tag]).where(published: true).order('updated_at DESC').page params[:page]
else
#posts = Post.where(published: true).order('updated_at DESC').page params[:page]
end
end
end
# GET /posts/new
def new
#post = current_user.posts.build
#categories = Category.pluck(:name, :id)
end
# GET /posts/1/edit
def edit
#categories = Category.pluck(:name, :id)
#cat = #post.category_id
end
# POST /posts
# POST /posts.json
def create
#post = current_user.posts.create(post_params)
respond_to do |format|
if #post.save
if params[:interim]
format.html { redirect_to edit_post_path(#post), notice: 'Post was successfully created.' }
format.json { redirect_to edit_post_path(#post), status: :created, location: #post, notice: 'Post was successfully created.' }
elsif params[:complete]
format.html { redirect_to #post, notice: 'Post was successfully created.' }
format.json { render :show, status: :created, location: #post }
end
else
format.html { render :new }
format.json { render json: #post.errors, status: :unprocessable_entity }
end
end
end
# PATCH/PUT /posts/1
# PATCH/PUT /posts/1.json
def update
respond_to do |format|
if #post.update(post_params)
if params[:interim]
format.html { redirect_to edit_post_path(#post), notice: 'Post was successfully updated.' }
format.json { redirect_to edit_post_path(#post), status: :ok, location: #post, notice: 'Post was successfully updated.' }
elsif params[:complete]
format.html { redirect_to #post, notice: 'Post was successfully updated.' }
format.json { render :show, status: :ok, location: #post }
end
else
format.html { render :edit }
format.json { render json: #post.errors, status: :unprocessable_entity }
end
end
end
# DELETE /posts/1
# DELETE /posts/1.json
def destroy
#post.destroy
respond_to do |format|
format.html { redirect_to posts_url, notice: 'Post was successfully destroyed.' }
format.json { head :no_content }
end
end
private
def set_post
#post = Post.find(params[:id])
end
def post_params
params.require(:post).permit(:title, :content, :user_id, :published, :category_id, :tag_list, :tag, { tag_ids: [] }, :tag_ids,
comment_attributes: [:id, :title, :user_id, :content, :post_id, :parent_id, :ancestry, :commentable, :commentable_id, :commentable_type])
end
end
No tags controller (not needed thus far)
Post Model:
class Post < ApplicationRecord
has_many :taggings
has_many :tags, through: :taggings
has_rich_text :content
include PgSearch::Model
multisearchable :against => [:title, :content]
def self.published(post)
post.published
end
def self.tagged_with(name)
Tag.find_by!(name: name).posts
end
def self.tag_counts
Tag.select('tags.*, count(taggings.tag_id) as count').joins(:taggings).group('taggings.tag_id')
end
def tag_list
tags.map(&:name).join(', ')
end
def tag_list=(names)
self.tags = names.split(',').map do |n|
Tag.where(name: n.strip).first_or_create!
end
end
end
Tag Model:
class Tag < ApplicationRecord
has_many :taggings
has_many :posts, through: :taggings
end
Tagging Model:
class Tagging < ApplicationRecord
belongs_to :tag
belongs_to :post
end
Post Form Partial (the actual new and edit views do nothing but render this form):
<%= bootstrap_form_for #post, local: true, html: { class: 'form-horizontal' } do |f| %>
<%= f.text_field :title %>
<%= f.rich_text_area :content, control_class: 'trix-content-edit' %>
<%= f.collection_select :category_id, Category.all, :id, :name %>
<%= f.form_group :published, label: { text: "Publication Status" } do %>
<%= f.radio_button :published, true, label: "Published" %>
<%= f.radio_button :published, false, label: "Draft" %>
<% end %>
<%= f.collection_select :tag_ids, Tag.order(:name), :id, :name, {label: 'Tags', include_blank: true}, {class: 'selectpicker show-tick', multiple: 'multiple', title: 'Make your selection...', 'data-live-search': 'true', 'data-actions-box': 'true'} %>
<br><br>
<%= f.submit "Save and Resume Editing", name: "interim", class: "btn btn-primary" %>
<%= f.submit "Save and Quit", name: "complete", class: "btn btn-primary" %>
<br><br>
<% end %>
No forms for tag at this time.
I am using the gem "acts_as_votable" in my Rails application so that Users can vote on Posts.
I'm adding Ajax functionality so that the entire page doesn't have to refresh when a user upvotes a post. This is what I have:
routes.rb
Rails.application.routes.draw do
devise_for :users, controllers: {registrations: 'registrations'}
resources :posts do
member do
put "like", to: "posts#upvote"
put "dislike", to: "posts#downvote"
end
end
# Define Root URL
root 'pages#index'
# Define Routes for Pages
get '/home' => 'pages#home'
get '/explore' => 'pages#explore'
get '/privacy' => 'pages#privacy'
get '/:id' => 'pages#profile'
end
pages_controller.rb
def explore
#posts = Post.where('created_at >= :one_days_ago', one_days_ago: Time.now - 16.hours)
end
posts_controller.rb
class PostsController < ApplicationController
def index
#posts = Post.allow
end
def new
#post = Post.new
end
def create
#post = Post.new(post_params)
#post.user_id = current_user.id
respond_to do |f|
if (#post.save)
f.html { redirect_to :back, :flash => { :notice => "Posted" } }
else
f.html { redirect_to :back, :flash => { :alert => "Error" } }
end
end
end
def destroy
#post = Post.find(params[:id])
if current_user == #post.user
#post.destroy
end
redirect_to :back, alert: 'Deleted.'
end
def upvote
#posts = Post.find(params[:id])
#posts.upvote_by current_user
respond_to do |format|
format.html { redirect_to :back }
format.js
end
end
def downvote
#posts = Post.find(params[:id])
#posts.downvote_by current_user
respond_to do |format|
format.html { redirect_to :back }
format.js
end
end
private
def post_params # allows certain data to be passed via form.
params.require(:post).permit(:user_id, :content)
end
end
explore.html.erb
<% if #posts.each do |p| %>
<div class="panel-body">
<p class="post-content"><%= auto_link(p.content, :html => { :target => '_blank' }) %></p>
<%= render :partial => 'vote', :class => "vote", :locals => { p: #posts } %>
</div>
<% end %>
_vote.html.erb
<div id="vote_<%= p.id %>" class="vote">
<%= link_to 'Vote Up', like_post_path(p), :class => "upvote", :method => :put, :remote => true %>
<span><%= p.score %></span> <!--show the current vote-->
<%= link_to 'Vote Down', dislike_post_path(p), :class => "downvote", :method => :put, :remote => true %>
</div>
upvote.js.erb
$(document).ready(function(){
$('#vote_<%= p.id %>').html("<%= escape_javascript render :partial => '/pages/vote' %>");
});
When I go to run the project I get this error:
NoMethodError in Pages#explore
Showing /home/ubuntu/workspace/app/views/pages/_vote.html.erb where line #1 raised:
undefined method `id' for #<Post::ActiveRecord_Relation:0x007f6715d018e8>
Did you mean? ids
<div id="vote_<%= p.id %>" class="vote">
The issue arises from trying to render _votes.html.erb.
Where am I going wrong here? I've been stuck on this for a while and would appreciate any help. Thanks!
You are passing entire collection to partial
<% if #posts.each do |p| %>
<div class="panel-body">
<p class="post-content"><%= auto_link(p.content, :html => { :target => '_blank' }) %></p>
<%= render :partial => 'vote', :class => "vote", :locals => { p: #posts } %>
</div>
<% end %>
In line <%= render :partial => 'vote', :class => "vote", :locals => { p: #posts } %> replace #posts with p
I am using ajax for creating post that belongs to a particular topic where I render the form in post index page. Each post can have many tags and I am also using devise authentication and CanCanCan authorization.
I need to capture the error message of the post submit and show it in the browser through this JavaScript template Create.js.erb with custom error messages instead of doing in post form.
Below is the code:
Post controller
class PostsController < ApplicationController
load_and_authorize_resource
before_action :set_post, only: [:show, :edit, :update, :destroy, :update_status]
skip_before_action :verify_authenticity_token
# GET /posts
# GET /posts.json
def index
if params[:topic_id].present?
#topic = Topic.find(params[:topic_id])
#posts = #topic.posts.paginate(page: params[:page], per_page: 10)
#post = #topic.posts.new
else
#posts = Post.eager_load(:topic, :user).paginate(page: params[:page], per_page: 10)
end
#tags =Tag.all
end
# GET /posts/1
# GET /posts/1.json
def show
#tags = #posts.tags
end
def update_status
current_user.posts<<(#posts)
end
# GET /posts/new
def new
#topic = Topic.find(params[:topic_id])
#posts = #topic.posts.new
#tags =Tag.all
end
# GET /posts/1/edit
def edit
#tags = #posts.tags
end
# POST /posts
# POST /posts.json
def create
#topic = Topic.find(params[:topic_id])
#posts = #topic.posts.create(post_params)
respond_to do |format|
#posts.user_id = current_user.id
if #posts.save
format.html { redirect_to topic_posts_path(#topic), notice: 'Post was successfully created.' }
format.js
format.json { render :show, status: :created, location: #posts }
else
format.html { render :new }
format.json { render json: #posts.errors, status: :unprocessable_entity }
end
end
end
# PATCH/PUT /posts/1
# PATCH/PUT /posts/1.json
def update
#tags = #posts.tags
respond_to do |format|
if params[:rate].to_i>0
#posts.ratings.create(:star => params[:rate])
format.html { redirect_to post_path(#posts), notice: 'Rating was successfully updated.' }
elsif #posts.update(post_params)
format.html { redirect_to post_path(#posts), notice: 'Post was successfully updated.' }
format.json { render :show, status: :ok, location: #posts }
else
format.html { render :edit }
format.json { render json: #posts.errors, status: :unprocessable_entity }
end
end
end
# DELETE /posts/1
# DELETE /posts/1.json
def destroy
#posts.destroy
respond_to do |format|
format.html { redirect_to topic_posts_url(#posts.topic_id), notice: 'Post was successfully destroyed.' }
format.json { head :no_content }
end
end
private
# Use callbacks to share common setup or constraints between actions.
def set_post
#posts = Post.find(params[:id])
end
# Never trust parameters from the scary internet, only allow the white list through.
def post_params
params.require(:post).permit(:image, :name, :message, :topic_id, {tag_ids:[]}, :rate, :user_id)
end
protected
def json_request?
request.format.json?
end
end
post form
<%= form_for [#topic, #post], remote: true do |f| %>
<% if #post.errors.any? %>
<div id="error_explanation">
<h2><%= pluralize(#post.errors.count, "error") %> prohibited this post from being saved:</h2>
<ul>
<% #post.errors.full_messages.each do |message| %>
<li><%= message %></li>
<% end %>
</ul>
</div>
<div class="field">
<%= f.label :Image %><br>
<%= f.file_field :image %>
</div>
<div class="field">
<%= f.label :Name %><br>
<%= f.text_field :name %>
</div>
<div class="field">
<%= f.label :Message %><br>
<%= f.text_area :message %>
</div>
<% if #tags %>
<% #tags.each do |tag| %>
<div>
<%= check_box_tag "post[tag_ids][]", tag.id, #post.tags.include?(tag) %>
<%= tag.name %>
</div>
<% end %>
<% end %>
<br><br>
<%= link_to 'Create Tag', tags_path %>
<br><br>
<%= f.submit %>
<% end %>
create.js.erb
$("#post_table").append("<%= j render #posts %>");
alert("Post created")
index.html.erb
<p id="notice"><%= notice %></p>
<div id='ajax_loader' style="position: absolute; left: 50%; top: 50%; display: none;">
<%= image_tag "ajax-loader.gif" %>
</div>
<script>
$(document).ajaxStop(function(){
$("#ajax_loader").hide();
});
$(document).ajaxStart(function(){
$("#ajax_loader").show();
});
</script>
<%= will_paginate %>
<h1>Listing Posts</h1>
<table id = "post_table">
<thead>
<tr>
<th><th>Name</th></th>
<th><th>Author</th></th>
<th><th>Message</th></th>
<th><th>Status</th></th>
<th colspan="4"></th>
</tr>
</thead>
<tbody>
<%= render #posts %>
</tbody>
</table>
<br>
<% if #topic %>
<%= link_to 'New Post', "#", id: "new_post" %>|
<section id = "new_post_section">
<%= render 'form' %>
</section>
<%= link_to 'Back to Topics', topic_path(#topic) %>
<% else %>
<% link_to 'New Post', new_post_path %>
<% end %>
<%= will_paginate %>
Post model
class Post < ActiveRecord::Base
belongs_to :user
belongs_to :topic
has_many :comments
has_and_belongs_to_many :tags
has_many :ratings
validates_presence_of :name, :presence => true
validates_length_of :name, maximum: 5
has_attached_file :image
#validates_attachment_presence :image, :presence => true
validates_attachment_content_type :image, :content_type => ['image/jpeg', 'image/png']
validates_attachment_size :image, :in => 0..100.kilobytes
end
Please help me.
Modify your the create action in the posts_controller.rb to something like this (notice that format.js was added to the else clause of the respond_to block):
# POST /posts
# POST /posts.json
def create
#topic = Topic.find(params[:topic_id])
#posts = #topic.posts.create(post_params)
respond_to do |format|
#posts.user_id = current_user.id
if #posts.save
format.html { redirect_to topic_posts_path(#topic), notice: 'Post was successfully created.' }
format.js
format.json { render :show, status: :created, location: #posts }
else
format.html { render :new }
format.js # call create.js.erb on save errors
format.json { render json: #posts.errors, status: :unprocessable_entity }
end
end
end
Then, in create.js.erb you can check for errors and handle them however you'd like. Example:
<% if #post.errors.any? %>
alert("ERROR(S): <%= j #post.errors.full_messages.join('; ') %>")
<% else %>
$("#post_table").append("<%= j render #posts %>");
alert("Post created");
<% end %>
I have 2 model Post and Comment, where Post has-many Comments and Comment belongs_to Post.
Every thing working fine with creation of posts and comments for that posts.
Now I have a requirement where when I click on "create new comment" in posts/show page, I want to show the comments/_form in modal.
comments_controller.rb:
class CommentsController < ApplicationController
before_action :set_comment, only: [:show, :edit, :update, :destroy]
# GET /comments
# GET /comments.json
def index
#comments = Comment.all
respond_with(#comments)
end
# GET /comments/1
# GET /comments/1.json
def show
respond_with(#comments)
end
# GET /comments/new
def new
#post = Post.find(params[:post_id])
#comment = #post.comments.build
end
# GET /comments/1/edit
def edit
#post = Post.find(params[:post_id])
end
# POST /comments
# POST /comments.json
def create
#post = Post.find(params[:post_id])
#comment = #post.comments.create(comment_params)
respond_to do |format|
if #comment.save
format.html { redirect_to(#comment, :notice => 'Article was successfully created.') }
format.xml { render :xml => #comment, :status => :created, :location => #comment }
format.js
else
format.html { render :action => "new" }
format.xml { render :xml => #comment.errors, :status => :unprocessable_entity }
format.js
end
end
end
# PATCH/PUT /comments/1
# PATCH/PUT /comments/1.json
def update
#post = Post.find(params[:post_id])
#comment.update(comment_params)
redirect_to post_path(#post)
end
# DELETE /comments/1
# DELETE /comments/1.json
def destroy
#comment.destroy
respond_to do |format|
format.html { redirect_to comments_url, notice: 'Comment was successfully destroyed.' }
format.json { head :no_content }
end
end
private
# Use callbacks to share common setup or constraints between actions.
def set_comment
#comment = Comment.find(params[:id])
end
# Never trust parameters from the scary internet, only allow the white list through.
def comment_params
params.require(:comment).permit(:commenter, :description)
end
end
posts/show.html.erb:
<%= link_to 'New Coment', new_post_comment_path(#post), :id => 'create_comment' %>
comments/new.html.erb:
<div id="content">
<h1>New Comment</h1>
<%= render 'form' %>
</div>
comments/_form.html.erb:
<%= form_for([#post, #comment], :remote => true) do |f| %>
<div class="field">
<%= f.label :commenter %><br>
<%= f.text_field :commenter %>
</div>
<div class="field">
<%= f.label :description %><br>
<%= f.text_field :description %>
</div>
<div class="actions">
<%= f.submit %>
</div>
<% end %>
comments/create.js.erb:
<%- if #comment.errors.any? %>
console.log('Error');
$('#dialog-form').html('<%= escape_javascript(render('form')) %>');
<%- else %>
console.log('Created');
$('#dialog-form').dialog('close');
$('#dialog-form').remove();
$('table').append('<%= escape_javascript(render(#comment)) %>');
<%- end %>
I don't know where I went wrong. When I click on "create comment" it is redirecting to new page instead of pop-up model and even couldn't create comment.
Please help.
Create a partial for showing comment form.
Render this partial to post show page as hidden.
show hidden partial.
Example:
<%= link_to 'Show Modal', "#", data: {toggle: "modal", target: "#modal"} %>
hidden partial
<div class="modal hide fade" id="modal" title="My modal">
/* your comment form */
/* render comment form */
</div>
Just make sure target is your hidden partial id.
Thanks for your time.
I am working on rails 3.2 and I am using gems simple_form, cocoon, and rails3-jquery-autocomplete.
I have following models Machine, Part, PartUsage, MachineCosting and PartCosting
Machine and Part models with many-to-many association.
class Machine < ActiveRecord::Base
attr_accessible :name
has_many :part_usages
has_many :parts, :through => :part_usage
end
class Part < ActiveRecord::Base
attr_accessible :name
end
class PartUsage < ActiveRecord::Base
attr_accessible :machine_id,
:part_id
belongs_to :part
belongs_to :machine
end
MachineCosting and PartCosting models with one-to-many association.
class MachineCosting < ActiveRecord::Base
attr_accessible :machine_id,
:total_cost,
:machine_name,
:part_costings_attributes
attr_accessor :machine_name
has_many :part_costings, :dependent => :destroy
accepts_nested_attributes_for :part_costings, :allow_destroy => true
def machine_name
self.machine.try(:name)
end
end
class PartCosting < ActiveRecord::Base
attr_accessible :part_id,
:part_name,
:cost
attr_accessor :part_name
belongs_to :machine_costing
belongs_to :part
def part_name
self.part.try(:name)
end
end
Form for MachineCosting
views/machine_costings/_form.html.erb
<%= simple_form_for(#machine_costing, :html => {:multipart => true}) do |f| %>
<%= f.input :machine_id, :url => autocomplete_machine_id_machines_path,
:as => :autocomplete %>
<!-- HERE, WHENEVER I SELECT A MACHINE, I WANT TO ADD NESTED-FORMS FOR
PARTCOSTINGS CORRESPONDING TO ALL THE PARTS OF THE MACHINE I JUST SELECTED.-->
<%= f.simple_fields_for :part_costings do |part_costing|%>
<%= render 'part_costings/part_costing_fields', :f => part_costing %>
<% end %>
<% end %>
Please suggest how can I populate and dynamically add fixed no of fields for PartCostings using javascript after machine_id is selected in the field.
I would be happy to provide any more information.
Thanks again!!
First, summary of my approach.
In MachineCosting form, use 'rails3-jquery-autopopulate' gem to search for Machines by their name.
Hack MachineController to also send 'Parts info' together with matching machines in json form.
When user selects a machine, use javascript to add nested_forms for PartCostings and partially populate them using data recieved in json form.
Now here is how I have done it (code part).
views/machine_costings/_form.html.erb
<%= simple_form_for(#machine_costing, :html => {:multipart => true}) do |f| %>
<%= f.error_notification %>
<%= f.input :machine_name, :url => autocomplete_machine_name_machines_path,
:as => :autocomplete, :input_html => { :id_element => "#machine_costing_machine_id"} %>
<%= f.association :machine, :as => :hidden %>
<div id='part_costings'>
<%= f.simple_fields_for(:part_costings) do |part_costing|%>
<%= render 'part_costings/part_costing_fields', :f => part_costing %>
<% end %>
<div class="links hidden">
<%= link_to_add_association 'Add part costings', f, :part_costings,
:partial => 'part_costings/part_costing_fields' %>
</div>
</div>
<%= f.button :submit%>
<% end %>
views/part_costings/_part_costing_fields.html.erb
<div class= 'nested-fields'>
<!-- DO NOT CHANGE THE ORDER OF ATTRIBUTES ELSE machine_costing.js WILL FAIL -->
<%= f.input :part_id, :as=> :hidden %>
<%= f.text_field :part_name, :disabled => 'disabled' %>
<%= f.input :cost %>
<%= link_to_remove_association '<i class="icon-remove"></i>'.html_safe, f ,:class =>"part_costing_remove" %>
</div>
controllers/machines_controller.rb
class MachinesController < ApplicationController
# OVER RIDING THIS BY OWN METHOD
# autocomplete :machine, :name, :full=> true, :extra_data => [:machine_id]
def autocomplete_machine_name
respond_to do |format|
format.json {
#machines = Machine.where("name ilike ?", "%#{params[:term]}%")
render json: json_for_autocomplete_machines(#machines)
}
end
end
def json_for_autocomplete_machines(machines)
machines.collect do |machine|
parts = Hash.new
unless machine.part_usages.empty?
machine.part_usages.each do |part_usage|
parts[part_usage.part.id] = part_usage.part.name
end
end
hash = {"id" => machine.id.to_s, "value" => machine.name,
"parts" => parts}
hash
end
end
#
#
#
end
And here is the javascript part.
assest/javascripts/machine_costings.js
$(function() {
// Part Costings
var part_id;
var part_name;
$('#machine_costing_machine_name').bind('railsAutocomplete.select', function(event, data){
console.debug("Machine selected...");
parts = data.item.parts;
console.debug("Removing existing part_costings...");
$('.part_costing_remove').click();
console.debug("Adding part_costings...");
for(var id in parts) {
part_id = id;
part_name = parts[id];
$('.add_fields').click();
}
console.debug("Done adding all part_costings...");
});
$('#part_costings').bind('cocoon:after-insert', function(e, part_costing) {
part_costing[0].getElementsByTagName('input')[0].value = part_id;
part_costing[0].getElementsByTagName('input')[1].value = part_name;
console.debug("Added part_costing...");
});
});
routes.rb
resources :machines do
get :autocomplete_machine_name, :on => :collection
end
The critical points in javascript solution are...
Method "bind('railsAutocomplete.select', function(event, data)" from rail3-jquery-autocomplete gem.
Method "bind('cocoon:after-insert', function(e, part_costing)" from cocoon gem.
That's all.
Please let me know if you have some suggestions. Thanks :)