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).
Related
I've been reading a lot about Rails and AJAX and 5.1 Unobtrusive javascript. It explains a lot about responding to Rails version of AJAX calls with a .js file for example.
However what im wanting to do isn't serving up an entire .js file, it's simply updating an element after a <% link_to %> POST request. From my understanding setting remote: true submits it as a AJAX request.
Essentially I have a "Post" which a user can like via a linked Like button. This sends a POST request to the "Post" controller which updates a post to liked and adds a like to the post.
Unfortunately to see the effects of the post being liked (Which is simply that the link changes color as well as the font-awesome icon) you need to refresh the page. I basically want it to update without needing refresh.
I "think" based off what i've read I need to make a respond do and respond via .js to the request with a .js file in the view I want to update (for instance if the controller action is called "like", maybe a like.js.erb file in the view im updating?). But I don't want to serve an entire new page..or would this simply just run the .js?
Then I could do something like $('i.fa-icon#id').style.color = "blue" or something? (Im assuming I can send data from the controller to the .js.erb file?). Not sure the best way to do this, don't rails elements a lot of times have some sort of data-attribute or something (Im still a beginner at this).
Your description is quite correct!
Opposed to the other answer, you don't even need a event listener but as you said you want to have a respond_to in the controller.
So starting from the html:
# post/index.html.erb
<div id="like-button">
<%= button_to "Like this post", post_path(#post), remote: true %>
</div>
Note, that when you use a button_to helper it'll be a POST request by default.
If you click it, it'll go to the controller#update, which you want to change to this:
#posts_controller.rb
...
def update
#post.save
respond_to do |format|
format.html { redirect_to post_path(#post) }
format.js # <-- will render `app/views/posts/update.js.erb`
end
end
Note: the format.html is rendered when JS is disabled.
Now in the scenario that JS is enabled, it executes the app/views/posts/update.js.erb file. It can look like this:
const likeButton = document.getElementById('like-button');
likeButton.innerHTML = '<%= j render "posts/liked-link", post: #post %>';
What is the last line doing? Of course, you can change the style directly with the JavaScript, but you can also render a new partial - and this you will create in a new html file:
# app/views/posts/liked_link.html.erb
<div id="like-button ">
<p>"You liked this post!" </p>
</div>
I just changed the link/button to ap now, but of course you can do whatever you want.
Hope that makes sense :)
Not sure if I understand the question, but if you want to update like button:
What you want to do is to add an event listener to the button, and when clicked it makes a POST request to whatever route handles the likes(with the correct parameters) and your controller should respond with the like object (or whatever in the database gets stored). Have your post request on success method to grab the like button and change it to whatever you want it to look like
$(“#like-btn”).click(function(){
Rails.ajax({
url: "/some/url/to/like/controller",
type: "post",
data: [your post data],
success: function(data) { $(`#${ data[“btn-name”] }`).attr(“color”, “blue”; }
})
}
You can stick this script right in the bottom of the html page
You don’t have to do it exactly like this, just giving you an idea of how to set up the pattern of having JavaScript and Ajax handle the post request and updating of the frontend instead of using html buttons
I posted a very poor question about this earlier, so I am reposting and making it MVCE.
I'm building a messaging service with Rails and AJAX. So far I can submit a message through a form, it will update in the HTML DOM, an AJAX POST method will send it to the controller, and the controller will save it in the database.
Now I need to add an AJAX method that will GET the message that was just submitted -- so that other users (in other browsers) will be able to view it.
Currently, and this is a hack job way of doing it, in my JS code I set a timeout that calls an AJAX GET function every half second. Is there a better way to do this -- as in, once the controller saves the message can it call the AJAX function? The AJAX code looks like this:
function retrieveMessages(){
var message;
<%debugger%>
$.ajax({
type:"GET",
url:"<%= messages_get_path %>",
dataType:"json",
data: { what_goes_here: "blah" }, //this is the part I do not understand -- see below
success:function(data){
message = data;
console.log(data)
}
});
setTimeout(retrieveMessages, 500);
}
$(document).ready(function(){
//get messages
setTimeout(retrieveMessages, 500);
... more irrelevant
The line data: { what_goes_here: "blah" } doesn't make sense to me. What is the syntax for the controller to send data back to be stored into data:? Furthermore, from the console I can see that what_goes_here is being passed as a parameter to the controller -- again this doesn't make sense to me.
My route looks like this get 'messages/get', :to => 'messages#get' (this might be incorrect?)
rake routes shows
messages_get GET /messages/get(.:format) messages#get
And as of now, I don't have anything in my controller other than a respond_to because at this point I'm just trying to call the controller. What is the syntax to send data back to the AJAX method?
def get
debugger
respond_to do |format|
format.html
format.json {render json: #variable} //is this #variable being passed to the AJAX call?
end
end
UPDATE
This makes more sense to me... the AJAX method simply calls the def get function. The def get function then finds the message in the database, and stores it in an instance variable. Subsequently, I can add some Javascript code that will insert it into the DOM. However I must have something wrong in my routing because I'm getting (in the console) http://localhost:3000/messages/get 404 (Not Found)
What you are doing, as you suspect, is not effective. The more users are online the more will be load from these refreshing requests, most of them probably returning no new data.
You should consider more active way of notifying your browsers about changes on the server. One option is to use ActionCable.
I'm working on an application in which a certain model is updated from a number of different locations using remote forms. I'm looking for a pattern to dynamically call the right JS callback after updating this model.
With a normal form this can be solved by passing a redirect url in the form itself and then redirecting to this url in the controller. The remote form side is harder: Should I pass a file name? Should I pass an arbitrary string and then switch on that string in a single .js.erb file? Any other suggestions?
Is this just a sign that the application should be restructured to prevent updating the same model from more than one location?
No it's fine If you can call the same controller action from different locations.
Your options:
1) Preferably this controller action can give the same response and will work for the different locations, ex. it just updates a container with a id which is present in all those locations.
2) You noted that redirects made things easy in the past, consider adding the following to your application controller:
def js_redirect_to(path, flash_messages = {})
flash_messages.each { |type, message| flash[type] = message }
respond_to do |format|
format.js { render :js => "window.top.location='#{path}';" }
end
end
This is the same signature as the normal redirect_to, it just allows you to redirect from a js request. Note that if you use turbolinks the js should be 'Turbolinks.visit(url);'.
3) If you really can't handle it generically like the options above, you could pass your JS namespace of the location you are submitting from in the form, and the controller calls the same method for all locations, it's just a different namespace. Ex:
Let say one location is from Pet administration, then in assets pet.js:
var pet = {
load = function() {
your page load js...
},
... more functions...
post_callback = function(html_segment1, html_segment2) {
this is where you handle the form callback for pets...
$('some_element').html(html_segment1);
$('another_element').html(html_segment2);
}
}
Construct more like these for other locations of your app. Using JS namespaces like this is anyway a good idea. Then your form submits a parameter :location => :pet to the controller, which responds with:
... your JS code that all pages should execute...
html_segment1 = "<%= escape_javascript(render 'some_partial') %>";
html_segment2 = "<%= escape_javascript(render 'other_partial') %>";
<%= #location %>.post_callback(html_segment1, html_segment2);
4) Use a widget gem, most popular is apotomo or cells.
5) Just use a case in the controller to render different views.
Hope this helps, let me know if you need clarification.
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
how do I refresh a rails path with an error message using ajax. I'm waiting for a callback from a payment API and if I get an error I want to reload the page and show the error. Problem is I'm using ajax. How can I pull this off ?
Refreshing the page to display a simple feedback error is completely unnecessary and long-winded. A much cleaner, and more direct method, is to use js to update the dom within the callback.
A very very simple demonstration:
$(".errors").append('<li>My error</li>');
In the name of simplicity, do that (or something similar within javascript), don't redirect just to display an error.
If error is persisted on database, you can use window.location on callback function in order to relaod the page with errors.
In the action that responds to the AJAX request you can do
flash[:alert] = "My error"
And then have the action render the redirect as such:
render js: "window.location.href = '#{my_named_route_path}'"
Or, you may want to write a method to abstract this, such as:
app/controllers/application_controller.rb
def redirect_to_via_xhr_if_applicable(url, options = {})
flash[:info] = options[:info] if options.key?(:info)
flash[:alert] = options[:alert] if options.key?(:alert)
flash[:notice] = options[:notice] if options.key?(:notice)
if request.xhr?
render js: "window.location.href = '#{url}'"
else
redirect_to(url)
end
end
Then you can just make this call from your AJAX (or non-AJAX) action:
redirect_to_via_xhr_if_applicable(my_named_route_path, alert: 'My error!')