I am having a difficult time finding information on this but think the solution is a simple one. In short, I need to have the ability to add multiple entries to one model at one time. The user story goes like this: User selects "Add New" and is directed to the page where they can add simply ONE entry or select a drop down of the desired entries they want to add.
All the posts I see have information about doing this with objects that are nested but I am just using one model. Do I need to follow the same protocol? Is there a simpler way? Am I just searching for the wrong terminology since being new to Ruby?
The basic application looks like this:
ticket_controller.rb
def new
#ticket = Ticket.new
end
def create
tnum = gets.chomp
tnum.times do Ticket.new(ticket_params)
respond_to do |format|
if #ticket.save
format.html { redirect_to #ticket, notice: 'Ticket was successfully created.' }
format.json { render action: 'show', status: :created, location: #ticket }
else
format.html { render action: 'new' }
format.json { render json: #ticket.errors, status: :unprocessable_entity }
end
end
end
new.html.erb
<h1>New ticket</h1>
<%= render 'form' %>
<%= link_to 'Back', tickets_path %>
I have looked throughout the site and just think I am missing something! Thanks for pointing me in the direction needed.
_form.html.erb
<%= form_for(#ticket) do |f| %>
<% tnum.times do |index|%>
<div class="field">
<%= f.label :type %><br>
<%= f.text_field :type %>
</div>
<div class="field">
<%= f.label :amount %><br>
<%= f.text_field :amount %>
</div>
<% end %>
<div class="actions">
<%= f.submit %>
</div>
<% end %>
There must be a better way, but this one way to do it. (Note I normally use a 'Form Object' but for this example I'll just use the raw collection):
In your controller change the object passed to the new form to be an array (in this case I'll pre-populate it):
def new
#tickets = [Ticket.new(title: 'New Ticket')]
end
Then in your new template you need to update it to iterate over the tickets array:
<%= form_tag tickets_path do |f| %>
<% #tickets.each do |ticket| %>
<%= fields_for "tickets[#{ticket.object_id}]", ticket do |builder| %>
<%= render 'ticket_fields', f: builder %>
<% end %>
<% end %>
<%= link_to_add_ticket "Add Tickets" %>
<%= submit_tag %>
<% end %>
The ticket fields partial looks like:
<fieldset>
<%= f.label :content, "Ticket" %><br />
<%= f.text_field :title %><br />
</fieldset>
For good measure add a helper to allow you to add new tickets dynamically:
module TicketsHelper
def link_to_add_ticket(name)
# create a new object for the form
new_object = Ticket.new
# get an id for javascript to hook into
id = new_object.object_id
fields = fields_for("tickets[#{id}]", new_object) do |builder|
render("ticket_fields", f: builder)
end
link_to(name, '#', class: "add_fields", data: {id: id, fields: fields.gsub("\n", "")})
end
end
and you need some coffee script to wire that up:
jQuery ->
$('form').on 'click', '.add_fields', (event) ->
time = new Date().getTime()
regexp = new RegExp($(this).data('id'), 'g')
$(this).before($(this).data('fields').replace(regexp, time))
event.preventDefault()
Basically all of this is an adaption of this railscast which might be helpful: http://railscasts.com/episodes/196-nested-model-form-revised however, that is dealing with the more classic nested model.
Couldn't you save the number of tickets the user wants to purchase to a variable, then pass that integer to a times loop? Like so:
#gets from form
ticket_buys = gets.chomp
ticket_buys.times do Ticket.new(ticket_params)
Related
I am trying to get a Boolean result from verify_recaptcha that is implemented my app controller.
Code from the controller:
def create
#render plain: params[:student].inspect
#student = Student.new(student_params)
if verify_recaptcha(model: #student) && #student.save
redirect_to #student
else
render 'new'
end
end
HTML code:
<h1 class="col-md-12">New Student</h1>
<div class="col-md-12">
<%= form_with scope: :student,url: students_path,local: true do |form|%>
<p>
<%= form.label :name %><br>
<%= form.text_field :name%>
</p>
<p>
<%= form.label :student_id,'Student ID' %><br>
<%= form.text_field :student_id%>
</p>
<p>
<%= form.label :course %><br>
<%= form.text_field :course%>
</p>
<%= recaptcha_tags%>
<p>
<%=form.submit 'Create Student'%>
</p>
<%end%>
<%= link_to 'Back', students_path %>
</div>
EDIT: Here is what I want to achieve when recaptcha fails.
So far, when the recaptcha fails, the webpage only reloads and doesn't go to the next page. What I want to do is create an alert indicating the number of errors. It turns out I am unable to use verify_recaptcha in the HTML. Any advice?
If you want to use the result of verify_recaptcha in the view, you could assign the result to something in the controller, maybe like this:
def create
#student = Student.new(student_params)
#recaptcha_succeeded = verify_recaptcha(model: #student)
if #recaptcha_succeeded && #student.save
redirect_to #student
else
render 'new'
end
end
Now in the view you should be able to refer to #recaptcha_succeeded.
However, by passing in a model to verify_recaptcha, an error should be added to the model as well, in this case in #student.errors. That information will also be available in the view. The docs on the recaptcha gem (https://github.com/ambethia/recaptcha) discuss this in more detail.
I decided to make a clone of Facebook in Rails. First I'm working on getting status updates working. I got it setup as the StatusUpdate model that is called by the Pages controller to render on the index page.
The issue I'm having is that if I use form_for(#status_update) I get:
undefined method to_key' for
<StatusUpdate::ActiveRecord_Relation:0x00000000049d3448>
Did you mean? to_set to_ary
If I use form_with(model: #status_update):
undefined method to_model' for
<StatusUpdate::ActiveRecord_Relation:0x000000000471cd80>
Did you mean? to_xml
If I use form_with(model: status_update):
undefined local variable or method status_update' for
<#<Class:0x0000000005801678>:0x0000000002ec8ec8>
Did you mean? #status_update
My action:
def create
#status_update = StatusUpdate.new(status_update_params)
respond_to do |format|
if #status_update.save
format.html { redirect_to root_path, notice: 'Status successfully posted!' }
else
format.html { render :new }
end
end
and erb view:
<%= form_with(model: status_update) do |sp| %>
<div class="form-group">
<%= sp.label :status_update %>
<%= sp.text_area :status_update, class: 'form-control', rows: 15, placeholder: 'Content' %>
</div>
<div class="form-group">
<%= sp.submit 'Submit', class: 'btn btn-primary' %>
</div>
<% end %>
I think you are missing the initialisation step. You have to first initialise the model object in new action of the controller.
def new
#status_update = StatusUpdate.new
end
and then use it in form.
form_with(model: #status_update)
The argument to form_with must be a single model instance. Not a whole collection.
class Pages
def index
#status_updates = StatusUpdate.all
#new_status_update = StatusUpdate.new
end
end
---
# app/views/pages/index.html.erb
<%= form_with(model: #new_status_update) %>
# ...
<% end %>
<%= #status_updates.each do |s| %>
# ...
<% end %>
Which is why you need to pay attention to pluralization when naming variables!
Another way to solve this is by using a condition:
# app/views/status_updates/form.html.erb
<%= form_with(model: local_assigns(:status_update) || StatusUpdate.new) %>
...
<% end %>
Which lets you use the form as a partial even without a StatusUpdate instance:
# app/views/pages/index.html.erb
<%= render partial: 'status_updates/form' %>
<%= #status_updates.each do |s| %>
# ...
<% end %>
I am really embarrassed with this problem :
I want to post comments from one view and if errors occurs in form, i want the form to be repopulated. I use the render method but my form isn't repopulated.
I specify that the form is displayed from a view and use another controller action, by other words means :
Form called from : views/cars/show.html.erb code below :
<h1>Fiche détaillée</h1>
<%= #car.marque %><br>
<%= #car.modele %><br>
<%= #car.nbkm %><br>
<%= #car.couleur %><br>
<%= #car.disponibilite %><br>
<hr>
<% x=0 %>
<h1><%= pluralize(#car.comments.count, 'commentaire') %></h1>
<% #car.comments.each do |k| %>
<%= x+=1 %>
Email : <%= k.email %><br>
Sujet : <%= k.sujet %><br>
Commentaire : <%= k.commentaire %><br>
<%= link_to 'Supprimer', [k.car, k], method: :delete %><br><br>
<% end %>
<hr>
<h1>Ajouter votre commentaire</h1>
<div style='width:300px;'>
<% flash.each do |key, msg| %>
<% if msg.count >0 %>
<p class="bg-danger" style='padding:10px;'><%= pluralize(msg.count,'error') %>
<ul><% msg.full_messages.each do |m|%>
<li><%= m %></li>
<% end %>
</p>
<% end %>
<% end %>
</ul>
<%= form_for([#car,#car.comments.build]) do |co| %>
<%= co.label :'Email' %><br>
<%= co.text_field :email , class: 'form-control' %><br>
<br>
<%= co.label :'Sujet' %><br>
<%= co.text_field :sujet , class: 'form-control'%><br>
<br>
<%= co.label :'Commentaire' %><br>
<%= co.text_area :commentaire , class: 'form-control' %><br>
<br>
<%= co.submit :'Envoyer votre commentaire', class: 'btn btn-info'%>
<% end %>
</div>
below my controllers :
Controller 1 : controllers/cars_controller.rb
def create
#render text: params[:car].inspect
#car = Car.new(params[:car].permit(:marque,:modele,:nbkm,:couleur,:disponibilite))
if !#car.save
render 'new'
else
redirect_to #car
end
end
def show
#car = Car.find(params[:id])
end
def index
#cars=Car.all
end
Controller 2 : controllers/comments_controller.rb
class CommentsController < ApplicationController
def new
#comment=Comment.new
end
def create
#car = Car.find(params[:car_id])
#comment = #car.comments.create(params[:comment].permit(:email,:sujet,:commentaire))
if !#comment.save
flash[:error] = #comment.errors
flash.keep[:error]
render 'cars/show'
else
redirect_to car_path(#car)
end
end
def destroy
#car = Car.find(params[:car_id])
#comment = #car.comments.find(params[:id])
#comment.destroy
redirect_to car_path(#car)
end
end
I really don't understand why it does not work !!
Thank you so much for any assistance ;)
Edited:
I did some similar tests a bit for your case, the problem should due to the flash method.
Replace below lines:
flash[:error] = #comment.errors
flash.keep[:error]
render 'cars/show'
To:
flash.now[:error] = #comment.errors.full_messages
#car.reload
render 'cars/show'
Because flash[:error] will only be available in next action, means only works in redirect_to, so you have to use flash.now[:error] for rendering same view template. And most importantly, though the save failed of the #comment, the comment list in #car will still receive an instantiated invalid object return by the comment's create, build, or new method. It is because these three methods will always return instantiated object to the #car.comments collection, though it is failed to save it. So we must reload the #car object by #car.reload to refresh the memory and get correct Comment collection from the database.
Previous response:
In crate action, the create method #car.comments.create(..) will directly create and return an instantiated object without given attributes then try to save it if it passed the validation. If you have not set validations for Comment model, then it will directly save it. Try #car.comments.new(..) or #car.comments.build(..) for collection associations, it will not force to save an instantiated object after the validation passed. Also, check your Comment model for setting the validations.
I am new to rails so sorry if sometimes I don't make much sense. Here is what I am trying to do. I am trying to build a vote system. So next to a blog post there is a link that says 'vote' (will probably say like later). So far I have working: when the vote button is clicked, a value of '1' is delivered to the vote table and then that particular posts vote records display beneath the vote via AJAX (I copied a comment functionality). Instead of rendering all the number '1's below, I want it to render the updated count.
My vote table has the columns 'vote' and 'post_id' that are successfully being entered. My thinking was that I could just change my partial template to do this. Here is the code:
votes_controller:
class VotesController < ApplicationController
def create
#post = Post.find(params[:post_id])
#vote = #post.votes.create!(params[:vote])
respond_to do |format|
format.html { redirect_to #post}
format.js
end
end
end
def count
#post = Post.find(params[:post_id])
#vote = calculate :count
respond_to do |format|
format.html { redirect_to #post}
format.js
end
end
end
Here is the page where is is showing, /posts/show.html.erb:
<div id="backto"<%= link_to 'Back to all BattleCries', posts_path %></div>
<%= render :partial => #post %><br/>
<p5>Add a Comment</p5>
<div id="belt">
<div id="belttext">
<% remote_form_for [#post, Comment.new] do |f| %>
<p>
<%= f.text_area ( :body, :class => "commentarea") %>
</p>
<%= f.submit "Add Comment"%>
<% end %>
</div>
<div id="beltbottom">
</div>
</div><br/>
<br/><p5>Comment Stream </p5>
<div id="comments">
<%= render :partial => #post.comments %>
</div>
<p>
<% remote_form_for [#post, Vote.new] do |f| %>
<p>
<%= f.hidden_field :vote, :value => '1' %>
</p>
<%= f.submit "Vote" %>
<% end %>
<div id="vote">
<div id="votes">
<%= render :partial => #post.votes %>
</div>
</div>
</p>
Here is the :partial, /votes/_vote.html.erb: (this is where I thought I would just need to change it to vote.count, or post.count or something but can't get it to work).
<% div_for vote do %>
<%= h(vote.vote) %>
<% end %>
Here is the /votes/create.js.rjs file:
page.insert_html :bottom, :votes, :partial => #vote
page[#vote].visual_effect :highlight
I hope that all makes sense.
I think it's repeating because your .rjs is "inserting at the bottom" of the div instead of "replacing" ... you probably want page.replace_html
It would be better to have a DIV or SPAN tag that contains the number of votes for a post ... then have your .rjs file update the DIV's inner_html with the number of votes (which would be #post.votes.count) ... I don't think you really need a partial.
You probably want:
<%= #post.votes.count %>
You also probably want to use replace instead of insert_html - does that make sense? Insert is just adding more elements to the DOM whereas replace will replace the element.
I have my code working so that I have a table of businesses. There's a pencil icon you can click on the edit the business information. The edit information comes up in a partial inside of a modal pop up box. The only problem is that once they make the changes they want and click update, it sends them to the 'show' page for that business. What I want to happen is have the pop up box close and have it update the information. This is my update function in my controller.
def update
#business = Business.find(params[:id])
respond_to do |format|
if #business.update_attributes(params[:business])
flash[:notice] = 'Business was successfully updated.'
format.html { redirect_to(business_url(#business)) }
format.js
else
format.html { render :action => "edit" }
format.xml { render :xml => #business.errors, :status => :unprocessable_entity }
end
end
end
I tried following railscast 43 and i created an .rjs file but I couldn't get that to work at all. My update was still taking me to the show page. Any help would be appreciated.
EDIT: Added some more code.
<% form_for(#business) do |f| %>
<%= f.error_messages %>
<p>
<%= f.label :name %><br />
<%= f.text_field :name %>
</p>
...
<%= f.label :business_category %><br />
<%= f.select :business_category_id, #business_categories_map, :selected => #business.business_category_id %>
</p>
<p>
<%= f.label :description %><br />
<%= f.text_area :description %>
</p>
<p>
<%= f.submit 'Update' %>
</p>
<% end %>
This is my form inside of my edit page which is being called through the index in a pop up by:
<div id="popupEdit<%=h business.id %>" class="popupContact">
<a class="popupClose<%=h business.id %>" id="popupClose">x</a>
<% if business.business_category_id %>
<% #business = business %>
<%= render "business/edit" %>
<% end %>
</div>
It's hard to say without seeing your form code (hint hint! :-), but it could be that you're using form_for (which will submit a POST as per normal html forms) instead of remote_form_for (which will send an AJAX request).
Gotta say, get your code to work without javascript first ... also, don't use RJS at all. Use unobtrusive javascript instead.
As Mr. Hyland says, we need to see your view code also before we can help any further.