I'm developing a simple rails app for my own use for learning purposes and I'm trying to handle 2 models in 1 form. I've followed the example in chapter 13 of Advanced Rails Recipes and have got it working with a few simple modifications for my own purposes.
The 2 models I have are Invoice and InvoicePhoneNumber. Each Invoice can have several InvoicePhoneNumbers. What I want to do is make sure that each invoice has at least 1 phone number associated with it. The example in the book puts a 'remove' link next to each phone number (tasks in the book). I want to make sure that the top-most phone number doesn't have a remove link next to it but I cannot figure out how to do this. The partial template that produces each line of the list of phone numbers in the invoice is as follows;
<div class="invoice_phone_number">
<% new_or_existing = invoice_phone_number.new_record? ? 'new' : 'existing' %>
<% prefix = "invoice[#{new_or_existing}_invoice_phone_number_attributes][]" %>
<% fields_for prefix, invoice_phone_number do |invoice_form| -%>
<%= invoice_form.select :phone_type, %w{ home work mobile fax } %>
<%= invoice_form.text_field :phone_number %>
<%= link_to_function "remove", "$(this).up('.invoice_phone_number').remove()" %>
<% end -%>
</div>
Now, if I could detect when the first phone number is being generated I could place a condition on the link_to_function so it is not executed. This would half solve my problem and would be satisfactory, although it would mean that if I actually wanted to, say, delete the first phone number and keep the second, I would have to do some manual shuffling.
The ideal way to do this is presumably in the browser with javascript but I have no idea how to approach this. I would need to hide the 'remove' link when there was only one and show all 'remove' links when there is more than one. The functionality in the .insert_html method that is being used in the 'add phone number' link doesn't seem adequate for this.
I'm not asking for a step-by-step how-to for this (in fact I'd prefer not to get one - I want to understand this), but does anyone have some suggestions about where to begin with this problem?
There is a counter for partial-collections:
<%= render :partial => "ad", :collection => #advertisements %>
This
will render "advertiser/_ad.erb" and
pass the local variable ad to the
template for display. An iteration
counter will automatically be made
available to the template with a name
of the form partial_name_counter. In
the case of the example above, the
template would be fed ad_counter.
For your problem of detecting whether a row is the first one or not, you could add a local variable when calling the partial:
<%= render :partial => 'mypartial', :locals => {:first => true} %>
As it would be much easier to detect in the main file, whether a row is the first or not I guess.
Instead of detecting whether a phone number is the first, you could also detect whether a phone number is the only one. If not, add remove links next to all numbers otherwise, do not display the remove link. Note that besides showing/hiding the link, you also need to add code, to prevent removing of the last number by (mis)using an URL to directly delete the number instead of using your form.
Related
I'm a newb to Ruby on Rails and I have a issue trying to display the index number of a record set. I've had a good search, but can't find the specific answer.
The issue I'm having is that I cannot find a way to output the index number from a recordset which i'm displaying.
[Title], [Description]
[Title], [Description]
[Title], [Description]
etc.
So to clarify, I'm looking to output the order (index) number (1, 2, 3) in a list where i'm able to output the Title & Description, also i'll point out the obvious - this is different to the unique ID which may be stored in the database, thus if i was to filter or sort the results I would still want to show the order (index) number (1, 2, 3 etc).
I have founds example of where they loop through the results and incrementally add to a pre-defined index value. The problem is my app doesn't use a loop statement to output the records, instead it's using an Active Record to display (and essentially loop through) the results. From what I understand, Active records will automatically loop through and output the records by rendering the code snippet ie. <%= render #links %> This works great for my example - For the full code for the app, please refer to the tutorial I deprived the app from:
https://www.codementor.io/danielchinedu/building-a-basic-hacker-news-clone-with-rails-5-4gr4hrbis
So in retrospect, I'm looking to clone the app in the tutorial but add an order number to the link lists.
From the documentation:
Rails also makes a counter variable available within a partial called
by the collection, named after the member of the collection followed
by _counter.if you're rendering #products, within the partial you can
refer to product_counter to tell you how many times the partial has
been rendered.
guides.rubyonrails.org
So you can use something like <%= link_counter %> in your _link.html.erb partial. Hope this will help.
The tutorial actually explains it quiet well, i think.
Underneath the <%= render #links %>:
We are using a rails feature here, when you call render on an instance variable holding an active record relation object, it will iteratively loop through the object and then render the appropriate partial.
So! It's a Rails feature making it easier to write partial loops!
<%= render #links %> is the same as: <%= render partial: 'link', collection: #links %>
If you want an index in your partial, you can just append a local variable. For example:
<%= render #links, locals: {num: 1} %>
And then in your view after you have written the variable, remember to add 1 so it's ascending.
<div class="link">
<div class="title">
<%= num %>
<%= link_to link.title, (link.url? ? link.url : link) %>
...
<% num += 1 %>
Good luck!
I have a view show.html.eb that displays details for an Order and displays all available Delivery Slots for the date of the order.
Eg:
Order 1
12/12/14
Delivery slots:
6.30 - 7.00
7.30 - 8.00
8.00 - 8.30
I then have a link next to each delivery slot which when clicked updates the field delivery_slot on the Order to the Id of the delivery slot that was clicked.
View Code
<% #slots = DeliverySlot.all.select {|slot| slot.day == #order.date} %>
<% #slots.each do |slot| %>
<%=slot.start_time.strftime("%I:%M%p") %> - <%=slot.end_time.strftime("%I:%M%p") %>
<%= link_to "Order", Order.update(#order,:delivery_slot => slot) %>
<br>
<% end %>
The issue is, when you click one Order link, all the order links are clicked (I can see this through the SQL in the terminal) so the end result is the delivery_slot field is always populated with the last delivery slot of the loop.
I appreciate that I am missing something here so could anyone explain:
1) Why all Order links are "clicked" when only 1 is clicked in practice.
2) Is there a better way to update the delivery_slot attribute on the Order?
Thanks
You are issuing an update when rendering the view.
<%= link_to "Order", Order.update(#order,:delivery_slot => slot) %>
You need to provide a path (or url_options which can be used to generate a path) as the second argument to link_to. Instead you are calling the update method. So when your view is rendered, you iterate over every delivery slot and issue an update for each one of them and as you have noted, the last one overwrites everything else.
If you observe your logs you will see that the SQL statements are seen in logs lines corresponding to the show request. You are updating the order even before any link is clicked.
Now since the link_to does not get a valid path, the actual HTML link is not pointing to anything. Clicking on it would be a no-op.
ri link_to is what you need to read.
So I finally arrived at the answer:
<%= link_to "Order", order_path(order:{:delivery_slot_id => slot}), :method => :put %>
I think what tripped me up in the end was that my strong parameters specific :delivery_slot_id and I was specifying :delivery_slot so I assumed I was doing something wrong when the answer was very simple.
Imagine a Rails project that looks up animal celeberties based on their names. This Rails app is backed by an external service that does the actual lookup. The service returns back results based on a key. For example, if I make a request to this external api like [GET] /animal?name=benji, I would get back something like {"type":"dog", "legs":"4", "tail-length":"short", "collar":"blue"}. However, if I pass in ...?name=flipper to the animal endpoint, I would get back {"type":"dolphin", "color":"gray", "food":"fish"}. (The data is returned in actual JSON or XML. I am just using pseudo code here to communicate the point.)
My first question is this... Given that the attributes of the return call vary based on data which is passed in, when unmarshaling a response (for lack of a better term) into a "model" object, does it make sense to implement some type of factory pattern (ala Design Patterns in Ruby, by Russ Olsen, Chapter 13) to create objects of an appropriate class? Are there other approaches that would make sense?
My next question is this, lets say that I want to display a list of all animals on a web page (using ERB templates.) Does it make sense to create different partial templates (eg _dolphin.html.erb and _dog.html.erb) and then put a case in the main list view that can deligate rendering each list item to an appropriate template.
For example:
list.html.erb...
<ul>
<% for animal in #animals.each %>
<li>
<% if animal.type == 'dog' %>
<%= render :partial => 'dog', :locals => {:animal => animal} %>
<% elsif item.type == 'dolphin' %>
<%= render :partial => 'dolphin', :locals => {:animal => animal} %>
<% else %>
<%= render :partial => 'generic_animal', :locals => {:animal => animal} %>
<% end %>
</li>
<% end %>
</ul>
(Here animal.type=='dog' is intentional. I am not using a symbol (:dog) because the data returned back from the API is a string value, and it is used to populate the animal.type attribute. Bad, I know.)
The project that I am working on is using this approach right now. (Obivously, I have changed the elements/domain.) I am wondering if this is a valid approach, and/or if others have dealt with similar problems and how they went about it.
Thanks!
I'd say create a single model and a single view which contains all possible attributes (can't be an infinite number ;) ).
And then you have an
if attribute_x exists then
display it
end
if attribute_y exists then
display it
end
for each attribute.
If you create a view for each animal this wouldn't be DRY at all, 'cause you'll repeat yourself sooo many times, just knowing that each animal has favorite food and a color, etc.. Another reason: If the API changes a bit, and an animal gathers or looses an attribute you would have to adapt this change.
With just one view, it would be all fine for all time.
If you want to be super-sure that you gather all attributes, you could place an array of all known attributes inside your controller and if there's something unknown: write it to a log file.
I'd only choose the way of 'one view per animal' if you want to be able to display things completely different for some animals. But then you could also tell your controller that it should choose another view if name = 'Donkey Kong'. you know what I mean.
I am new to rails, but not to programming or databases.
A BETTER PHRASING OF MY QUESTION IS IN MY ANSWER BELOW.
For simplicity in this example, I have 3 models:
User
Subscription
Default_Subscription
User has_many Subscriptions
Subscription belongs_to User
Default_Subscription has_many Subscriptions
Subscription belongs_to Default_Subscription
Default_Subscription is a pre-populated table with certain types of subscriptions.
During the subscription process, the default subscriptions are listed at one point, and there
is a quantity box alongside each one.
The user enters the quantities for each subscription and hits submit to continue on.
My question is:
How would one go about creating a new subscription for each quantity in a number form?
So you would have a list something like so:
<ol>
<%= each subscription with quantity box %>
</ol>
<%= button_to %>
When the user hits the button, how do you add up the quantity from each box and add a new subscription for each one? Do I have to use javascript to get the numbers? Do I somehow use rails forms even though this quantities are not associated with any specific database field? Any recommendations or pointing me in the right direction to figure this out on my own would be great.
This form box IS NOT A FIELD FOR ANY MODEL, it's a count for an association. Let me rephrase: Each quantity in the form boxes represent the number of NEW Subscriptions to be created. Each of these subscriptions BELONGS_TO 1 DEFAULT_SUBSCRIPTION. In essence, the number represents the number of new subscriptions ASSOCIATED WITH THAT DEFAULT SUBSCRIPTION.
I'm using rails 3.2.1, and ruby 1.8.7
Thank you
Not sure I totally understand your description, but I'll take a shot.
Use the 'form_for' function to build your form, based on an instance of #default_subscription (established in the "new" action in your controller). If there are default values in #default_subscription, will show in the fields and the user can change them as they see fit. (this assumes your DefaultSubscription model has three attributes: sub1, sub2 and sub3.)
<%= form_for #default_subscription do |f|
<%= f.label :sub1 %><br />
<%= f.number_field :sub1 %>
<%= f.label :sub2 %><br />
<%= f.number_field :sub2 %>
<%= f.label :sub3 %><br />
<%= f.number_field :sub3 %>
<% end %>
When the user clicks the submit button the contents of the form with we assembled into a hash and passed into your controller's "update" action via params[]. You can extract the subscription hash like this:
user_subscription = params[:default_subscription]
At this point you have all the numbers that the user entered into the fields in the user_subscription hash. You can now parse the hash to extract the numbers, do your math, and then create the appropriate subscriptions per the user's input. (one note: the numbers could be strings and you might need to convert them back to integers as I've shown below.)
For example, to total all the subscription values and save that total into a user's subscription:
total = 0;
user_subscription.each do |key, value|
total += value.to_i
end
new_sub = current_user.subscription.new
new_sub.total = total
new_but.save
As I said, I don't understand your description clearly, so this might not be germane, but hope it is close to what you were looking for.
Good luck.
I have figured out one way, and reading my original post again, the whole thing is really confusing because I didn't know how to say exactly what I was trying to accomplish. I must say a lot of the reason I was confused is because the form I wanted did not correspond to any model, just an association count, which is ultimately a basic html form if you want to create a bunch of new objects without having their attributes yet. I'll first clarify then show my solution.
Clarification:
I have 2 Tables:
Subscription
Default_Subscription (Pre-Populated)
Subscription belongs_to Default_Subscriptions
Default_Subscription has_many Subscriptions
A User is subscribing to my website. This process is a step by step: not everything happens on the same page.
This all happens in a subscribe_controller. Each action corresponds to a step in the process.
One of the actions is default_subscriptions. This action lists the Default_Subscriptions a User can choose from, except they do not just choose, they can enter an amount for each type of Default_Subscription they'd like.
When the Default_Subscriptions are listed on the default_subscriptions page, I wanted a form with an html number input alongside each of these Default_Subscription. When the form is submitted via a next button, I had no idea how to gather the quantities from each html input and create an array of Subscription.new, with each Subscription's default_subscription_id corresponding to the proper Default_Subscription.
One Possible Solution:
def default_subscriptions
#def_subscriptions = Default_Subscription.all
end
Lets say the page I want proceed to after all the quantities are entered on the default_subscriptions page is review_subscriptions.
Here's what I did to create the proper form to proceed to the next action in the controller:
<%= form_tag( {:controller => 'subscribe', :action => 'review_subscriptions'}, :method => 'post' ) do %>
<ol>
<% #def_subscriptions.each do |ds| %>
<li>
<%= ds.name + ' ' %>
<%= number_field_tag("subscription_counts[#{ds.id}]") %>
</li>
<% end %>
</ol>
<%= submit_tag('Next') %>
<% end %>
The trick here is that string passed to the number_field_tag. By placing a single set of square brackets at the end of the string for a field_tag method parameter, the part before the brackets is the name of the hash, and the thing in the brackets is a key in the hash, and the submit button causes the corresponding value for each key to be the value of the field. Pretty cool!
The parameters passed to the next action would contain a hash called subscription_counts, and iterating through this hash would give a corresponding new subscription amount for each default_subscription_id. Like so:
def review_subscriptions
subscription_counts = params[:subscription_counts]
subscription_counts.each do |id, amount|
counter = Integer(amount)
until counter == 0
new_subscription = Subscription.new
new_subscription.default_subscription_id = Integer(id)
#subscriptions << new_subscription # #subscriptions is an instance variable
counter -= 1
end
end
end
I'd just like to point out, the more I work with them, the more I love them; I love Rails, and I love Ruby. They are super fun and classy. An until loop... how cool is that? If you have other solutions, now that my question is more obvious, please chime in! I know others out there are trying to find some slick ways to create multiple new objects in a one to many association with a single post call like this. Technically my objects aren't saved in the database yet, but that wouldn't be to hard now.
The main reference which helped me the most in reaching this solution was:
http://guides.rubyonrails.org/form_helpers.html
If you are new to rails, and confused about forms, read this. I feel like a master now. Rails devs are really good at documenting things!
I'm working on a small picture application. That I'm trying to do is build a counter to track how many times each image is clicked.
Right now I have in my view:
<% #galleries.each do |g| %>
<% for image in g.images %>
<div id="picture">
<%= render 'top_nav'%>
<%= link_to g.source, :target => true do %>
<%= image_tag image.file_url(:preview) %>
<% g.vote %>
<% end %>
<%= will_paginate(#galleries, :next_label => "Forward", :previous_label => "Previous") %>
</div>
Obviously this doesn't work, as the g.vote executes every time it's rendered, not clicked. Here's the vote method in my model:
def vote
self.increment!(:score)
end
I'm looking for a solution to run the vote method only when the image above is clicked. The links are to external resources only, not to a show action. Should I be building a controller action that's accepts a post, executes the vote, then redirects to the source?
Anyway, looking for some ideas, thanks.
I've done something similar, but keeping a count of how many times a Download link was clicked. This was awhile ago and I didn't know about Ajax at the time, but now I would recommend using jQuery (a great library in my opinion, but you could use something else) and do an Ajax call when the image is clicked that would execute some controller action which would increment that vote.
The other way, which is what I did in my scenario, and is what you talked about there, is creating a custom action in the controller that accepts a post. But I have to ask as well, does clicking on the image do something else in the behaviour of your website? For example, if when you click the picture, another random image is supposed to come up, that means you'll already have an action to load a new image and it be easy to stick the vote up in there before showing a new image. Otherwise you'd have to create the new controller action. If that's the case, the Ajax would be more efficient as the user wouldn't see a momentary flash as the page was refreshed (especially bad if the refresh time is long).