My controller has two methods: index and search.
def index: renders the default view, index.html.erb, and displays a default map using YM4R/GM.
def search: renders the default view using render index, but uses form input to display a custom map using YM4R/GM.
The view, index.html.erb, contains a partial view, _form.html.erb, and the #map.div for YM4R/GM.
The map displays fine when rendered through the default, but when submitted to the search method I get the error:Template is missing. Missing template ym4r/gm_plugin/variables/_variable.erb in view path app/views.
What's weird is that I had this working yesterday until I started jacking around with trying to make my form submit using form_remote_tag. I started getting this error with that type of call so now I've changed everything back (I think), but I'm still getting the error. I think there's something I don't understand about how Rails renders views.
As you've pointed out here, there's an important difference between a method and a string.
# Call the index method, then render the result, whatever that is. Does not work as
# expected.
render(:action => index)
# Render using the index template
render(:action => 'index')
Ruby is really relaxed when it comes to calling methods so there's no requirement to use brackets as is the case with others such as JavaScript or Perl.
Related
I am working on a project which involves understanding the mechanism of how rails does rendering and layouts, including, where does rails know that it needs to do rendering (either by a render statement or default rendering), which view file it is going to render, how is view file nested together, how does rails merge the layouts with the view file, how does rails decide which layout file to use for a specific render statement. I hope there are some existing code module I can use to produce such rendering results, so I don't have to write code to mimic this process myself.
Backing what the Tin Man said, I'm gonna do my best to answer this as 'How does rendering views work'
First you create a controller....
rails g controller welcome index
This creates the welcome controller with the action index
By action, I mean if you look inside the new welcome controller you will see
def index
end
That's where all the variables and stuff go that you need inside of that 'page' for example if you need to print out all the users, you would do this
def index
#users = User.all
end
Now, inside of /config/routes.rb, when you typed in rails g controller welcome index it generated a route something like this
get '/welcome/index' => 'welcome#index'
What that does, is when someone goes to the URL http://localhost:3000/welcome/index Rails will point to the welcome controller and render the index action
What that will do is tell rails, 'HEY, theres an action called index, go look in /views/welcome and make sure there's a file called index.html.erb
That WILL be there because when you generated the controller, it generates the views for the actions you specify, in this case we only specified index.
Now Rails knows what controller to look in and what action and html to render.
That's pretty much the basics :)
Hope this helps!
I have a rails 3.2 application that recently I added mobile-fu gem to, in order to add separate mobile views.
There were a few hiccups but, for the most part, it works wonderfully.
However, I've only made mobile views for a handful of pages. When I attempt to go to a page that does not have a mobile view, from a mobile device, I get:
Missing template after_hour_it_supports/index, application/index with {:locale=>[:en], :formats=>[:mobile], :handlers=>[:erb, :builder, :prawn, :prawn_dsl]}
Which is what I would expect.
However, it is necessary to display some pages which might never have mobile counterparts. Specifically, there is a page to approve something. The thing that they are approving might never have a mobile view made for it, but the approval page already has a mobile view. My current approach, is to use an iframe to display the possibly non-mobile view. Of course, if a mobile view exists, I would prefer to use it over the non-mobile view (still in the iframe).
So what I would like to do is to attempt to render template with the :mobile format, but if the mobile format does not exist, to render with the :html format, which seems like something rails already does based on the :formats array mentioned in the MissingTemplate exception. I can find some documentation on how to set the :formats array when calling render, but I would like to do this automatically, without having to modify every existing response.
How do I modify :formats=>[:mobile] to be :formats=>[:mobile, :html] on an application level?
I finally found an answer which seems to work for me (the answer by Will Madden).
Specifically, I used his suggestion to override the formats= in my ApplicationController. Which he says is the same way that rails already adds this exact same functionality for the :js format.
The specific method he writes in his answer looks like this:
class ApplicationController
...
def formats=(values)
values << :html if values == [:mobile]
super(values)
end
...
end
This is by far the most elegant solution I have found for my circumstances. However, it fails whenever the controller for the page in question contains a respond_to block for the current action.
I have a page with a bunch of widgets that has content that is primarily rendered by the CustomerController. However, there is this other controller called the DigitalItemController that is used to process business logic regarding changes to a customer's item. I have a form that is used to upload a file that is to be handled by a method in DigitalItemController via POST. The form is located in a partial that is named :customer_assets because it contains customer files they have uploaded. This same widget has a table of the files already uploaded with links to them.
The problem I am having is that since CustomerController usually does the rendering, it renders my partial just fine with no complaints. However, when I have the user submit a file for uploading, it calls a method in my DigitalItemController, where the last line of it says to render the partial. When it hits that line, I get an ActionController::RoutingError as it tries to render the partial. The line it doesn't like is:
%td= link_to(item_file.storage_info.s3_key,
url_for(:controller => :data,
:action => :get_storage_information_link,
:digital_item_id => item.digital_item_id,
:digital_storage_id => item_file.digital_storage_id))
I didn't think there would be a problem since the DataController is explicitly used here. It should know I want to just re-render the partial like it did before. However, I get this instead:
No route matches {:digital_storage_id=>#####, :action=>"get_storage_information_link", :digital_item_id=>#######, :controller=>"data"}
Obviously ID numbers here have been censored out, just in case. All I need to do is have that method in the DigitalItemController handle the file upload and then re-render the partial so that the widget shows the correct and up-to-date data. It just seems odd to me that it renders just fine when it is rendered normally, but as soon as the file is submitted, it throws routing errors.
What is the way to fix this?
In my current project I have a couple instances where I have a re-usable form that exists inside a rails partial. This form submits to a specific controller via ajax (:remote => true). The controller does some stuff and then returns back the appropriate js.erb to modify the page via javascript.
This works fine for when I have a single view. But the problem seems to happen when this re-usable partial exists on multiple views. In view 1 I might want to issue a completely different set of javascript commands then in view 2.
As a concrete example, say I have a comments controller that has the normal CRUD operations.
I now have partial called _comments_box.erb. This _comments_box.erb contains the ability to submit a comment via a simple line:
- form_for comment, :url => post_comments_path(post), :remote => true do |f|
This submits to a comments_controller.rb create method which looks somethings like this:
def create
... do some stuff, like create a new comments model
respond_to do |format|
# will respond with create.js.erb
format.js
end
end
The create.js.erb in turn adds a comment to the view, perhaps doing a bunch of other updates to the DOM.
Say I render the _comments_box.erb within a view called post_summary.erb. Now I have another view, post_detail.erb that requires the same _comments_box.erb. However the post_detail.erb requires me to update completely different divs on the DOM in response to a new comment.
I need to create a different JS response for each instantiation. So I can either:
Create an alternate controller method, say create_2. Pass in some parameter to the _comments_box.erb from post_detail.erb to the _comments_box.erb partial so it knows which controller method to fire. This will allow me to have a separate file _create_2.js.erb that will allow me to manipulate the post_detail.erb view independently.
Forget about using js.erb altogether and just use plain old AJAX and get back JSON, and handle the javascript manipulation completely on the client-side.
It seems option 1 allows me to continue to use the UJS supported by Rails which is nice. But also means I probably will be adding a lot of duplicate code everywhere which is annoying. Is there a way for me to do this elegantly while continuing to use UJS?
That's exactly the purpose of Apotomo: http://apotomo.de/
Here is it's own description:
Apotomo is a true MVC widget framework
for Rails. Widgets are based on Cells
and provide reuseable view components.
Having bubbling events, they know when
and how to update themselves via AJAX!
Working with Apotomo widgets almost
feels like developing GUI components –
in a Rails environment.
Have a try, it's great.
I'd not recommend using UJS for frontend apps: server shouldn't take care of client side business. I agree it's useful and clean but it lacks performance and thus should be kept for backend stuff (RJS will move into a gem, see here: http://weblog.rubyonrails.org/2011/4/21/jquery-new-default).
That said, back to the solutions you expose:
1) I think you won't need an extra controller, you'd just have to pass additional params in order to know from where to query came from. A hidden_field could do the trick. With this info, render the good js.erb file
format.js { if condition
render "create.js.erb"
else
render "create_2.js.erb"
end
}
2) I'd go for it and return json but you'll face the same problem: knowing from where the request comes from.
A better solution (than using a hidden_field) might be to check the request.referer in your controller action. This way you leverage the fact that each context has a unique URL, and don't have to explicitly specify another unique value when rendering your widget partial.
I am using the "in_place_editing" plugin for rails to render a form with in-place edits. Thing work fine as long as the default template is chosen by rails (no 'render' method is invoked inside the controller), but they break down when I try to render a partial using "render :partial => 'partial_name'" call. Is this a known issue (in_place_edit does not work with partials?) or am I missing something? I am getting the following error while rendering the partial:
Called id for nil, which would mistakenly be 4 -- if you really wanted the id of nil, use object_id
.../vendor/plugins/in_place_editing/lib/in_place_macros_helper.rb:74:in `in_place_editor_field'
You don't provide anywhere near enough information in your question, giving only two lines of the backtrace and no fragments of the view which does work, or the partial which does not. This means that any attempts to answer you must be based largely on guesswork. That said, the in-place editor helper is just a helper method like any other, nothing special. You can call it from just about any view component. It is highly likely that the way in which that view is included by the controller, or indeed a parent view, is not the reason it is failing.
The helper method is complaining about a nil value. This means that most likely, your partial is invoking in_place_editor_field and passing it values which are not defined in the partial. Check to make sure it isn't using local variables which are not defined, compared to those used in the view where your in_place_editor_field call works; check to make sure that it isn't asking for different instance variables too. In all probability you'll find the views which work are using one variable name while the partial you've tried to render is using another.
The render :partial => ... mechanism supports different ways of explicitly passing in values to the partial; you may choose to use these to clarify your code. See the :locals and :object options for the "Rendering partials" section of the render documentation in the Rails API at:
http://api.rubyonrails.org/classes/ActionController/Base.html#M000658
I am working on a maintenance project which is in rails 2.3.8. And this issue ate a lot of my time
In the view, Change the view to have an instance variable:
#batch = batch
in_place_editor_field :batch, 'priority'