I'm attempting to run javascript on specific pages and my only solution seems like an anti-pattern. I have controller.js generated inside of assets/javascripts/. I'm using gem 'jquery-turbolinks' I have code resembling the following:
$(document).ready(function () {
//Initiate DataTables ect..
}
)
This code is firing on every page So I added the following inside of it.
if ($('#page-specific_element').length > 0) {
//Initiate Datatables ect.
}
My question being, is there a way to set rails to utilize only the javascript files necessary to a specific controller or is logic gating behind an element search the best solution?
This is a pretty nice turbolinks specific solution:
<body data-controller="<%= controller.controller_name %>" data-action="<%= controller.action_name %>">
// Instead of listening for document ready
$(document).on('users#show:loaded', function(){
$('#logger').append('<li>Users Show action loaded</li>');
});
// Instead of listening for document ready
$(document).on('users:loaded', function(){
$('#logger').append('<li>Some users controller method loaded</li>');
});
// Turbolinks fires "page:change" when it changes the page content.
// you could change this to just document ready if you are not using
// turbolinks.
$(document).on("page:change", function(){
var data = $('body').data();
$(document).trigger(data.controller + ':loaded');
$(document).trigger(data.controller + '#' + data.action + ':loaded');
});
// This is just for demonstration purposes...
$(document).trigger("page:change");
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<body data-controller="users" data-action="show">
<ul id="logger"></ul>
</body>
You should note that if you are using Turbolinks that you'll have a long-running, persistent session with maintained state. Which means that if you add inline script tags or load additional javascript files in your views they will linger in the browsers memory.
You therefore might therefore want to cleanup whatever page specific event handlers you have created on page:before-unload.
There are many ways to do this. I'll describe one.
I use this old gem to wrap all my .module.js files in commonjs modules.
I then have a helper that writes something like the following to a <script> tag in the head of every HTML page.
<script>
var App {
pageModule: 'posts/create'
};
</script>
posts/create is generated from a simplified mix of the current controller and action rendering the request.
I then have something like the following in my domready for my application.js.
try { App.pageModule = require(App.pageModule); } catch(e) { console.log('%c' + e, 'color:red; background-color:yellow'); }
if (_.isPlainObject(App.pageModule) && _.has(App.pageModule, 'initialize')) {
App.pageModule.initialize();
}
Finally, I'll have a file like this which is specific to the /posts/create URL.
app/assets/posts/create.module.js
module.exports = {
initialize: function() {
// data-tables stuff here...
}
}
Related
Is it possible to call a javascript function when a user has successfully been redirected to a new view?
I have tried the following-
C#
if (Session["UserId"])
{
return RedirectToAction("LoggedIn");
}
cshtml
#if (Session["UserId"] != null)
{
<script>$('#btnTest').click();</script>
}
Fails with
JavaScript runtime error: '$' is undefined
I have referenced jquery in the project and tested it just with a button click and this works however the above is failing. How can I achieve this?
Most likely, this HTML is being inserted before jQuery is actually referenced. For example, if you're not referencing jQuery until the closing body tag (as you should), then this will be run before jQuery actually exists. The best way to handle this type of thing is with sections. You can defined a section in your layout for scripts and then ensure that that section is below required dependencies like jQuery.
_Layout.cshtml
<script src="/path/to/jquery.js"></script>
#RenderSection("Scripts", required: false)
</body>
AwesomeView.cshtml
#section Scripts
{
#if (Session["UserId"] != null)
{
<script>$('#btnTest').click();</script>
}
}
This will result in the following HTML being generated (assuming the session variable isn't null of course:
<script src="/path/to/jquery.js"></script>
<script>$('#btnTest').click();</script>
</body>
It's important to note that this only works with standard views. Partial views cannot define sections, so if you need to include some bit of script inside a partial, you'll have to take a different approach to deferring execution. Generally, the best method at that point is simply utilize window.onload directly:
var onload = window.onload
window.onload = function () {
onload();
// your code here
}
That should ensure that jQuery has already been loaded. However, it's really better to just not include JS code in partials, in the first place. There's a number of issues that can result from that and it makes your code very fragmented and brittle, in general.
I'm using a *.js.erb in my Rails-App, which reacts to a clicked Link.
But when I render my page, by getting there via a link_to, I have to refresh the Page, to make it work. Is there any Trick, how I can make this work?
Here is a very simplified version of my code:
$(document).ready(function(){
$('#toright').click(alert(">> was clicked"));
});
$(document).ready(function(){
$('#toleft').click(alert("<< was clicked"));
});
$(document).ready(function(){
$('#save').click(alert("save was clicked"));
});
$(document).ready(function() {
alert("The document is ready!");
});
After the site is rendered, it does not show my function, but when I type the URL into my address-bar, it works.
Some EDITS:
I'm using Rails 4.0.1
I have the js.erb in my assets folder
Ok. I've put them all in one $(document).ready, but I still have to reload my file for making my JS work.
UPDATE:
So I have edited my application.js to contain
$(document).ready(function(){
alert("The document is ready!");
$('#toright').click(alert(">> was clicked"));
$('#toleft').click(alert("<< was clicked"));
$('#save').click(alert("save was clicked"));
});
and put my js.erb to be only .js in my assets/javascripts folder.
But anyway, it just won't work until I have refreshed the page.
ready is a jQuery function, thus ensure you to use and include properly the jQuery library in your Javascript.
Furthermore, if you don't need to exchange data between your controller and your Javascript, I advice you to put your Javascript in your asset folder, for instance: /app/assets/application.js.
This is possible only for the Rails applications 3.x and 4.x.
Best regards.
Since the introduction of turbolinks in rails 4 you can use:
$(document).on('turbolinks:load', function() {
// Events
}
Instead of:
$(document).ready(function(){
// Events
}
How can I to know if resource file is loaded by a page using Javascript?
For example some .css, js or other sort of sort of file?
It depends on whether you're adding the files dynamically. I'll explain for JavaScript files.
Either use this piece of code to load an external file:
var scriptElem = document.createElement('script');
scriptElem.onload = function() {alert('Script loaded.');}
scriptElem.src = 'test.js';
document.getElementsByTagName('head')[0].appendChild(scriptElem);
Or if you're adding them through HTML, you can make the script file reflect the fact it's been loaded with a variable. E.g. in test.js:
// ...rest of file...
window.testLoaded = true;
You can then check for window.testLoaded === true anywhere on the page.
For CSS files you can also use the first technique, but of course not the second. It might be possible to define a stub definition for a class .test, then create an element with that class and check whether it has a certain style that you set in the CSS file.
Use jQuery.ready event which means that all the page has been setup and then you start executing your JavaScript logic
$(document).ready(function(){
//your code
});
If you have the need to load stuff asynchronously then you should use your JavaScript code to load it like the example of loading a script:
$.getScript("http://.../some.js", function(){
//script loaded and hooked on the page; your custom logic here
});
For anything more complex I'd suggest some dependency management system like requireJS.
I'm working in a Joomla environment but I think this is not the source of the problem.
I have a view which renders subviews (containing JavaScript code like <script type="text/javascript></script>) with AJAX. Problem is : the JavaScript code is ignored. I guess that's because it isn't in the document when it is loaded.
Here's the JavaScript code contained in one of the subview :
<script type="text/javascript">
window.addEvent('domready', function() {
$('annuler').addEvent('click', function() {
var a = new Ajax(
'{$url}',
{
method: 'get',
update: $('update')
}
).request();
});
});
</script>
Another basic example, if I load a subview with the following code in it, it won't work either :
<script type="text/javascript">
function test()
{
alert('ok');
}
</script>
<a id="annuler" onclick="test()">Annuler</a>
I'm getting the following error message : "test is not defined"
I can't find a solution to that problem so I'm starting to think that it is not a good way to use JavaScript...and, yes, I'm kind of new to event based JavaScript (with frameworks and so on).
I finally managed to put all the subviews and the JavaScript code into the same page. I'm using the CSS display property to hide/show a subview (<div>) (instead of loading it with Ajax).
Place the code you want to run in a function and call the function from an on ready block
EDIT:
Example:
$(document).ready(function() {
// put all your jQuery goodness in here.
});
Found here: http://www.learningjquery.com/2006/09/introducing-document-ready
What is the best practice of activating jquery ui widgets for html loaded and inserted into the document by ajax?
I am an advocate of unobtrusive javascript and strongly believe that all functionality accessed by javascript should be also accessible without it. So in the ideal case, each form which opens in a popup, should also have its separate page and links to them should be replaced with javascript-based ajax loading.
I find this pattern very useful for loading and inserting a part of another page into the current document:
$('#placeholder').load('/some/path/ #content>*');
Or to make it more generic:
$('a.load').each(function() {
$(this).load($(this).attr('href') + ' #content>*');
});
However, I would also like to activate the javascripts from the dynamically loaded page, so that different widgets function correctly.
I know, I could add those javascripts to the current document and activate all of them in the callback of .load(), or I could use $.get() to get some JSON with html and javascripts separately, but I guess, there might be a more elegant generic solution to this.
What would you recommend?
BTW, I am using Django in the backend.
The question is how you're activating your javascript currently. If you're doing something like:
$(document).ready(function() {
$('a.foo').click(function() { ... });
})
You could consider changin things to:
$(document).ready(function() {
$('a.foo').live('click', function() { ... });
})
That way when new DOM objects are loaded the event handlers are attached.
What I've done is used the "load" option that is specifiable by jquery.ui widgets. Unfortunately, this isn't well documented, so you won't see the option here: http://jqueryui.com/demos/tabs/#options for example, but you will see it here: http://jqueryui.com/demos/tabs/#method-load
For the most part, each of the methods you invoke have an initial option that can be set, which is what prompted me to try using the load.
In my own application, I have 3 levels of nested tabs that are being created dynamically via AJAX. In order to have the javascript for each of the tabs applied dynamically, I have nested load functions that are first initiated when the document is loaded.
So my template file has:
<script type="text/javascript" src="{{ MEDIA_URL }}js/tabs.js"></script>
<script type="text/javascript">
$(function() {
$('.overall_tabs').tabs({
load: initializeOverallTabs
});
});
</script>
My tabs.js file has:
function initializeOverallTabs(event, ui){
...
$('.lvl_two_tabs').tabs({
load: initializeTabLevel2
});
...
}
function initializeTabLevel2(event, ui){
...
// So on and so forth
...
}
Also, I recommend when working inside the loaded areas to make your references be specific to that pane. This was extremely important when working with tabs. The best way I found to do this is below.
In your tabs.js file:
function initializeOverallTabs(event, ui){
$panel = $(ui.panel);
$panel.find('lvl_two_tabs').tabs(...);
}
I found this question strangely coincidental! I recently explained my solution to a few developers to the same situation with the same Jquery/Django Environment. I hope that helped!
One way I decided myself for handling widgets from external pages is parsing the HTML of the other page, searching for scripts and executing them in the current page.
For example, there is a form with autocomplete widget on another page which is loaded and inserted to this page. The autocomplete widget should be activated with specific properties, like available choices:
<script type="text/javascript">
//<![CDATA[
$(function() {
$("#colors").autocomplete({
source: ['red', 'green', 'blue', 'magenta', 'yellow', 'cyan']
});
});
//]]>
</script>
Then in the current page I can have the following script which loads HTML and additionally collects all javascripts within it and executes them:
var oRe = /<script\b[^>]*>([\s\S]*?)<\/script>/gm;
$('#placeholder').load(
'/some/path/ #content>*',
function(responseText, textStatus, XMLHttpRequest) { // <-- callback function
var sScripts = "";
responseText.replace(
oRe,
function($0, $1) {
sScripts += $1;
return $0;
}
);
eval(sScripts);
}
);
One drawback here is that the current document should initially be loading all the libraries which might appear in the included forms. For example, in this case, it would be the jquery-ui including the autocomplete widget. I guess I could extend the code by searching for script tags which load external scripts and loading them in the current document if they are not present.