Rails Sprockets::CircularDependencyError with asset_path - ruby-on-rails

In my application, I have javascripts, with a refer to default.html. I.e.
function link() {
var url = '<%= asset_path("sidenav/default/default.template.html") %>';
}
This works, but, when I use the same to refer with asset_path in another file, i.e. a html (.html.erb of course, and last script, too is a .js.erb), I receive the error Sprockets::CircularDependencyError. A example of html
<a ng-href="<%= asset_path('sidenav/default/default.template.html') %>">Default</a>
If I dont use the reference to default in javascript, html works, but if I use the same refer in multiples files, this error occur.
Any help?

Related

How to call a javascript function inside a rails view?

I did just a upgrade from RAILS 5 to RAILS 6 and I see that all rails views are not able to call a javascript function as before in RAILS 5.
I have an external javascript file located under
app/javascript/packs/station.js
This is is embeded in in app/views/layouts/application.html.erb as
<%= javascript_pack_tag 'station' %>
This is the code how I call the javascrpt function from html.erb file :
<%= text_field_tag(:station_text_field, ... ,
onkeyup: "javascript: request_stations(); ") %>
When I try to call a function thats is part of the station.js then I get an error in the browser developmer view: ReferenceError: request_stations is not defined
But I can also see in the brwoser view, under Debugger :
Webpack / app/javascript / packs / station.js
and the javascript function I want to call.
So it seems that this script was loaded by the browser.
In contrast, when I just copy and paste these few lines that represent this javascript function direct into the template view file (...html.erb), something like :
<script>
function request_stations ()
{
alert("calling request_stations");
};
</script>
then - it works as expected !
By default, variables/functions defined inside JavaScript files that are packed by Webpacker will not be available globally.
This is a good thing, because it prevents global naming conflicts. Generally speaking, you don't want to reference javascript functions/variables from your view. You instead want to write JavaScript in a way that attaches functionality to DOM nodes using their id or other attributes.
Here is a basic example based on the code you provided:
# in your rails view
<%= text_field_tag(:station_text_field, ..., id: 'station-text-field') %>
// in your javascript
function request_stations() {
alert("calling request_stations");
};
const stationTextField = document.querySelector("#station-text-field");
stationTextField.addEventListener('keyup', (event) => {
request_stations();
});
Agree with mhunter's answer.
This post helped me get a grounding on this difference in Rails 6: https://blog.capsens.eu/how-to-write-javascript-in-rails-6-webpacker-yarn-and-sprockets-cdf990387463
What I don't see in your question is whether or not you did this in app/javascript/packs/application.js:
require("#rails/ujs").start()
require("turbolinks").start()
require("#rails/activestorage").start()
require("channels")
require("station")
The big difference in Rails 6 is that you have to deliberately:
require a JS file
deliberately export something from that file
deliberately import that something, in the file where you want to use it.
So if there is a function in station.js that you want to use, connect the steps above. Start with a simple function in station.js that fires upon DOMContentLoaded, and add a console.log("hey, station.js is alive and well"). If you don't see it, then something in those 3 steps is not right.
In pre-Rails6, you had a "garden" of JavaScript, just by virtue of being in the asset pipeline. In Rails 6, you have to be more deliberate.

Uncompiled partials in dust.js

