My rails application is using js.erb views instead of html.erb views for specific windows. This works fine in practice, but when I'm using cucumber tests with capybara it gives me an error of
Missing template admin/groups with {:handlers=>[:erb, :rjs, :builder, :rhtml, :rxml], :formats=>[:html], :locale=>[:en, :en]} in view paths
When I click the button pertaining to this particular view. There is no groups.html.erb but there is a groups.js.erb. I want to somehow tell cucumber/capybara to not try to render the groups.html.erb but still render the groups.js.erb. I would prefer not to generate an unnecessary html file to render the same thing the escape javascript below is doing.
groups.js.erb:
$("#admin_content").html("<%= escape_javascript(render :partial => 'groups') %>");
Relevant admin controller method:
def groups
#groups = Group.all
end
You should use proper MIME type handling for your responses. Try this:
def groups
#groups = Group.all
respond_to do |format|
format.js
end
end
eventually you can be more specific about how to respond :
format.js { render layout: false }
more info on respond_to here.
note: if your controller only manages js responses, you can add respond_to :js at the top of the class, and then use respond_with #object in your actions.
Related
So, I'm trying to respond to an action with a js file.
def my_schedule
respond_to do |format|
format.js
end
end
In my view, I have 'my_schedule.js.erb' but it's not even executed, rails broke in the controller and throw me an error : ActionController::UnknownFormat, where I have the respond_to.
I tried to add
respond_to :js, :json, :html
at the beginning of my controller out of the actions but still not working.
Need help to debug this and understand how respond_to really works.
format.js will only respond to an xhr request. You can't trigger this response by just navigating to the route that points to this controller and method.
You can test the js.erb execution by changing the respond_to block to
def my_schedule
respond_to do |format|
format.html
format.js
end
end
Then create a my_schedule.html.erb file in the same view folder as the js.erb with the following contents
<%= link_to 'Test', my_schedule_path, data: { remote: true } %>
Note that you may need to adjust that path, I'm just guessing on that.
Then navigate to the same path you were trying to before. You should see a link which, when clicked, will fire the js response.
I've been following http://blog.markhorgan.com/?p=522 as a guide to update an image in a form with an ajax callback. Image saves fine but I want to do some clever ajax so the page doesn't refresh.
Here's my code:
edit.html.haml:
#promo-image
= render partial: 'promo_image'
_promo_image.html.haml:
= form_for( #property, remote: true) do |f|
= f.file_field :promo_image, :pattern => "^.+?\.(jpg|JPG|jpeg|JPEG|png|PNG|gif|GIF)$", :id => 'promo-image-upload'
= f.submit 'Update'
= image_tag #property.promo_image.url(:medium)
properties_controller.rb
def update
#property = Property.find(params[:id])
if #property.update(property_params)
format.js
else
render 'edit'
end
end
update.js.haml:
$("#promo-image").html("#{escape_javascript(render partial: 'promo_image',)}");
With the code outlined above I get error pointing to the format.js line:
ArgumentError in PropertiesController#update too few arguments
Can anyone see where I'm going wrong or perhaps point me in the right direction?
Many thanks!
Steve
UPDATE
Just to be clear, I want to be able to update JUST the Div stated here:
update.js.haml:
$("#promo-image").html("#{escape_javascript(render partial: 'promo_image',)}");
This code works, but refreshes the whole page:
respond_to do |format|
format.html { redirect_to edit_property_path(#property) }
format.js
end
FURTHER UPDATE
Just to be clear on my motives, I want to be able to update an element on the edit page, and not be redirected to a different one, e.g. show or index. This is for UI reasons. The guide above talks about the exact same thing.
FINAL UPDATE
The issue is because I'm using a file upload, this can't be achieved via ajax. For those in a similar situation see here: Rails form_for with file_field and remote => true and format => :js
A solution could lay here, and I will investigate this: https://github.com/JangoSteve/remotipart
Thanks to everyone for helping me work out the error of my ways!
Regarding your first update, you said that this code works, but refreshes the page:
respond_to do |format|
format.html { redirect_to edit_property_path(#property) }
format.js
end
If that is the case, that means the incoming request is an html request, rather than an AJAX request. So the format.html block runs and redirects the browser to the same page, which now has the updated image.
What you need to do is figure out why the page is not sending the request as AJAX. You can see the request format if you look at the terminal output (if running locally). It will say something like:
Processing by ControllerName#action as [format]
Format needs to be JS in order for the format.js to render update.js.haml.
UPDATE:
Now that you mention it, the issue is indeed the file_upload field. Uploading files with AJAX is actually not possible with the Forms Helper. See the docs:
Unlike other forms making an asynchronous file upload form is not as simple as providing form_for with remote: true. With an Ajax form the serialization is done by JavaScript running inside the browser and since JavaScript cannot read files from your hard drive the file cannot be uploaded. The most common workaround is to use an invisible iframe that serves as the target for the form submission.
I did a quick search on Google and found the remotipart gem, which seems to specialize in doing this. I don't have any experience with it though, so you're on your own from here on. :)
Try changing your update action to
def update
#property = Property.find(params[:id])
if #property.update(property_params)
respond_to do |format|
format.html { redirect_to properties_path }
format.js
end
else
render 'edit'
end
end
Source
I'm following the book Pragmatic Agile Web Development With Rails 4th Edition, BUT I'm using Rails 3.2.2 instead of 3.0.5 as recommended in the book:
~$ ruby -v
ruby 1.9.3p125 (2012-02-16) [i686-linux]
~$ rails -v
Rails 3.2.2
I got stuck when including AJAX to redraw the Cart without reloading the page. Here is the create action in line_items_controller.rb:
def create
#cart = current_cart
product = Product.find(params[:product_id])
#line_item = #cart.add_product(product.id)
respond_to do |format|
if #line_item.save
format.html { redirect_to(store_url) }
format.js
format.json { render json: #line_item, status: :created, location: #line_item }
else
format.html { render action: "new" }
format.json { render json: #line_item.errors, status: :unprocessable_entity }
end
end
end
And here is my RJS file create.js.rjs (under app/views/line_items):
page.alert('NO PROBLEM HERE')
page.replace_html('cart', render(#cart))
However, when I click the button that starts this action:
<%= button_to 'Add to Cart', line_items_path(:product_id => product), :remote => true %>
I get the following error in the development log:
ActionView::MissingTemplate (Missing template line_items/create, application/create with {:locale=>[:en], :formats=>[:js, :html], :handlers=>[:erb, :builder, :coffee]}. Searched in:
* "/home/me/src_rails/depot/app/views"
):
app/controllers/line_items_controller.rb:47:in `create'
If I change the filename of create.js.rjs to create.js.erb, the problem is corrected:
Rendered line_items/create.js.erb (0.4ms)
but nothing happens in the view.... not even the alert.
What am I missing?
What is the difference between file.js.erb and file.js.rjs?
It looks like rjs has been removed as the default as of Rails 3.1. You could get it back by installing the prototype-rails gem, but I think you should just use jQuery, which is the new default.
As for your code, the reason it's not working is that it's an rjs template being interpreted as .js.erb, and this is likely just producing invalid JavaScript (you should see errors in the JavaScript console in your browser). An rjs template used to set the page variable for you, and you would write Ruby code using it to manipulate your page. In a .js.erb template, it works more like in your .html.erb views. You write actual JavaScript, with Ruby embedded using <% %> tags. So the code in create.js.erb should look something like this:
alert('NO PROBLEM HERE');
$('#cart').html("<%= escape_javascript(render(#cart)) %>");
In rails >= 3.1 there is no jquery-rjs anymore. But you can use CoffeeScript here:
line_items/create.js.coffee:
alert 'NO PROBLEM HERE'
$('#cart').html '<%= j render(#cart) %>'
or something like that.
I would love to be able to have an ERB partial like this:
<ul id='things-index'>
<% #things.each do |t| %>
<li><%= t.name %></li>
<% end %>
</ul>
And be able to update it in the controller like so:
class ThingsController < ApplicationController
def create
#thing = Thing.new(params[:thing])
#thing.save
respond_to do |format|
format.html
format.js do
#things = Thing.all
page.replace('things-index')
end
end
end
end
Meaning that JavaScript would be sent as a response without me having to explicity write a js.erb template like the following create.js.erb:
$('#things-index').replaceWith('<%= escape_javascript(render("things/index")) %>')
There may be something like this already, either built in to Rails or available as a gem, but if there is, I'm not aware of it.
I suppose ideally, it would re-render the 'index' action via JS and send the update to the browser, so it might look more like this:
respond_to do |format|
format.html
format.js do
render 'index'
end
end
And know to replace #things-index (or allow me to explicitly specify it).
Update
Whoops...Apparently there was page.replace_html when Prototype was part of Rails, but that functionality has been replaced by the .js.erb template method. That seems much less DRY to me (tons of near-identical js.erb templates), so if anyone has a solution, I'd appreciate it.
You can pass whatever you want to your js.erb, including what to replace, and with what.
$('<%= #id %>').replaceWith('<%= escape_javascript(render(#renderable)) %>')
Note Even in 2012 this wasn't really a recommended way of doing things like this. These days (early 2015) I'd say it's even less recommended given the great client-side technologies available.
Another solution would be to send back just the data, for the client to render as it choses:
respond_to do |format|
format.html
format.json do
#things = Thing.all
render :json => #things
end
end
And on the client side:
function updateList(data){
var $item,
$list = $('#things-index');
$list.find('li').remove();
$.each(data, function(i, item){
$item = $('<li />').text(item.name);
$list.append($item);
});
}
$.getJSON('/my/route.json', function(data){
updateList(data);
});
Creating an update.js.erb, or edit.js.erb javascript file is connected to the rails 3 actions?
I'm new to rails 3 but know javascript.
If I add respond_to to each action to accept javascript, then will this code be called upon the action?
Thanks
Unable to understand your question, but based on what I understand you might looking for this.
You can render js files using render :js like:
render js: "$('#div_name').some_event;"
In Rails 3 you should set different respond types on the top of your controller class.
Something like this (probably it does not make any sense, but this is a dummy example):
class UsersController < ApplicationController::Base
respond_to :html
respond_to :xml, :json, :except => [ :edit ]
respond_to :js, :only => [:create]
def index
respond_with(User.all)
end
end
Then, you are responding using respond_with, and Rails will recognize type of request based on Accept header or request.format like (/users.json, /users.xml, etc.) and render proper files (index.html.erb, create.js.erb, etc.) depending on format
"Creating an update.js.erb, or edit.js.erb javascript file is connected to the rails 3 actions?" not necessarily, but it should be. conventionally, the file name should be the same as the method name. its a logical relation. but u can specify a file name(if the file name is different the method name) by
respond_to do |format|
format.js { render :action => "different_action" }
end