How to pass id from link_to to locals? - ruby-on-rails-3

I m passing the id from link_to to locals. But it is not able to find the result, saying cannot find article without id. I need to pass the id for display articles in modal.
<% #articles.each do |a|%>
<p>
<%= link_to a.title, {"data-toggle" => "modal", "data-target" => '#myModal', :id => a.id}%>
</p>
<% end %>
<div id="myModal" class="modal hide fade" tabindex="-1" role="dialog" aria labelledby="myModalLabel" aria-hidden="true">
<%= render :partial => "site#show", :locals => {#article => Article.find(params[:id])}%>
</div>
In the controller
def home
#articles = Article.order("created_at DESC").limit(5)
#videos = Video.order("created_at DESC").limit(5)
end
And i m getting the id from the controller. It is not nill.
The thing is i m able to find the articles in the block but not being able to send it to the locals.

this is because params[:id] is null or the record with params[:id] does not exist
In controller you should find where whether the article with the specific id exists if not handle it
#article = Article.find(params[:id])
good practise is to not use finders in views.

make sure that params[:id] is not nil and while passing locals you should always use symbols and access them as local variables in the partial
e.g.
"site#show", :locals => { :article => Article.find(params[:id])}%>
in _show.html.erb
use it as

Related

How to pass attribute name as a secondary parameter through link_to?

IN my application Controller
class FeedEntriesController < ApplicationController
def index
#search = FeedEntry.search(params[:search])
#feed_entries = #search.page(params[:page])
#app_keys = AppKey.all
end
def update
#feed_article = FeedEntry.find(params[:id])
#feed_article.toggle!(#saad)
redirect_to feed_entries_path
end
end
And in my Views/feed_entries/index.html.erb
<div id="content">
<%= paginate #feed_entries, :theme => 'twitter-bootstrap'%>
<p><%= render(#feed_entries) || 'There is no Feeds'%><p>
<%= paginate #feed_entries, :theme => 'twitter-bootstrap'%>
</div>
And in _feed_entry.html.erb partial.
<%= link_to "Make UnStar |", feed_entry_path(:id => feed_entry.id, #saad => :is_to_read), method: :put %> <%= link_to feed_entry.name, feed_entry.url %>
But the above code is not working, I just want to pass attribute name (:is_to_read) as a secondary parameter through link_to into the controller's update action.
:is_to_read is boolean attribute of feedentries table.
Please someone help me to resolve this issue.
A parameter is a pair of name and value. So it should be: :saad => ... and not #saad =>
Further you need to call the is_to_read attribute on the object. So:
<%= link_to "Make UnStar |", feed_entry_path(:id => feed_entry.id, :saad => #feedentry.is_to_read), method: :put %>

Adding fields to nested forms

So I have a model called City and it has_many :places and it accepts_nested_attributes_for :places. Each Place belongs_to :category. When I render a form for a City I have f.fields_for :places do |place| and I do it like this:
<% f.fields_for :places do |place| %>
<%= render "place_fields", :f => place
<% end %>
My _place_fields.html.erb contains the folowing:
<div class="place_header"><%= f.object.category.name %></div>
<div><%= f.label :name %>: <%=f.text_field :name %> </div>
<div><%= f.text_area :description %> </div>
But the problem apears when I try to add a new place. First of all I want to bring up a simple select form to select a category for the new place, and then render that same partial based on the category_id.
I do that inside the same action:
def add_place
if params[:category_id]
#place = Place.new(:category_id => params[:category_id])
respond_to do |format|
format.html { return nil }
format.js {
#here comes the render
}
end
else
render_message :header => "Choose category", :partial => "category_select", :over => 10
end
end
But if I try to do $("#places_tab").append("<%= escape_javascript(render :partial => "place_fields", :f => #place %>"); it gives an error, wich is expected.
Once again: I need to render the fields for that new Place and just don't know how to do that.
UPDATE
Received some advice on passing the original City Formbuilder to the action and rendering that Place right from that builder, but don't have any idea of how to do that.
The problem is that you are passing an instance of the Place model (#place) as the form builder instead of the form builder itself.

Rails link_to anchor within partial

In my Rails 3 app I have a link_to that takes the User from their profile page to the settings page. The settings page contains a form :partial for editing a profile. What I'd like to do is have the link_to in the Profile take the User to an anchor in the form partial. I've given it a go but am having trouble.
As my link_to I have:
<%= link_to('Visit the info section', settings_path, {:anchor => 'info'}) %>
Within settings_path is <%= render :partial => 'profiles/edit_settings_form' %>
Within that partial is:
<div class="infoBlock" anchor="#info">
</div>
I have a route for settings that I thought was causing the error:
match "/settings" => "settings#show", :as => 'settings'
I tried adding another match for the anchor to "profiles#edit_settings_form" but it didn't work. Any ideas?
<div class="infoBlock" anchor="#info">
is not proper way to do an anchor to a page. Correct syntax is something similar:
<a name="anchor"></a>
Now when you got to page with pagename#anchor, it redirects you to that block. For example:
<%= link_to "Block in home", :action => "home", :anchor => "anchor" %>

Ruby on Rails: Custom Actions (Follow up)

This question is a follow up to this previous question: Ruby on Rails: Custom actions
As a follow up, what would be the syntax to use a custom action in a form_for? For my app, I have a partial called _invite_form.html.erb, and set the form to have a :url specification that I thought would link the form to the invite action on the Users controller:
<div id = "invite_form">
<h1>Invite</h1>
<%= form_for(invited, :url => invite_user_path) do |f| %>
<div class="field">
<%= f.text_field :email, :class => "inputform round", :placeholder => "email" %>
</div>
<div class="actions">
<%= f.submit "Invite", :class => "submit_button round" %>
</div>
<% end %>
</div>
This partial is called on certain pages, and this error is given:
"No route matches {:action=>"invite", :controller=>"users"}"
In my routes.rb file I have included the appropriate lines:
resources :users do
member do
get :invite
post :invite
end
end
Why is it that the route doesn't work? How do I change these files to make the form use the action "Invite" on the Users controller?
** Forgot to mention earlier: I defined invited in the Users helper: users_helper.rb:
module UsersHelper
def invited
#invited = User.new(params[:user])
end
end
As you don't have a persistent User just yet, make this a collection operation by:
Changing invite_user_path to invite_users_path in your controller
Changing member do to collection do in your routes
invite_user_path expects a user as an argument. Try invite_user_path(invited). You will also need to save the user before you can compute a path to it.
To experiment, go into rails console and see the difference between the following:
app.invite_user_path
app.invite_user_path(User.first)
app.invite_user_path(User.new)

undefined method `to_key' for #<Class:0x17a6408> -rails-3

I am experiencing problems with undefined method `to_key' for, on a form for polymorphic upload.
This is the form partial:
<%= form_for [#parent, Upload], :html => { :multipart => true } do |f| %>
<div class="field">
<%= f.label :document %><br />
<%= f.file_field :document %>
</div>
<div class="actions">
<%= f.submit "Upload"%>
</div>
<% end %>
This is the controller:
class UploadsController < ApplicationController
before_filter :find_parent
respond_to :html, :js
def index
#uploads = #parent.uploads.all unless #uploads.blank?
respond_with([#parent, #uploads])
end
def new
#upload = #parent.uploads.new unless #uploads.blank?
end
def show
#upload = #parent.upload.find(params[:upload_id])
end
def create
# Associate the correct MIME type for the file since Flash will change it
if params[:Filedata]
#upload.document = params[:Filedata]
#upload.content_type = MIME::Types.type_for(#upload.original_filename).to_s
#upload = #parent.uploads.build(params[:upload])
if #upload.save
flash[:notice] = "suceessfully saved upload"
redirect_to [#parent, :uploads]
else
render :action => 'new'
end
end
end
def edit
#upload = Upload.where(params[:id])
end
private
def find_parent
classes ||= []
params.each do |name ,value|
if name =~ /(.*?)_id/
#parent = classes << $1.pluralize.classify.constantize.find(value)
end
end
return unless classes.blank?
end
end
If i change
<%= form_for [#parent, Upload], :html => { :multipart => true } do |f| %>
to
<%= form_for [parent, Upload], :html => { :multipart => true } do |f| %>
I get a new error: undefined local variable or method `parent' for #<#:0x21a30e0>
This is the error trace:
ActionView::Template::Error (undefined method `to_key' for #<Class:0x2205e88>):
1: <%= render :partial => "uploads/uploadify" %>
2:
3: <%= form_for [#parent, Upload], :html => { :multipart => true } do |f| %>
4:
5:
6: <div class="field">
The "uploads/uploadify" partial is in this gist: https://gist.github.com/911635
Any pointers will be helpful. Thanks
From what I can see, your form_for should be something along the lines of
<%= form_for [#parent, #upload], :html => { :multipart => true } do |f| %>
as I'm assuming your upload object is nested within another object, similar to the following:
resources :posts do
resources :uploads
end
What form_for does when passed an array like this is construct the relevant path based on the class of the given objects and whether they are new records.
In your case, you create a new upload object in the new action of your controller, so form_for will inspect the array, get the class and id of #parent, then get the class and id of #upload. However, because #upload has no id, it will POST to /parent_class/parent_id/upload instead of PUTting to parent_class/parent_id/upload/upload_id.
Let me know if that doesn't work and we'll figure it out further :)
-- EDIT - after comments --
This means that one of #parent or #upload is nil. To check, you can put the following in your view
<%= debug #parent %>
and the same for #upload and see which is nil. However, I'm guessing that #upload is nil, because of this line in your controller:
# UploadsController#new
#upload = #parent.uploads.new unless #uploads.blank?
specifically the unless #uploads.blank? part. Unless you initialize it in the ApplicationController, #uploads is always nil, which means #uploads.blank? will always be true, which in turn means #upload will never be initialized. Change the line to read
#upload = #parent.uploads.new
and the problem will hopefully be resolved. The same is true of the other methods where you have used unless #uploads.blank?.
On a semi-related note, in UploadsController#find_parent, you have this line
classes ||= []
because the variable is local to the find_parent method, you can be assured that it is not initialized, and should rather write classes = [].
Also, you have this line of code
return unless classes.blank?
right before the end of the method. Did you add that in so that you return from the method once #parent has been initialized? If so, that line should be inside the each block.
Further, since classes isn't used outside of the method, why define it at all? The code could read as follows and still have the same behaviour
def find_parent
params.each do |name ,value|
#parent = $1.pluralize.classify.constantize.find(value) if name =~ /(.*?)_id/
return if #parent
end
end
Amongst other things, you'll see that this does a few things:
Avoids initializing a variable that is not needed.
Inlines the if statement, which helps readability for single line conditionals
Changes use of unless variable.blank to if variable. Unless your variable is a boolean, this accomplishes the same thing, but reduces the cognitive load, as the former is essentially a double negative which your brain has to parse.
-- EDIT - from email exchange about the issue --
You are correct - if #parent will return true if parent is initialized. As I mentioned on SO however, the exception to this is if #parent is initialized and set to false. Essentially what it means is that in Ruby, all values except nil and false are considered true. When an instance variable has not been initialized, it's default value is nil, which is why that line of code works. Does that make sense?
In terms of setting #parent in each action that renders form in the UsersController, which of these is the correct way to do this on the index action. I have tried all 3 but got errors
Remember that both #parent and #upload must be instances of ActiveRecord (AR) objects. In the first case, you set #parent to User.all, which is an array of AR objects, which will not work. Also, you try to call #parent.uploads before #parent is initialized, which will give a no method error. However, even if you were to swap the two lines around, you are calling #parent.uploads when parent is an array. Remember that the uploads method is defined on individual AR objects, and not on an array of them. Since all three of your implementations of index do similar things, the above caveats apply to all of them in various forms.
users_controller.rb
def index
#upload = #parent.uploads
#parent = #user = User.all
end
or
def index
# #user = #parent.user.all
#parent = #user = User.all
end
or
def index
#parent = #upload = #parent.uploads
#users = User.all
end
I'll quickly walk you through the changes I made. Before I start, I should explain that this
<%= render "partial_name", :variable1 => a_variable, :variable2 => another_variable %>
is equivalent to doing this
<%= render :partial => "partial_name", :locals => {:variable1 => a_variable, :variable2 => another_variable} %>
and is just a shorter (and somewhat cleaner) way of rendering. Likewise, in a controller, you can do
render "new"
instead of
render :action => "new"
You can read more about this at http://guides.rubyonrails.org/layouts_and_rendering.html Now on to the code.
#app/views/users/_form.html.erb
<%= render :partial => "uploads/uploadify" %>
<%= form_for [parent, upload], :html => { :multipart => true } do |f| %>
<div class="field">
<%= f.label :document %><br />
<%= f.file_field :document %>
</div>
<div class="actions">
<%= f.submit "Upload"%>
</div>
<%end%>
On the uploads form, you'll see that I changed #parent and #upload to parent and upload. This means you need to pass the variables in when you render the form instead of the form looking for instance variable set by the controller. You'll see that this allows us to do the following:
#app/views/users/index.html.erb
<h1>Users</h1>
<table>
<% #users.each do |user| %>
<tr>
<td><%= link_to user.email %></td>
<td><%= render 'uploads/form', :parent => user, :upload => user.uploads.new %></td>
</tr>
<% end %>
</table>
Add an upload form for each user in UsersController#index. You'll notice that because we now explicitly pass in parent and upload, we can have multiple upload forms on the same page. This is a much cleaner and more extensible approach to embedding partials, as it becomes immediately obvious what parent and upload are being set to. With the instance variable approach, people unfamiliar with the code base might struggle to determine where #parent and #upload are being set, etc.
#app/views/users/show.html.erb
<div>
<% #user.email %>
<h3 id="photos_count"><%= pluralize(#user.uploads.size, "Photo")%></h3>
<div id="uploads">
<%= image_tag #user.upload.document.url(:small)%>
<em>on <%= #user.upload.created_at.strftime('%b %d, %Y at %H:%M') %></em>
</div>
<h3>Upload a Photo</h3>
<%= render "upload/form", :parent => #user, :upload => user.uploads.new %>
</div>
This is similar to the changes above, where we pass in the parent and upload objects.
#config/routes.rb
Uploader::Application.routes.draw do
resources :users do
resources :uploads
end
devise_for :users
resources :posts do
resources :uploads
end
root :to => 'users#index'
end
You'll see that I removed uploads as a top level resources in the routes. This is because uploads requires a parent of some sort, and so cannot be top level.
#app/views/uploads/new.html.erb
<%= render 'form', :parent => #parent, :upload => #upload %>
I made the same changes as above, passing parent and upload through explicitly. You'll obviously need to do this wherever you render the form.
#app/controllers/users_controller.rb
class UsersController < ApplicationController
respond_to :html, :js
def index
#users = User.all
end
def show
#user = User.find(params[:id])
end
def new
#user = User.new
end
def create
#user = User.new(params[:user])
if #user.save
redirect_to users_path
else
render :action => 'new'
end
end
def update
#user = User.find_by_id(params[:id])
#user.update_attributes(params[:user])
respond_with(#user)
end
def destroy
#user = User.find_by_id(params[:id])
#user.destroy
respond_with(#user)
end
end
I've removed any mention of #parent from the user controller, as we pass it through explicitly.
Hopefully that all makes sense. You can extrapolate from these examples and pass through the parent and upload object wherever you want to render an upload form.
[#parent, Upload] => [#parent, :upload]
<%= form_for [#parent, :upload], :html => { :multipart => true } do |f| %>
UPD
You should change places :upload and #parent
<%= form_for [:upload, #parent], :html => { :multipart => true } do |f| %>