Passing parsed variables into different models form - ruby-on-rails

I have a products scaffold set up, and I just created a new foo controller and view. In my foo controller I parse a url, and get back an arry of objects. How can I pass each of these variables into the products form as defaults?
my controller:
require 'net/http'
require 'json'
def index
if params[:commit] == "Add Product"
#productId = params[:q]
#resultsReceived = true
if
url = URI.parse("url" + params[:q].to_s)
#response = JSON.parse(Net::HTTP.get_response(url).body)
end
else
#resultsReceived = false
#response = []
end
respond_to do |format|
format.html
end
end
end
My current index
<%= form_tag("/foo", :method => "get") do %>
<%= label_tag(:q, "Enter Product Number") %>
<%= text_field_tag(:q) %>
<%= submit_tag("Add Product") %>
<% end %>
<% if #resultsReceived == true %>
Title: <%= #response["product"]["title"] %> </br>
ID_Str: <%= #response["product"]["id_str"] %> </br>
Image Url: <%= #response["product"]["image_url"] %> </br>
Base Item Price: <%= #response["product"]["base_item_price"] %> </br>
Current Item Price: <%= #response["product"]["price"] %> </br>
Seller Name: <%= #response["product"]["mp_seller_name"] %> </br>
Description: <%= #response["product"]["descr"] %> </br>
<% end %>
I want the variable above to be passed to my already existing products from.

I think you have to move the access of the other data from your index action to either your your new action, or an action you create 'new_prefilled' possibly. It would helpful if the attributes matched (the stuff you get from the url has the same attribute names as your product model)
i.e.
def new_prefilled
if url = URI.parse("url" + params[:oid].to_s)
#response = JSON.parse(Net::HTTP.get_response(url).body)
end
#product = Product.new(#response['product'])
render 'new'
end
Then you'd have to add a route,
get '/products/new/:oid' => 'products#new_prefilled'
Then in your index action, you would do this:
if params[:commit] == "Add Product"
redirect_to "/products/new/#{params[:q]}"
end
So you would render your new products view, but it would be pre-filled with that data you got from the other site.

Related

Rails: Get params from click