I'm trying to figure out how to include an uncompiled dust partial in a dust view.
main.dust:
<div class="container">
{>"toolbar"/}
</div>
toolbar.dust:
<div class="row">
toolbar
{somevariable}
</div>
This attempts to load a compiled partial named toolbar.js not the uncompiled toolbar.dust template?
I found this post on here: How do you include a raw, uncompiled partial in dust.js? which will let me load files but any {variables} they contain are not replaced.
I don't want to have to compile views every time I change them. How can I include an uncompiled template with its variables replaced?
You can add an onLoad handler to Dust to tell it how it should try to load partials. By default, Dust expects you to have pre-registered all templates you're going to try to use.
More info: Loading Templates (it sounds like you want to load Uncompiled Templates)
Note: you shouldn't really do this in production, because compiling templates is much slower than rendering them. If you're using something like Express, take time to set up a build step or an Express plugin to compile for you. There are examples in the examples directory on the Dust GitHub repository.
An onLoad handler might look something like this (assuming you're running Dust on the server-- the idea is the same for the client):
dust.onLoad = function(templateName, callback) {
fs.readFile(templateName + '.dust', { encoding: 'utf8' }, function(err, data) {
callback(null, data);
});
};
Notice that callback uses the Node errback signature, so you can simplify this code to:
dust.onLoad = function(templateName, callback) {
fs.readFile(templateName + '.dust', { encoding: 'utf8' }, callback);
};
There is an example on our GitHub repo that does basically this.

asset_path returns wrong path for folder

I have .swf files under vendor/assets/images/swf/. I need the asset path of that folder.
But this (.js.coffee.erb)
#= soundmanager2
$ ->
soundManager.setup
url: '<%= asset_path "swf/" %>'
is rendering this (.js):
(function() {
var $ = jQuery;
$(function() {
return soundManager.setup({
url: '/swf/'
});
});
}).call(this);
I am using rails 4.0.0.rc1. I am on development mode. The path /assets/swf/soundmanager2.swf returns 200, while /swf/soundmanager2.swf returns 404. The helper image_path returns /images/swf/, but /images/swf/soundmanager2.swf also returns 404.
It is not worth the trouble, because you would have to disable digest to get the name of the files right. So the solution is to fix the library. In the case of Sound Manager 2, I did this:
Some CoffeeScript that I require:
#= require soundmanager2
jQuery ->
soundManager.swfNames =
"/soundmanager2.swf": "<%= asset_path('swf/soundmanager2.swf') %>"
"/soundmanager2_debug.swf": "<%= asset_path('swf/soundmanager2_debug.swf') %>"
"/soundmanager2_flash9.swf": "<%= asset_path('swf/soundmanager2_flash9.swf') %>"
"/soundmanager2_flash9_debug.swf": "<%= asset_path('swf/soundmanager2_flash9_debug.swf') %>"
soundManager.setup
debugMode: <% if Rails.env.development? %>true<% else %>false<% end %>
url: '/'
In my copy of soundmanager2.js (V2.97a.20130512), inside the definition of normalizeMovieURL:
url = ... // After url is set
url = sm2.swfNames[url]; // Workaround
on rails 4 all the asset helpers (image_path, asset_path and the likes) appear to only return a config.assets.prefix-prefixed path if the asset you're accessing is actually resolvable by sprockets.
put simply: it must exist in you asset path on the disk after precompilation.
therefore, asset_path('swf/') will not work since it is a directory and not a file.
also, i experienced the following: rails < 4 (sprockets, rather) copied original images (and thus swf files) and created a digested version of that same file. because of this soundmanager was still able to find the non-digested swf files even though i have config.assets.digest = true.
with rails 4, these original images are not copied anymore because they changed some precompile internals which leads soundmanager to throw up if it wants to fallback to flash.
to properly fix this soundmanager needs to be patched, like michelpm proposes.
for soundmanager-rails i started working on a fix including a proper patch for soundmanager which you can find over on github.

Phonegap + jQuery mobile: injecting html files from iOS app's Documents folder with jQuery Mobile

I am developing an app which uses both Phonegap and JQuery Mobile.
The app connects to an external server to check for new content/content updates (html and pdf files).
If needed, in iOS those files are successfully downloaded into the app /Documents folder.
The app then retrieves each content file's absolute path (file://localhost/var/mobile/Applications/APPID/Documents/subfolder) and creates listviews that link to each file's absolute path.
The problem I am having is the following: tapping a listview opens the linked page BUT not as an ajax call. The page loads but then no javascript (cordova.js, jquery.js app.js etc) is referenced in the page and hence I can't navigate back to the main menu. It seems like the jQueryMobile ajax navigation stops working when I open html files in the /Documents folder.
This happens only for the downloaded content in the /Documents folder (and hence outside Phonegap's www folder).
From the remote debugger, if I try to call the $.mobile.changePage('previousPage.html')* function, the console returns that $ is not defined, as if the page couldn't reference jQuery. But in none of the pages in the /www folder I need to re-reference the js files.
The app uses a multipage layout and each page has its own javascript after the <div data-role="page" id="pageid"> container.
Each .html in the /Documents folder is structured as a jQueryMobile page (with data-role attributes).
This is the code that creates the listviews:
<script type="text/javascript">
$('#content').live('pagebeforeshow', function(e) {
var curCat = parseInt(window.localStorage.getItem('currentCat'));
console.log('PAGE BEFORE SHOW: CONTENT');
db.db.transaction(function(tx) {
tx.executeSql('SELECT * FROM `content` WHERE `content`.`catID` = ?', [curCat],
function(tx, result) {
var path = window.localStorage.getItem('contentPath') + '/';
if (result.rows && result.rows.length) {
var html = '<ul data-role="listview" id="linkUl">';
for (var i = 0; i < result.rows.length; i++) {
var filename = result.rows.item(i).index_file.substr(result.rows.item(i).index_file.lastIndexOf('/'));
console.log(result.rows.item(i).id);
html += '<li id="' + result.rows.item(i).id + '">';
html += '<a href="' + path + filename +'">';
html += result.rows.item(i).title;
html += '</a></li>';
}
html += '</ul>';
console.log(html);
$('#contCnt').html(html);
$('ul#linkUl').listview();
}
},
function(tx, error) {
console.log(error.code);
var html = '<div id="errorDB">';
html += 'ERROR RETRIEVING FILES';
html += '</div>';
$('#contCnt').html(html);
}
);
});
});
$('div#content').live('pageshow', function(e) {
$('ul#linkUl').listview('refresh');
});
</script>
where the ContentPath variable is stored as a fileSystem object's directory.toUrl();
My fear is that jQueryMobile can't ajax-pull html from an external directory (the /Documents folder in iOS), or that I am missing some attribute or setting in order to do so.
Perhaps because I am using an absolute url? If so, how can I get the relative url from Phonegap's /www folder?
Do I have to declare something on the cordova.plist file?
Also, the downloaded content won't have to contain any js, they should be only plain html, but I need to keep jQuery Mobile header/footer and navigation system in all the pages.
I am using Cordova 2.2.0 and the latest releases of both jQuery and jQuery mobile.
Thanks in advance and sorry if something in the formatting goes wrong, I am new to SO (in case I'll edit asap).
You can open files from the documents folder (or any other folder the app has read access to).
There are two reasons that a link may not load as an ajax page
jQuery Mobile thinks that it isn't part of the app
There is a javascript error loading the page, which causes the default link click action to run instead of the ajax loader.
You could try using a call to $.mobile.changePage instead of just setting up the links - that gives you a little bit more visibility into what is going on.
I don't think a file url in a different folder should be treated as a different domain by jQuery Mobile, but to eliminate that possibility it should be reasonably easy to construct a relative url to the documents folder.
Solved, and thanks to Tom who led me through the right direction.
I guess the problem was that jQueryMobile was interpreting the absolute path as a an external link and thus the WebView was opening the html files as a new file, detaching it from the rest of the application.
What I did was substituting the absolute path file://localhost/var/mobile/Applications/APPID/Documents/subfolder
with a relative one, which in my case is './../../Documents/subfolder/filename.html
and now it works like a charm.

Rails 3 - How to use stylesheet's compiled name inside javascript file?

I have javascript file in rails app, where i pull in the stylesheet like this -
loadAssets: function(){
var stylesheet = document.createElement('link');
stylesheet.href = "<%= asset_path('lib/myStyles.css') %>";
stylesheet.rel = 'stylesheet';
stylesheet.type = 'text/css';
document.getElementsByTagName('head')[0].appendChild(stylesheet);
}
But asset_path helper used here, just gives plain myStyles.css name, where it should have been compiled name of that css file.
Because of this, i can't expire cache and get new myStyles.css file.
So, my question is, how can i get compiled name of this myStles.css file in my javascript file. Above code containing javascript file also gets compiled.
<%= asset_path('lib/myStyles.css'), :digest => true) %>

Resources