I have a rails app that gets requested cross domain (I use rack-cors to accomplish this cross domain request without jsonp) through this ajax: It responds with html
//to ensure cache=true gets passed
$.ajaxPrefilter('script', function(options) {
options.cache = true;
});
//ajax requests html
$.ajax({
beforeSend: function (xhr) {
xhr.setRequestHeader ('Authorization', api_key);
},
dataType: 'html',
type: 'GET',
url: url+'/gwsearch/ajax_search?d1='+d1_val+'&d2='+d2_val,
crossDomain: true,
success:function(result){
$("#display").html(result);
},
error: function(result) {
$('#display').html('Unauthorized client.');
}
It responds with HTML that is displayed in #display. It has this script tag included in the html
<script src="http://localhost:3000/assets/application.js" type="text/javascript"></script>
I have input buttons on the newly displayed rails app, they look like this:
<%= form_tag plans_collapse_plans_path, :method => 'post', :remote => true do %>
<%= hidden_field_tag(:plangroup_id, plangroup.id) %>
<%= image_submit_tag "collapse.png" %>
<% end %>
Clicking on the input button does a POST. The problem is that the input button will POST as many times as I've requested the cross domain rails app. If I've requested the app 5 times, when I click an input button on the app, it will POST 5 times.
No matter how many times I send the initial ajax request, I still only have one application.js present; it is not physically cached more than one time.
EDIT:This is what my cache looks like, as you can see, only one application.js:
The collapse_tplans POSTs all correspond to the same application.js file, same line. If i had loaded the initial ajax request 6 times, I would have 6 collapse_tplan POSTs from application.js
My guess is that the script tag in the html page being sent is initializing a new version of jquery (since jquery is contained within application.js) and also, possibly duplicates of other things that are being loaded in application.js each time it is sent through the script tag. With that said, under Network in Chrome, I can only see application.js being requested once, no matter how many times I execute the ajax request.
Any ideas? I need my input button to only POST once, like it should be doing. I'm truly stumped with this one.
As #natedavisolds said, I guess your application.js script is executed 5 times, and the click hanlder of your button or the submit handler of your form is attached 5 times.
The fact that application.js is requested only once does not tell you how many times it is executed.
Try adding the following line at the beginning of the application.js script :
console.log('application.js executed');
and check your browser console to see how many times the message appears.
From your script's name, application.js, I guess it is meant to initalize the whole page, while your ajax request looks like it is only filling the #display node with search results.
You will have to refactor your application.js file :
Read the code in your application.js file, and split the code in two parts : the part that concerns setting up the #display div, and the remainder.
Move the first part in a separate results.js (or whatever name you see fit).
Remove the <script src="application.js"> node from the response to the /gwsearch/ajax_search request, and replace it with a <script src="results.js"> node instead
Since you are using remote:true on the form, my theory is that rails.js is attaching a delegated event on the document element each time the script is loaded. 5 loads = 5 events = 5 ajax calls.
To fix it you could:
Remove the remote true and roll your own making sure it only happens once.
Remove the delegated events on document before loading a new script.
Try: (I am guessing here)
success:function(result){
$(document).undelegate('submit.rails');
$("#display").html(result);
},
Explanation:
Every time rails.js runs, a submit.rails event is added to the stack of events on document. When you insert your html into the page, the rails.js code runs. So, we undelegate the events first, then load the event back in.
The answer to this problem was to stop my application from serving application.js and host it on the client. I did this by the following:
app/views/layout/application.html.erb
I commented out the portion that serves application.js
<!--# <%= javascript_include_tag "application" %> -->
In my client, I added
<script src="https://myapp.herokuapp.com/assets/application.js"></script>
So the client serves application.js, and the rails html response gets sent without application.js. Hope this makes sense to everyone, this took me a while to piece together!
Related
Using:
C# MVC5 and Jquery
I have a filter screen that potentially uses multiple different filters. Based on what the user selects I make a call to the server and I load a partial view into a bootstrap modal as follows:
$.ajax({
url: filterUrl,
contentType: 'application/html',
success: function (filterContent) {
$("#divReportFilterModalBody").html(filterContent);
LoadFilterScript(SCOPESTRINGS[currentReport.Scope]);
},....
The next step is to load the necessary javascript for that filter page because you cant have scripts on a partial view. For this I also request the script from the server as follows:
$.getScript(scopeString + "FilterJavaScript",
function () {
The mvc controller:
[OutputCache(NoStore = true, Duration = 0, VaryByParam = "*")]
public ActionResult ScopeFilterJavaScript()
{
return
File(System.IO.File.ReadAllBytes(Server.MapPath("~/Scripts/.../filterPartial.js")), "text/javascript");
}
Because the user can only use one filter at a time and may or may not use multiple filters my questions are:
The scripts aren't big, is it better practice to load them all upfront rather then fetch them as required? The reason I load them as required is because they might not get called and didn't want to load a bunch of scripts that will not get used
Is not caching them a good idea because the user can use the same filter multiple times and in my current case the script will get loaded each time? OR should I rather cache the script and figure out a way not to load it again?
I'm also not 100% clear on script caching. What happens to the script in this case after it was loaded? If I make a call to the server I can see that it gets loaded again, was the previous scripts removed? Because when I look at the script tab on firebug they are all still listed there? Will this cause conflicts on the page?
What would best practice be in this scenario?
Thanks
Edit: I've been researching the topic a bit further and found this article (Old but still very relevant in my opinion). enter link description here
It's always a good idea to only load stuff if you actually need it. When the files arent that huge, maybe you can combine them and include them in the first place.
OR should I rather cache the script and figure out a way not to load it again?
yup.
When you load a script (without any queries) the browser caches it. But this has nothing to do with what happens when you load a script again. Either the servers delivers it "again" or the browser uses the cached one. Nevertheless, the script then executes again. Even if you remove it from the dom - once loaded scripts are just there.
Maybe you can wrap your scripts like so:
if (!window.foobarLoaded) {
// your script content
window.foobarLoaded = true;
}
Then you can load the script as many times as you like - it only "executes" once.
I'm using the latest version of devise and rails 4.2.4.
I've my home page
public/index.html
which contains heavy javascript. But when I load it from some other page, it doesn't load with all the effects. So included
$(document).on('page:load', ready);
<script src="/js/modernizr-2.6.2.min.js"></script>
<script src="/js/main.js"></script>
in application.js file.
But then
Logout
<%= link_to "Logout",destroy_user_session_path, method: :delete %>
doesn't work! It gets rendered as GET /users/sign_out.
When I remove those links from application.js, it works fine.
Any workaround for this issue?
Your javascript has an error in it.
--
Each time you use method: :delete or method: :patch, Rails populates a form with the appropriate verb, using Javascript to set it correctly:
[link_to] dynamically creates an HTML form and immediately submit the form for processing using the HTTP verb specified. Useful for having links perform a POST operation in dangerous actions like deleting a record (which search bots can follow while spidering your site). Supported verbs are :post, :delete, :patch, and :put. Note that if the user has JavaScript disabled, the request will fall back to using GET
The above is true if your javascript is broken (IE you have an error preventing it from working properly on the page).
--
Since you haven't posted your JS, I cannot give you any specifics.
However, I can say that your JS should be as unobtrusive as possible.
Using <script> anywhere in your Rails app is bad practice; you should have the files concatenated in your application.js with the help of sprockets manifest directives:
#app/assets/javascripts/application.js
//= require main
//= require modernizr
The issue is that most browsers do not support the DELETE method, the request will actually be submitted as GET, with a data-method="delete" attribute. This relies on the "Unobtrusive scripting adapter for jQuery" (jquery-ujs) being loaded by your page.
If you were using the standard rails templates, the 'application.js' manifest file would take care of that for you, but given that you are putting your index.html in the public directory you will have to manually include both jquery and jquery-ujs in your header.
An alternative is to change the route to logout to actually use GET (you will anyway have issues with the PUT and PATCH methods as well), adding this to your devise config (from devise wiki):
# config/initializers/devise.rb
# The default HTTP method used to sign out a resource. Default is :delete.
config.sign_out_via = :get
If you plan to use rails you should find out why you are having issues with the standard rails way of dealing with view templates. I use a lot of javascript-heavy pages, and they work just fine. Plus you get all the benefits of the asset pipeline.
Actually I have two, three questions but the heart of three question is same as written in Title . But I make my all points clear here in description
Following will be the specification of my question
Javascript file path => assets/javascript/
js.erb file path => views/customers/index.js.erb
view file name => views/customerS/html.erb
controller name => customers
action name => index
point # 1
If I make ajax call to my customers/index through my javascript file (with the mentioned path) then will it hit the index.js.erb file ?
point # 2
can we use js and js.erb file for the same action . what i mean is 'is it possible to send ajax from js file in assets folder and after controller action say index method handle the response in index.js.erb file in view folder ?'
point # 3
Say we can handle the response in index.js.erb file then how can / or if we can stop sending back the response / data in js file
if I have missed some more related points then kindly edit the question and place those , so that maximum people can enjoy the knowledge
Also have a look at This question and answer if you can
Update
I made a small experiment , by keeping the same function as mentioned in the
1: linked question , if ajax is sent through js file then js.erb doesn't do any thing but if i made the ajax call using the :remote => true , then it hits the js.erb file . Any explaination will be appreciated . For having a look at code sample in js, controller and js.erb kindly see the linked question
I think some language barrier is making it hard to completely understand your question by I'll try an answer.
The flow in Rails for :remote => true is this:
click on :remote => true link (foo) -> AJAX call is made to the controller requesting it to execute foo -> the controller executes the code contained in the foo action, it will then look for a file in the same view's folder called foo.js.erb and execute that JS -> that code will update the portion of the original HTML page using a file called foo.html.erb in the same view's folder.
Any call to a JS function contained in assets/javascript/ will just execute the JS called, whether it is AJAX or not.
So assets/javascript/ = a place to put JS code instead of putting it in <script> tags in the HTML file
:remote => true = an AJAX call to a specific controller action that then renders a similarly named .js.erb file and .html.erb file to update the page asynchronously.
Actually the flow of assets pipeline and js.erb files is much different . All the thing asked in the question is
what is difference if ajax is called from assets pipeline and if ajax is called through :remote => true . Now the answer is
,"There is almost no difference in the sense of functionality but there can be some minor (but in some case) very important differences depending upon our rendering methods and codes in js and js.erb files" e.g
If I make the ajax from the view i.e by using :remote=> true , then It'll simply goto controller action, perform whatever is defined , after that looks into js.erb file and finally the html.erb file . That's it . Here no assets js is looked for .
Now there is case when we make call from assets javascript , there are two cases for it .
First case is that i want to handle the response in the same js file from where the ajax is sending then no worries , control will come back to success function by default , so don't specify the respond_to method in the controller's action .
Second case is , when we want ajax to be fired from assets javascript , but want to handle the response in the js.erb file then specify the respond_to as below
respond_to do |format|
format.js #{ render 'index.js.erb' }
format.html
end
Now it will hit to js.erb file also . Point to remember is control of program will get back into assets success function also but at this scenario , response will contain the data present in the js.erb file instead the data of controller function
From the above explanation one should remember that assets js is
called before getting into rails controller while js.erb functionality
is performed after the rails controller has finished it's work .
another day, another night spent banging my head over JS and JQuery.
I want to call a page via HTML (and show the route in the header), but have it also load a js.erb script. Or alternatively, I want to call the js.erb script remotely, but also change the http header to indicate the new route.
I want this because:
a) I want to learn and this is frustrating me
b) currently, when the user refreshes, his current page doesn't come up, but the one from which the remote => true call came in;; it's confusing for the user to go back to a page he doesn't remember.
I'm implementing the JQuery.pageless plugin. I call the user/show page remotely and this is what goes into my show.js.erb file:
$(function(){
$("#container").html("<%= escape_javascript(render '/users/show') %>");
$('#relationship_list').pageless({ totalPages: "<%= #reviews.count %>"/5+1
, url: '/reviews'
, params: {id: "<%= params[:id] %>"}
, loaderMsg: "loading"
});
});
Now this code is A-OK. it works when called remotely from show.js.erb. The problem is that I want to load the "user show" page via html, and not remotely. That means that this script doesn't get loaded. I need to find another, unobtrusive way to load it.
I want to reiterate: there is no bug in the code.
Hopefully someone can help. Also please note: someone wrote about using the gon gem to address the issue. Not for me - I really want to figure this out without gems.
It sounds like you have the page load working via a remote call (I'm assuming an AJAX GET request after clicking a link or button), which works properly, but the URL doesn't update in the address bar?
You're looking for the HTML5 pushState() method.
Update your show.js.erb file like so:
$("#container").html("<%= escape_javascript(render '/users/show') %>");
history.pushState(null, "Page Title", "<%= user_path(#user) %>");
I'm trying to upload a file to my Rails application with ajax. To facilitate this, I have included the jQuery.remotipart gem.
// app/assets/javascripts/application.js
//= require jquery.remotipart
I have a form for uploading files. These files are processed by CarrierWave.
<%= form_for #import, remote: true do |f| %>
<fieldset>
<%= f.label :file, "Attach a CSV file" %>
<%= f.file_field :file %>
</fieldset>
<%= f.submit :upload %>
<% end -%>
Unfortunately, when I submit the form with a file attached, it doesn't seem to arrive at my controller action correctly. The params hash has stringified JS objects as keys.
Started POST "/file_imports" for 127.0.0.1 at 2012-11-06 01:00:49 +0000
Processing by FileImportsController#create as JS
Parameters: {"object Object"=>{","=>{"object Object"=>{","=>{"object Object"=>nil}}}}}`
In Chrome's Dev Tools, I can see that this is indeed the form data that is being sent to the server:
The form works perfectly when I remove remote: true (of course, it sends a HTML request rather than a JS request in that case).
Anyone have any idea what I'm doing wrong? By the way I'm using Rails 3.2.8 and Remotipart 1.0.2 (latest).
Edit: Did some more digging.
Looking into the Remotipart source, I can't seem to understand what it is supposed to do. For example, in vendor/assets/javascripts/jquery.remotipart.js, Line 22 has the following:
settings.data = form.serializeArray();
A little further down, the settings are sent to the server via $.rails.ajax(settings).
The $.fn.serializeArray() method returns an array of JS objects. If we assign them to the data attribute of a call to jQuery.ajax(), that would account for the serialized object parameters I'm seeing on the server. We need to pass an object as the data attribute, not an array of objects?
However, when I try to flatten the array into one object, the whole thing breaks and a HTML request is sent to the server instead of a JS one. I think it has something to do with the fact that jQuery.ajax.processData == false.
I have an issue on the Remotipart Github.
After a ton of debugging and reading through lots of code, I finally have the latest gem working with Rails 3.2.8. I was nailed by three gotchas:
(1) I was disabling my file input field prior to form submission and this causes remotipart to ignore it for inclusion in the iframe submission. You must make sure your file inputs are enabled. This is the direct cause of the bug you are seeing.
(2) In the course of my debugging, I was overriding jquery.iframe-transport with the latest upstream source and it doesn't have support for the X-Http-Accepts hidden variable in the iframe. You must use the version bundled with the remotipart gem.
(3) Don't forget to wire up a callback for ajax:complete on the form if you are using a datatype other than script. This is the default if you don't specify dataType in the global ajax options, or use the data-type attribute on the form.