How to improve speed execution of coffeescript in rails? - javascript

To increase speed of page loading I`ve implemented creation comments via AJAX. It is simple and not heavyweight.In controller action I have:
def create
#comment = #commentable.comments.new(params_comment)
respond_to do |format|
if #comment.save
flash.now[:notice] = "Your comment added."
#response = {comment: #comment, model: #commentable}
format.js { #response }
format.html { redirect_to #commentable }
else
format.js { render nothing: :true, status: :not_acceptable }
format.html { redirect_to #commentable, status: :not_acceptable }
end
end
end
and js file:
$("<%= escape_javascript( render 'comments/comment', #response)%>").appendTo(".comments").hide().fadeIn(500)
$('.notice-wrapper').html("<%= j(render partial: 'shared/notice') %>")
$('.alert').fadeOut(3000)
if $(".no-comments").css("display") is 'block'
$(".no-comments").hide()
$(".new_answer").hide()
$(".open").show()
But instead of speed up performance I got the opposite effect. Response time through JavaScript increased on 100-200 ms(~300ms total). Is this normal behavior or I am doing something wrong? Is there any way to improve speed little bit?
My performance test:
UPD:
My performance test with just JS file.

Let's put aside for the moment my opinion that embedding ERB in CoffeeScript is utterly gross to look at and unmaintainable. It is indisputable that there's a huge performance impact when you generate and compile CoffeeScript on every request.
You also lose any chance at HTTP caching.
My first suggestion would be to separate CoffeeScript from ERB. Populate hidden fields in your ERB with the data you need, and then mine those fields in your CoffeeScript.
But if you must embed ERB in what should be static files, embed the ERB tags in pure JavaScript instead, and let the compiled CoffeeScript use those.

The code you've written wouldn't really be expected to speed up the request, because Rails still need to process all the ERB etc. You're still returning rendered HTML and sending it to the browser where it is added to the DOM.
If you wanted to make it 'faster' you could simply render the #response as json and deal with it on the client using jQuery or a front end framework.
The code does make it nicer for the user, however, because it doesn't refresh the entire page.

My suggestion would be to hook into the form request, and on success, render the new comment, else re-render form with errors. An example:
# app/javascripts/comments.coffee
$('#comment-form').bind 'ajax:complete', (data, status, xhr) ->
// psuedo code
1. if status is success
append comment -- $('#comments').append(data.comment)
2. else
re-render form with errors -- $('#comment-form').html(data.form)
Return comment template (comments/comment) and append to comments
Update your controller to return the form with JS response if not_acceptable.
Note the file is found in app/javascripts/comments.coffee

Related

Rails & AJAX, is there a reason you shouldn't render html view directly in controller action for ajax to process?

The classic way to work with Rails & Ajax is always something that looks like this:
// JS - let's assume this submits to dummies#create
$(form).submit()
# Dummies Controller
def create
#dummy = Dummy.new(dummy_params)
respond_to do |format|
format.js
end
end
# /views/dummies/create.js.erb
$("page").append("<%= escape_javascript(render partial: 'dummy_view' ) %>");
# /views/dummies/_dummy_view.html
<h1><%= #dummy.name %></h1>
I've always been curious, because the above seems to create a random create.js.erb file with very little meat... is there a reason (e.g., it's terrible convention, or terribly insecure or whatever), why you should NOT instead just render the view directly back to ajax?
// JS - basically takes responsibilites of create.js and puts it into the always
$.ajax(...).always(function(xhr, status){
$("page").append($(xhr['responseText']))
// responseText contains the partial rendered by the controller action
})
# Dummies Controller
def create
#dummy = Dummy.new(dummy_params)
render partial: 'dummy_view'
end
# /views/dummies/_dummy_view.html
# unchanged
<h1><%= #dummy.name %></h1>
NOTE above is pseudo-code, apologies for minor errors. The conceptual idea & question remain unchanged, though.
The create.js.erb is not random, is the view for the action with the expected format.
Generally, you may not have a view so simple (You may have different selectors other than "page", you may have some extra js code to be executed after or before the append), a js view/script is the general solution for an ajax request to give the response full control over what to do.
You could have what you want, but it will just work for your particular case when the selector is always "page" and you only want to append html to that element. Nothing prevents you from doing that (though you might want to use a custom ajax request and not rails' one since it sets js format by default and executes the response's script).
The convention is that a rails' remote request renders a js script, you can move out of the convention if you want. You'll lose a lot of flexibility with your approach as is (like... what if the create action fails an you need to display errors?).

Dojo request.post to Rails Fails

I'm converting a website from Symfony to Ruby on Rails and am finally down to my javascript pop up email contact form. I don't want to rewrite the form or the java script as it took a lot of work to get it to work in the first place. That will be phase two.
Here's where I'm having the problem:
sendBtn = new Button({
label: "Send",
onClick: function(){
if (emForm.validate() == true){
// Post the data to the server
request.post("/contact/create",
{data: domForm.toObject("contact-form"),
// Wait 2 seconds for a response
timeout: 2000
}).then(function(response){
emailDialog.hide();
alertDialog.set("content",response)
alertDialog.show();
});
}
}
},"submit-btn");
I know it gets to the request.post as I copied the line "email.Dialog.hide()" just before it and it hid it. I later added code to catch any errors. It goes there immediately and not after the two second timeout. I'm using dojo here, by the way. So I suspect it doesn't like the "/contact/create" as it's the only thing I changed.
In my routes.rb I have:
get 'contact/create'
Do I have the right information in my post? If not how do I get there? The javascript is included in application.html.erb so it can be invoke from all pages on the site.
In case it's pertinent, my contact_controller.rb is currently just this:
class ContactController < ApplicationController
def create
respond_to do |format|
#format.html {}
#format.js {}
format.json { render :json => {:response => 'Amazing, it works'} }
end
end
def show
end
end
Take a look at your network tab in dev tools, it should tell you why it is failing post... I'd say try adding handleAs: 'json' option to your request.post. For more on dojo/request, read this
As it turned out, I had the right path in the request.post statement. I found out my copying the Javascript into my html.erb file so I could use
<%= contact_create_path %>
in it's place. I ended up getting the same value so that wasn't the problem. I then checked my Firebug console. Rails sends a nice dump of the problem. I was getting a 404 error. The problem was that I was doing a post and there was no route for it. So I changed the routes.rb file from
get 'contact/create'
to
post 'contact/create'
This might cause me other problems later on if I want to do a non-Javascript version.
I then got another error:
ActionController::InvalidAuthenticityToken in ContactController#create
Through the help of Stackoverflow I found the fix. I added the second line below:
class ContactController < ApplicationController
skip_before_filter :verify_authenticity_token
...
Again, this solution may cause other problems. Skipping verification doesn't seem like a good thing. I had a number of other problems getting the whole process to work, but their specific to my application.

Ruby ajax call initiates unwated Popup Box

I have an AJAX call for one of my routes in my Ruby on Rails project. It calls a method in a Ruby Controller to update flags on several of my objects, and then I need the page to reload to reflect those changes. This was my solution, at the end of the Ruby method:
respond_to do |format|
format.js {render js: "window.location = '#{processed_items_path(params)}'" }
end
This does exactly what I need it to do, it refreshes the page and pulls the user back to the top to see a flash notice. However, before doing so, it pops up a window that says:
The page at localhost says:
"window.location = '#{processed_items_path(params)}'"
And it requires you to click "OK" before you can continue on. Is there any way to get rid of that box?
According to the comments above, instead of redirecting it'll be better to just render your partial containing your table
I'm assuming your ajax call is working (as it's taking you to your method). For rendering your partial you can do:
def your_method
#your logic of updating attributes
respond_to do |format|
format.js {} # this will let rails look for a file named your_method.js.erb in your view
end
end
Now you simply need to render your partial in your_method.js.erb
$("#some_id_of_parent_element").html("<%=j render partial: "partial_containing_table", locals: { :your_local => partial_local} %>");
For details refer to Working with javascript in rails

Rails render JS partial printing code to page

I'm using AJAX in my Rails app to render a JS error message when needed. It was working initially, but now coming back to it some time later, it still shows the JS error message but for some reason it now also prints the entire JS file as HTML in the window. This is what's called in the controller:
respond_to do |format|
format.js { render :partial => 'error' }
end
My file named _error.js.erb contains some JS which isn't relevant as regardless of what it contains Rails prints it to the window still.
This is what the JS looks like outputted to the window: (I tried commenting out the JS to see if it made a difference)
You can try it with some modification :
respond_to do |format|
format.js
end
Inside the action and in the view action_name.js.erb write your js code ar if you want to put your erb then use escape_javascript.
Check the following link :
Why escape_javascript before rendering a partial?
I did it! In case there will be someone else wondering a few years later, there's the answer: you should put rendered value in a javascript_tag inside your html.erb. Like this:
javascript_tag render: 'error'
that will put what rendered between <script>...</script> tags and escape all unnecessary code.
Here's the documentation on it

Rails: How do load/trigger a js.erb using the controller?

I'm not even sure how to ask this question in a way thats understandable.
Basically, I'd like to do some javascript using a js.erb after I save. I'm submitting the form using a regular javascript/coffee-script file (if all fields are filled in correctly, then the form is submitted, else the form does nothing & just displays errors).
Part of my coffee-script:
fieldCorrectResponse: (fields, response) ->
if fields == correct
$('#new_mail')[0].submit()
else
$('#mail_error').text('error while filling out form')
my mail controller:
def create
#mail = Mail.new(mail_params)
if #mail.save
#PERFORM SOME JS USING A JS.ERB
else
render :new
end
END
So I guess what I'm really is asking is, how would you call a js.erb in the controller?
Wrote the solution to my problem below..
You should be able to render js and use create.js.erb.
Please try:
# MailsController
def create
#mail = Mail.new(mail_params)
if #mail.save
respond_to do |format|
format.js
end
else
render :new
end
END
Then, implement your javascript in app/views/mails/create.js.erb.
"do some javascript" isn't terribly descriptive. are you wanting to return a JSON object from the create action, which can then be parsed by the success callback on your jquery? Or do you want to have a template that has javascript in it that gets called as a result of the save action?
vinod covered the second way. make sure you have your routes set up correctly.
if you want to return a parseable JSON object, then write
render json: { some: 'json objects', should: 'go here' }
Also, not knowing what "mails" are, if you're trying to send emails that should be done with action mailer, and probably done as a part of committing the main model to the database (if you're creating a user and also trying to send an email, have a method as part of user creation that sends out the email).

Categories

Resources