I'm working on a Pinterest-like app to learn Rails, where users can collect items and add them to collections.
I have an "Add to collection" button which opens a modal with a list of all collections of a user. This works fine on individual item show pages, but not on my item index page (which lists all items that have been posted).
This is my collections controller:
def index
#item = Item.find(params[:item_id])
#selected_collections = selected_collections(#item.id)
#collections = current_user.collections
respond_to do |format|
format.js {
render
}
end
end
My items controller:
def index
#items = Item.all.order(created_at: :desc).paginate(:page => params[:page], :per_page => 20)
#collections = current_user.collections
#selected_collections = selected_collections(item.id)
end
def show
#item = Item.find_by_id(params[:id])
return render_not_found if #item.blank?
#collections = current_user.collections
#selected_collections = selected_collections(#item.id)
end
This is my Add-to-collection button, which should be shown for each item on the index:
<%= link_to "+ Add", item_collections_path(item, format: :js), class: 'btn btn-secondary btn-30', data: {toggle: 'modal', target: '#CollectionModal'}, remote: true, format: :js %>
And part of the corresponding modal:
<div class="modal-body">
<%= render 'collections/collections_list', :collections=>#collections, :selected_collections=>#selected_collections %>
</div>
_collection_list.html.erb
<div id="collection_list">
<% #collections.each do |collection| %>
<%= render 'collections/collection_checkbox', collection: collection, selected_collections: selected_collections %>
<% end %>
<%= render 'collections/collection_checkbox', collection: Collection.new, selected_collections: selected_collections %>
<div style='display: none;' class="new_collection_template">
<%= render 'collections/collection_checkbox', collection: Collection.new, selected_collections: selected_collections %>
</div>
_collection_checkbox.html.erb
<div class="form-check">
" <%= 'checked' if selected_collections.include?(collection.id) %> <%= "disabled" unless collection.id %>>
<%= collection.collection_name %>
<% if !collection.id %>
<input type="text" class="collection-name" placeholder="New collection" />
<% end %>
On my index I get the following error message:
undefined local variable or method `item' for
ItemsController:0x007f69022aefa0 Did you mean? item_url items_url item_path #items
Screenshot of Error Message
I assume that this is because I don't have individual #item defined on my index. On the item show pages I can simply find the params in the URL. But how can I solve this problem on the index?
Thanks for your help, much appreciated :)

Like button likes all posts

I've implemented a like/unlike function in my app. On the show page for an item, you can like/unlike without any issues. On a user's profile page though, where it lists all the foods they've uploaded, I have two issues: First, when I click the like button for one food, it triggers it for every food and then the button text under every food says "unlike this food", instead of only having the button text change for just the food that was clicked on. The like gets saved to the db for the correct food, but obviously I don't want the button text to change for foods that I haven't liked. Second, when I try to like a different food on the page without refreshing, the liked gets saved in the db as the original food that I clicked on, instead of the one I actually clicked on.
users.show.html.erb
<% #user.foods.each do |food| %>
<div class="row col-md-12">
<div class="food col-md-4">
<h3 class="title"><%= link_to food.title.capitalize, food_path(food.id) %></h3>
<%= link_to (image_tag (food.image.url)), food_path(food.id) %>
<%= render 'shared/vote_form', :food => food %>
<%= link_to pluralize(food.votes.count, "person"), user_votes_path(:user_id => current_user.id, :food_id => food.id) %> <%= food.votes.count == 1 ? 'wants' : 'want' %> to gobble this
</div>
<div class="description col-md-6">
<% if #user.id == current_user.id %>
<% if food.description.length == 0 %>
<p><%= link_to "Add Description", edit_food_path(food) %></p>
<% else %>
<p><%= food.description %><p>
<p><%= link_to "Edit Description", edit_food_path(food) %></p>
<% end %>
<% if food.recipe.length == 0 %>
<p><%= link_to "Add Recipe", edit_food_path(food) %></p>
<% end %>
<% else %>
<% if food.description.length == 0 %>
<p>No Description</p>
<% else %>
<p><%= food.description %><p>
<% end %>
<% if food.recipe.length == 0 %>
<p>No Recipe</p>
<% else %>
<p><%= link_to "View Recipe", food_path(food) %></p>
<% end %>
<% end %>
</div>
</div>
<% end %>
votes controller
class VotesController < ApplicationController
def index
#votes = Food.find(params[:food_id]).votes
end
def new
#vote = current_user.votes.new
end
def create
#user = current_user
#vote = current_user.votes.build(vote_params)
if #vote.save!
#food = #vote.food
respond_to do |format|
format.html {redirect_to :back, notice: "Liked!"}
format.js
end
puts #vote.food.id
else
puts "No"
redirect_back(fallback_location: root_path)
end
end
def show
#user = current_user
#vote = Vote.find(params[:id])
end
def destroy
#vote = Vote.find(params[:id])
#food = #vote.food
if #vote.destroy!
respond_to do |format|
format.html {redirect_to :back, notice: "Unliked!"}
format.js
end
else
puts "NOOOOOO"
end
end
private
def vote_params
params.require(:vote).permit(:food_id)
end
end
vote partial
<% unless current_user.votes.pluck(:food_id).include?(food.id) %>
<%= button_to "Like This Food", user_votes_path(current_user, { vote: { food_id: food.id } }), :remote => true %>
<% else %>
<% vote = food.votes.where(user_id: current_user.id).first %>
<%= button_to "Unlike This Food", user_vote_path(current_user, vote), :remote => true, method: "delete" %>
<% end %>
create.js.erb
$('.button_to').replaceWith("<%= j (render :partial => 'shared/vote_form', :locals => { :food => #food, :food_id => #food.id }) %>");
destroy.js.erb
$('.button_to').replaceWith("<%= j (render :partial => 'shared/vote_form', :locals => { :food => #food }) %>");
Take a look at your create.js.erb (destroy.js.erb has the same problem). You use select all elements with the class button_to - that is all buttons on the page. Then you call replaceWith that replaces all these buttons with the button provided as the argument. The result is that all buttons get replaced with the like button corresponding to the food you just liked. This is responsible for affecting all buttons on the page and making them refer the food you liked first.
The solution is to add an id attribute to the buttons that would include the ID of the food the button pertains to. For example, if Food with ID 1 is Lettuce then you need to add id="like-1" to the button and change $('.button_to') to $("#like-1").

Ruby Problems Displaying Variables From New View, but Not Edit

In my app I have a quiz in a partial because I need the same (lengthy) quiz for my new and for my edit view. In the header of the application.html.erb and on my users#show page I display the results from this quiz, once it is taken, using an if/else statement. For some reason, after a user takes the quiz in the new view it is registering that the quiz has been completed (you can tell by the if/else statement) but not displaying the variables. However, using the edit view (that calls the same partial _quiz.html.erb) the variables display perfectly.
My new view calls the partial like this:
<%= render partial: "quiz", locals: { url: swing_books_path, method: :post } %>
While my edit view calls it like this:
<%= render partial: "quiz", locals: { url: edit_swing_book_path(#swing_book), method: :put } %>
The partial looks like this:
<%= form_for #swing_book do |f| %>
<div class="clearfix">
<h1 class="text-center">
<%= f.text_field :swing01, class: 'form-control input-1' %>
<%= f.text_field :swing02, class: 'form-control input-1' %>
<%= f.text_field :swing03, class: 'form-control input-1' %>
-
<%= f.text_field :swing04, class: 'form-control input-1' %>
<%= f.text_field :swing05, class: 'form-control input-1' %>
<%= f.text_field :swing06, class: 'form-control input-1' %>
-
<%= f.text_field :swing07, class: 'form-control input-1' %>
<%= f.text_field :swing08, class: 'form-control input-1' %>
<%= f.text_field :swing09, class: 'form-control input-1' %>
<%= f.text_field :swing10, class: 'form-control input-1' %>
</h1>
<div class="form-group text-center">
<%= f.submit "Submit Swing Code", class: 'btn btn-success' %>
</div>
</div>
<% end %>
However, I use these exact same details (with just the variable names changed) on another quiz that is not having the same issue. (I have three other similar quizzes all of which display perfectly from both new and edit views.)
My model looks like this:
class SwingBook < ActiveRecord::Base
before_save :set_swing_code
def set_swing_code
self.swing_code = "#{self.swing01}#{self.swing02}#{self.swing03}-#{self.swing04}#{self.swing05}#{self.swing06}-#{self.swing07}#{self.swing08}#{self.swing09}#{self.swing10}"
end
belongs_to :user
validates :user, presence: true
end
My controller looks like this:
class SwingBooksController < ApplicationController
before_action :require_sign_in
def show
#swing_book = SwingBook.find(params[:id])
end
def new
#swing_book = current_user.build_swing_book
end
def create
#swing_book = SwingBook.new
#swing_book.swing_code = params[:swing_book][:swing01]
#swing_book.swing_code = params[:swing_book][:swing02]
#swing_book.swing_code = params[:swing_book][:swing03]
#swing_book.swing_code = params[:swing_book][:swing04]
#swing_book.swing_code = params[:swing_book][:swing05]
#swing_book.swing_code = params[:swing_book][:swing06]
#swing_book.swing_code = params[:swing_book][:swing07]
#swing_book.swing_code = params[:swing_book][:swing08]
#swing_book.swing_code = params[:swing_book][:swing09]
#swing_book.swing_code = params[:swing_book][:swing10]
#swing_book.swing_code = params[:swing_book][:swing_code]
#swing_book.user = current_user
if #swing_book.save
flash[:notice] = "Your swing code was saved successfully."
redirect_to user_path(current_user)
else
flash[:alert] = "Sorry, your results failed to save."
redirect_to welcome_index_path
end
end
def edit
#swing_book = SwingBook.find(params[:id])
end
def update
#swing_book = SwingBook.find(params[:id])
#swing_book.assign_attributes(swing_book_params)
if #swing_book.save
flash[:notice] = "Results were updated successfully."
redirect_to user_path(current_user)
else
flash.now[:alert] = "There was an error saving your results. Please try again."
redirect_to welcome_index_path
end
end
private
def swing_book_params
params.require(:swing_book).permit(:swing01, :swing02, :swing03, :swing04, :swing05, :swing06, :swing07, :swing08, :swing09, :swing10, :swing_code)
end
end
Finally, here's the logic I'm using to display these variables in the application.html.erb file:
<% if current_user.swing_book != nil %>
<p>ID Code:
<%= current_user.swing_book.swing01 %>
<%= current_user.swing_book.swing02 %>
<%= current_user.swing_book.swing03 %>
-
<%= current_user.swing_book.swing04 %>
<%= current_user.swing_book.swing05 %>
<%= current_user.swing_book.swing06 %>
-
<%= current_user.swing_book.swing07 %>
<%= current_user.swing_book.swing08 %>
<%= current_user.swing_book.swing09 %>
<%= current_user.swing_book.swing10 %></p>
<% end %>
And on the user#show page:
<% if #user.swing_book == nil %>
<h3>ID Code: ???-???-????<%= %></h3>
<p><%= link_to "Get Your ID Code Now!", new_swing_book_path %></p>
<% else %>
<h3>ID Code:
<%= #user.swing_book.swing01 %>
<%= #user.swing_book.swing02 %>
<%= #user.swing_book.swing03 %>
-
<%= #user.swing_book.swing04 %>
<%= #user.swing_book.swing05 %>
<%= #user.swing_book.swing06 %>
-
<%= #user.swing_book.swing07 %>
<%= #user.swing_book.swing08 %>
<%= #user.swing_book.swing09 %>
<%= #user.swing_book.swing10 %></h3>
<p><%= link_to "Update Your Code", edit_swing_book_path(#user.swing_book) %></p>
<% end %>
I originally tried displaying just the swing_code variable (as established in the method, but it was displaying a single integer so I switched to the code seen above. Ideally, I would like to display it as just the swing_code variable, but the (somewhat repetitive) workaround I'm currently using works fine-ish if I could only get the new/edit display issue worked out.
Any ideas as to what could be going wrong? I can't find any differences between this and the other quizzes that could be causing a problem!
In your create method, you have a block of code that explicitly sets the #swing_book values. This code is setting all of the values from the form into just the swing_code member. Since the last assignment is also from the swing_code param to the swing_code member, it's hiding the mistake. Here's the code with the correct member fields assigned:
#swing_book.swing01 = params[:swing_book][:swing01]
#swing_book.swing02 = params[:swing_book][:swing02]
#swing_book.swing03 = params[:swing_book][:swing03]
#swing_book.swing04 = params[:swing_book][:swing04]
#swing_book.swing05 = params[:swing_book][:swing05]
#swing_book.swing06 = params[:swing_book][:swing06]
#swing_book.swing07 = params[:swing_book][:swing07]
#swing_book.swing08 = params[:swing_book][:swing08]
#swing_book.swing09 = params[:swing_book][:swing09]
#swing_book.swing10 = params[:swing_book][:swing10]
#swing_book.swing_code = params[:swing_book][:swing_code]
Now, while that will fix the immediate problem, you probably just want to use the same approach as the update method. Try this for your create method, instead:
def create
#swing_book = SwingBook.new
#swing_book.assign_attributes(swing_book_params)
#swing_book.user = current_user
if #swing_book.save
flash[:notice] = "Your swing code was saved successfully."
redirect_to user_path(current_user)
else
flash[:alert] = "Sorry, your results failed to save."
redirect_to welcome_index_path
end
end
However, you have an opportunity to minimize the overall code to help eliminate errors like this. Consider something like this for your controller code:
class SwingBooksController < ApplicationController
before_action :require_sign_in
def show
#swing_book = SwingBook.find(params[:id])
end
def new
#swing_book = current_user.build_swing_book
end
def edit
#swing_book = SwingBook.find(params[:id])
end
def create
#swing_book = SwingBook.new
apply_form_values
end
def update
#swing_book = SwingBook.find(params[:id])
apply_form_values
end
private
def apply_form_values
#swing_book.assign_attributes(swing_book_params)
if #swing_book.save
flash[:notice] = "Results were updated successfully."
redirect_to user_path(current_user)
else
flash.now[:alert] = "There was an error saving your results. Please try again."
redirect_to welcome_index_path
end
end
def swing_book_params
params.require(:swing_book).permit(:swing01, :swing02, :swing03, :swing04, :swing05, :swing06, :swing07, :swing08, :swing09, :swing10, :swing_code)
end
end
You can use techniques like this to make it easier to write your initial code, and make it far easier to maintain.

First argument in form cannot contain nil or be empty form_for :method=>"get"

I'm getting an error "First argument in form cannot contain nil or be empty" for
<%= form_for #assessment, :method=>"get", :url => url_for(:action => 'new') do |f| %>
I get the error when re-rendering the :options page after an invalid submission. What's wrong with the way I've set this up?
Step 1: User needs to choose a template and a patient_id in Assessment#Options
def options
#assessment = current_user.assessments.new
#patients = current_user.patients.sort_by{|e| e.first_name.downcase}
#patient = current_user.patients.new
#templates = Template.all
end
Assessment#options view:
<%= form_for #assessment, :method=>"get", :url => url_for(:action => 'new') do |f| %>
<% if #assessment.errors.any? %>
<div>
<%= pluralize(#assessment.errors.count, "error") %> prevented the account from being created:
<ul>
<% #assessment.errors.full_messages.each do |msg| %>
<li><%= msg %></li>
<% end %>
</ul>
</div>
<% end %>
...
<%= f.submit "Create Assessment", :class => "btn btn-primary", :style=>"width:100%;", :id=>"options-submit" %>
<% end %>
On submit, I GET this action:
def new
if params[:assessment][:template_id].present? && params[:assessment][:patient_id].present?
#assessment = current_user.assessments.new
#assessment.template = Template.find(params[:template_id])
#assessment.patient = current_user.patients.find(params[:patient_id])
else
render :options
end
end
You need #assessment to be instantiated. You can move it outside the condition.
def new
#assessment = current_user.assessments.new
if params[:assessment][:template_id].present? && params[:assessment][:patient_id].present?
#assessment.template = Template.find(params[:template_id])
#assessment.patient = current_user.patients.find(params[:patient_id])
else
render :options
end
end
The line
render :options
does not execute the action options in your controller. It only renders the view using the variables in the new action. As a result none of your instance variables are actually being being set, so your form craps out.
Try moving the stuff in the options method into your if-then loop.

Creating multiple Ajax buttons with a each loop

I have two models : User and Product. One user has many owned_products, and a product belongs to an owner. The products have a available:boolean.
I want to make a list of owned_products that can be toggled from available to unavailable with a button. Here is what I did, using the M. Hartl example :
app/views/shared/_owned_products_list.html.erb
<ol class="products">
<% #owned_products.each do |product| %>
<%= link_to(product.name, product) %>
<%= render 'products/available_form', product: product %>
<% end %>
</ol>
app/views/products/_available_form.html.erb
<div id="available_button_<%=product.id%>">
<% if product.available? %>
<%= form_for(product, remote: true) do |f| %>
<div><%= f.hidden_field :available, value: nil %></div>
<%= f.submit t('product.available.undo'), class: "btn btn-small" %>
<% end %>
<% else %>
<%= form_for(product, remote: true) do |f| %>
<div><%= f.hidden_field :available, value: true %></div>
<%= f.submit t('product.available.do'), class: "btn btn-primary btn-small" %>
<% end %>
<% end %>
</div>
app/controllers/products_controller.rb
.
.
.
def update
#product = Product.find(params[:id])
if #product.update_attributes(product_params)
respond_to do |format|
format.html do
flash[:success] = t('flash.success.product.update')
redirect_to #product
end
format.js
end
else
render 'edit'
end
end
.
.
.
app/views/products/update.js.erb
$("#available_form_<%=#product.id%>").html("<%= escape_javascript(render('available_button', product: #product)) %>")
But it doesn't work : The available button is not refreshed at all :
When I click on the available (or unavailable) button, nothing change. If I refresh the whole page, it toggles, regardless the number of clicks...
Do you know where I failed ?
EDIT
OK, I got it, it was a dumb fault : my available_form id was available_button_<%=#product.id%> and not available_form_<%=#product.id%>...
So here is the right one :
app/views/products/update.js.erb
$("#available_button_<%=#product.id%>").html("<%= escape_javascript(render('available_button', product: #product)) %>")
I assume that you're using rails with jquery and jquery_ujs, make it simple:
in app/views/shared/_owned_products_list.html.erb
<ol class="products">
<% #owned_products.each do |product| %>
<%= link_to product.name, product %>
<%= link_to product.available ? 'turn off' : 'turn on', '#', :class => 'toggle-availability', :'data-id' => product.id %>
<% end %>
</ol>
in your js-script file:
$.ajaxSetup({
headers: {
'X-CSRF-Token': $('meta[name="csrf-token"]').attr('content')
}
});
$(document).on('click', '.toggle-availability', function() {
var el = $(this);
var status = el.text();
$.post('/products/toggle', { id: el.data('id') }, function() {
el.text(status == 'turn off' ? 'turn on' : 'turn off');
});
return false;
});
in the controller file:
def toggle
if request.xhr?
product = Product.find(params[:id])
# note here that you should also check the owner of the product
product.available = product.available.nil? ? true : false
product.save
end
render :nothing => true
end
and in your routes.rb add:
resources :products do
collection do
get :toggle
end
end

Resources