I'm writing a (text) messaging app with Rails. I'm using nested_forms to allow you to send a message to multiple people.
In the controller, I instantiate a new Message object, then for each Member, build a Recipient object (child of Message). In the form, I display a checkbox next to each recipient. I want it so that the new Message object only has the recipients that have checks next to them. This is not working.
So by the time the form is rendered, Recipient objects are instantiated for all members. In other words, by default, a message gets sent to each member, unless specified not to. But I want to use the form to allow the user to specify who he wants the messages sent to
Here are my models:
class Message < ActiveRecord::Base
has_many :recipients
accepts_nested_attributes_for :recipients
#columns: body:string, from:string, from_member_id:integer
end
class Member < ActiveRecord::Base
#columns phone:string, name:string
end
class Recipient < ActiveRecord::Base
belongs_to :message
belongs_to :member
#columns: member_id:integer, message_id:integer
end
messages_controller.rb:
def new
#message = Message.new
#members = Member.all
#members.each do |member|
#message.recipients << Recipient.new(:member_id => member.id)
end
end
def create
#message = Message.new(params[:message])
redirect_to '/somewhere'
end
...
And here's my form for Message (app/views/message/new/html.erb)
<%= form_for(#message) do |f| %>
<% if #message.errors.any? %>
<div id="error_explanation">
<h2><%= pluralize(#message.errors.count, "error") %> prohibited this message from being saved:</h2>
<ul>
<% #message.errors.full_messages.each do |msg| %>
<li><%= msg %></li>
<% end %>
</ul>
</div>
<% end %>
<%= f.fields_for :recipients do |builder| %>
<div class="field">
<input type="checkbox" value="<%= builder.object.member_id %>" name="recipients[id]" />
/*WHAT GOES ^^^HERE^^^? */
<%= builder.object.member.name %>
</div>
<% end %>
<div class="field">
<%= f.label :body %><br />
<%= f.text_field :body %>
</div>
<div class="actions">
<%= f.submit %>
</div>
<% end %>
The commented line in the form is where I'm having trouble. Also, it seems I might need to modify some code in MessagesController#create, but I'm not sure where to start.
First, instead of writing your checkbox HTML by hand, you should use the Rails helpers for this. It'll save you a lot of work, particularly in redisplaying the form upon a validation failure.
To do this, you'll need to create an attribute on your Recipient class:
class Recipient
attr_accessor :selected
end
Then you can hook up that attribute to the checkbox:
<%= builder.check_box :selected %>
The next step is to make that attribute do something. You could try using the :reject_if option for accepts_nested_attributes_for. You could pass it a proc that returns true if the checkbox is not checked, e.g.:
accepts_nested_attributes_for :recipients, :reject_if => proc { |attributes| attributes['selected'] != '1' }
See these docs for details on :reject_if:
http://api.rubyonrails.org/classes/ActiveRecord/NestedAttributes/ClassMethods.html
Related
First, the example I read in the docs shows to declare the associated model as singular, :address, but if I do I get the error Association named 'address' was not found on User; If I change it to plural :addresses, then the next problem I have is the association doesn't work in views undefined method `country' for ...
Why am I declaring the association as plural and how can I make the association available in the view
User.rb:
class User < ActiveRecord::Base
searchkick word_middle: ['full_name', 'description', 'interests']
has_many :addresses
scope :search_import, -> { includes(:addresses) }
search.html.erb:
<% #users.each do |u| %>
<li>
<%= link_to "#{u.first_name} #{u.middle_name} #{u.last_name}", page_path(name: u.name) %>
<% #ua=u.addresses.where("current=?", true) %>
<% if #ua.country=="US" %>
<%= #ua.city %>, <%= #ua.state %> <%= ISO3166::Country.find_country_by_alpha2(#ua.country) %>
<% else %>
<%= #ua.city %>, <%= ISO3166::Country.find_country_by_alpha2(#ua.country) %>
<% end %>
</li>
<% end %>
</ul>
In controller, do this: #user = User.includes(:addresses).where(your_query) to make the association readily available in view.
And yes has_many associations are bound to be plural: "User has_one
:life" and "User has_many :passions"; does it make sense?
And finally your error: where returns an array because you queried: "bring me all records which fulfill this condition". find, on the other hand, will bring back 1 specific record as it expects a unique attribute or brings back first record that matches that attribute.
What you need to do:
You should either do this (if you are dead-sure that you will get 1
such record or you need only one of that type):
<% #ua=u.addresses.where("current=?", true).first %>
OR
If you need to go through all the resultant array then:
<% #ua=u.addresses.where("current=?", true) %>
<% #ua.each do |ua| %>
# Your code for each ua instead of #ua.
<% end %>
Happy Learning :)
My form partial is as follows:
<%= form_with(model: guitar, local: true) do |form| % >
<% if guitar.errors.any? %>
<div id="error_explanation">
<h2><%= pluralize(guitar.errors.count, "error") %> prohibited this
guitar from being saved:</h2>
<ul>
<% guitar.errors.full_messages.each do |message| %>
<li><%= message %></li>
<% end %>
</ul>
</div>
<div class="form-group">
<%= form.label :title %>
<%= form.text_field :title, class: "form-control" %>
</div>
<div class="form-group">
<%= form.label :description %>
<%= form.text_area :description, class: "form-control" %>
</div>
<div class="form-group">
<%= form.submit "Create New Guitar Lesson", class: "btn btn-primary"
%>
</div>
<% end %>
When I try to create a new entry in a table via a form, an error message pops up 'user must exist', which is fine by me.
How do I auto insert (which file / section should I place the code in) current logged in user ID to the form in the background without having the user keying it in manually?
I've pushed the entire app up to the cloud at http://github.com/cheese1884/197451 –
Assuming your field in table is called user_id and you are using Devise.
You should insert the following in your form
<%= form.hidden_field :user_id, value: current_user.id %>
The user's id (taken from Devise's current_user) will be prepopulated in a hidden field that they won't be able to see.
As per doc mentioned of user model
app/models/guitar.rb
class Guitar < ApplicationRecord
belongs_to :user
end
app/models/user.rb
class User < ApplicationRecord
has_many :guitars
end
In rails 5 belongs_to association required by default
which means at the time of creation of each record of guitar user_id is required.
So here by at controller you can solve it by: -
In devise after sign_in there is a helper method which current_user which is current logged_in user
in guitars_controller.rb
def create
new_guitar_record = current_user.guitars.new(guitar_params)
if new_guitar_record.save
#guitar created successfully for current logged in user
else
#current_user.guitar.errors.full_messages
end
end
def guitar_params
params.require(:guitar).permit(:name, :description)
end
Please amend controllers/application_controller as you have messed up devise's current_user and your custom current_user
class ApplicationController < ActionController::Base
protect_from_forgery with: :exception
end
Note: - You can also pass current_user.id by hidden_field :user_id with guitar form but as per security concern it's not good, as user can invoke any user_id via browser.
There are many way to implement this, as i understand you have an user_id field in Guitar model.
Simple solution is need to attach user_id into Guitar object on create action.
In GuitarController go for create and add this line. .merge(user_id: current_user.id).
Remember User must be signed in to get current_user object.
Sample:
#g = Guitar.new(guitar_params.merge(user_id: current_user.id))
Edited
You have many bugs there, 1st of all you need to clean up your controllers.
ApplicationController: remove lines between 6-18. No need it, because Devise gem will provide you these features already.
GuitarsController:
def guitar_params
params.require(:guitar).permit(:name, :description)
end
// guitar view from
<div class="form-group">
<%= form.label :name %>
<%= form.text_field :name, class: "form-control" %>
</div>
// models/user.rb
//add this line
has_many :guitars
If you want to sign in a user in the background, use the sign_in helper inside your controller's action:
sign_in(:user, user)
I am trying to submit info from a form in my view, that passes the submitted info :hashtag into the model and then runs the model with that info. But it seems thats my model just runs with the words "hashtag" instead of the form info. I believe it is close. I just can't figure out where to go next.
home.html.erb
<div class="row">
<div class="span6 offset2">
<%= form_for :hashtag do |f| %>
<div class="input-prepend input-append">
<span class="add-on swag">#</span>
<%= f.text_field :hashtag , class: "span3 inverse", id:"appendedPrependedInput" %>
<%= f.submit "Swag!", class: "btn btn-inverse" %>
<% end %>
</div>
</div>
<div class="span4">
<div id="hashtags">
<% #random_hashtags.each do |hashtag| %>
<blockquote><%= hashtag.content %></blockquote>
<div class="from">— #<%= hashtag.screen_name %></div>
<% end %>
</div>
</div>
</div>
hashtag.rb
class Hashtag < ActiveRecord::Base
attr_accessible :content, :profile_image, :screen_name, :tweet_date, :tweet_id
def self.pull_hashtag
Twitter.search("%#{hashtag}").results.map do |tweet|
unless exists?(tweet_id: tweet.id)
create!(
tweet_id: tweet.id,
content: tweet.text,
profile_image: tweet.profile_image_url,
screen_name: tweet.from_user,
tweet_date: tweet.created_at
)
end
end
end
end
hashtags_controller
class HashtagsController < ApplicationController
def home
#random_hashtags = Hashtag.order("RANDOM()").limit(4)
end
def create
#hashtag = Hashtag.pull_hashtag(params[:search])
end
end
Updated code that I am currently using now as I was not posting anything to the model
It is going though on submit but it seems nothing from there.
Update 2,
I am trying to post the information to the database, by taking the info from the form, running a Twitter.search on it and creating the results in my database.
Can you try to replace with this?
form_for #hashtag, :url => :action => 'home'
my guess is that the action needs to be specified.
In the end I would like a text field that passes a client_id to the partial. I would like to do this asynchronously so the shipment_products partial would dynamically change when the textfield value was updated. What is the best way to do this?
In index.html.erb
<!-- Text Field Here-->
<div id="available_products">
<%= render "shipment_products" %>
</div>
In _shipment_products.html.erb
<div id="shipment_products_container">
<h3>Assign Products to Ship<\h3>
<ul class="shipment_products" id="shipment_products">
<% Product.by_client(client_id).each do |product|%> <!-- TextField value passed here -->
<%= content_tag_for :li, product, :value => product.id do %>
<%= hidden_field_tag("shipment[product_ids][]", product.id) %>
<%= product.product_name %>
<% end %>
<% end %>
<\ul>
</div>
Model relationships:
Models and Relationships
Shipment has_many :products :through => :shipment_products
Product has_many :shipments :through => :shipment_products
ShipmentProducts belongs_to :shipment, belongs_to :product
Product belongs_to :client
Client has_many :products
This is similar to what I want in the end.
Since I do not know what's in your controller and how you make routes, I made some suggestions. Change a few things to the actual. I assume that you do not need to change index action (if only to add respond_to js)
index.html.erb
<%= form_tag shipment_path, remote: true do %> - points to index action
<%= text_field_tag :client_id %>
<%= submit_tag :load %>
<% end %>
<div id="available_products">
<%= render "shipment_products" %>
</div>
index.js.erb
$('#available_products').html("<%= j render "shipment_products" %>");
I think you should use nested model concept for this please refer-
http://railscasts.com/episodes/197-nested-model-form-part-2?view=asciicast
I'm creating a little newsletter application, with 'double opt-in restrictions', when I simply fill in my form (subscription page) and submit the form I get redirected to my subscribed page (which is all normal) however my form appends a querystring to my action attribute of my form (http://localhost:3000/newsletter/subscribe?format=)
routes:
match 'newsletter/subscription' => 'newsletter_subscriptions#subscription'
post 'newsletter/subscribe' => 'newsletter_subscriptions#subscribe'
controller:
class NewsletterSubscriptionsController < ApplicationController
respond_to :html
# GET /newsletter/subscription
def subscription
respond_with (#subscription = NewsletterSubscription.new)
end
# POST /newsletter/subscribe
def subscribe
# If there's already an unconfirmed record with the submitted email, use that object otherwise create a new one based on the submitted email
sub_new = NewsletterSubscription.new
sub_new.email = params[:newsletter_subscription]['email']
sub_old = NewsletterSubscription.find_by_email_and_confirmed sub_new.email, 0
#subscription = sub_old || sub_new
if #subscription.save
Newsletter.delay.subscribed(#subscription) # with delayed_job
else
render :action => "subscription"
end
end
...
end
view (newsletter_subscription/subscription.html.erb):
<h1>New newsletter_subscription</h1>
<%= form_for(#subscription, :url => newsletter_subscribe_path(#subscription)) do |f| %>
<% if #subscription.errors.any? %>
<div id="error_explanation">
<h2><%= pluralize(#subscription.errors.count, "error") %> prohibited this newsletter_subscription from being
saved:</h2>
<ul>
<% #subscription.errors.full_messages.each do |msg| %>
<li><%= msg %></li>
<% end %>
</ul>
</div>
<% end %>
<div class="field">
<%= f.label :email %>
<br/>
<%= f.text_field :email %>
</div>
<div class="actions">
<%= f.submit %>
</div>
<% end %>
PS: I would be pleased if someone could evaluate my ruby code please (posted above), I'm still learning a lot and would like to see some 'guidelines' or feedback, I think I still can learn a lot.
Try removing the #subscription argument you're passing into newsletter_subscribe_path. Since there isn't an :id in the route and it's a new object, passing it doesn't really make sense. I'm assuming that's what is being interpreted as the format.
<%= form_for(#subscription, :url => newsletter_subscribe_path) do |f| %>
As for improvements you can make to the code, the biggest thing I see is moving the old/new subscription logic into the model.
# in NewsletterSubscription
def self.with_email(email)
find_by_email_and_confirmed(email, 0) || new(:email => email)
end
# in controller
#subscription = NewsletterSubscription.with_email(params[:newsletter_subscription]['email'])
if #subscription.save
#...
Also respond_to and respond_with aren't really necessary here since you're just dealing with HTML views. You can remove that.