Rails wait for Turbolinks to be loaded to execute JavaScript import - ruby-on-rails

Ich want to call a JavaScript function within the application.html.erb. The function is imported and provided in the application.js:
import myFunction from 'myJSModule';
window.myFunction = myFunction;
I am using turbolinks and vite_rails and it seems like the JS function is called before the application.js is loaded and it is undefined.
Example:
application.html.erb
<html>
<head>
<script type="text/javascript">
</script>
<%= vite_javascript_tag "application", 'data-turbolinks-track': "reload" %>
<%= render "layouts/partial" %> # here I call the window.myFunction
</head>
<body></body>
</html>
layouts/_partial.html.erb
<script>
window.myFunction
</script>
Behaviour:
window.myFunction is undefined.
When I try to call the function in the console, after the side is loaded, then it is defined.
Also when I wait for the turbolinks:load event:
document.addEventListener("turbolinks:load", function() { window.myFunction }
The Problem with the event is, that it is triggered every navigation event, but I want to call the function only when the partial is re-rendered.
I recently migrated from webpack to vite. For webpack the solution seemed to be to move the render partial below the java_script_tag. Unfortunately, this does not work with with vite.

I could find a solution. Appending the attribute type of the script with module:
<script type="module">
window.myFunction
</script>

Related

How can I use LitElement with Webpacker (rails Webpack) for custom Web Components?

For those who may not know Rails now uses Webpacker, an integration with Webpack. There is a /packs dir that will get transpiled for use on a html page:
<script src="/packs/bundle-06cd8a4ad388853ace59.js"></script>
I have written a test web component.
import { LitElement, html } from 'lit-element';
class TestComponent extends LitElement {
render(){
return html`
<!-- template content -->
<p>ZZZZZZZZZZ</p>
`;
}
}
customElements.define('test-component', TestComponent);
I'm importing this into my packs/bundle.js as:
import '../web_components/test-component'
In the browser I get the error:
Uncaught TypeError: Class constructor LitElement cannot be invoked without 'new'
Webpack is turning this into:
var TestComponent = function (_LitElement) {
_inherits(TestComponent, _LitElement);
function TestComponent() {
_classCallCheck(this, TestComponent);
return _possibleConstructorReturn(this, (TestComponent.__proto__ || Object.getPrototypeOf(TestComponent)).apply(this, arguments));
}
_createClass(TestComponent, [{
key: 'render',
value: function render() {
return Object(__WEBPACK_IMPORTED_MODULE_0_lit_element__["b" /* html */])(_templateObject);
}
}]);
return TestComponent;
}(__WEBPACK_IMPORTED_MODULE_0_lit_element__["a" /* LitElement */]);
customElements.define('test-component', TestComponent);
I'm not really sure of how to make this work, but I would really love to get started using web components.
Please help!!
This seems to be a common issue.
Found a solution that works for me, hopefully thats helpfull to you too. Just add the following script tag inside application.html.erb. It's a polyfill, you can read more about it here
<!-- ADD POLYFILL -->
<script type="text/javascript" src="https://unpkg.com/#webcomponents/webcomponentsjs#2.4.3/custom-elements-es5-adapter.js"></script>
<!-- be sure that the webpack tags are AFTER the polyfill -->
<%= javascript_include_tag 'application' %>
<%= javascript_pack_tag 'application' %>
<%= yield %>
the polyfill can also be added through NPM, but I prefere to just use the CDN

Rails 5.2: How do I use page specific JS scripts when using Turbolinks?

I am writing some exploratory javascript for a page. It includes the following line:
dashboard_settings.addEventListener('ajax:complete', function(){
...
I have placed the script just before the closing body tag.
I notice that when I navigate to the page, the JS works as I expect it to and there are no error messages in the console. However, when I navigate away from the page, an error message displays in the JS console:
Uncaught TypeError: Cannot read property 'addEventListener' of null
I understand that this is something to do with turbolinks. However, I am unsure how to go about avoiding it without turning turbolinks off.
Is there any way at all that I can specify that the script loads for the one page only?
I have tried the following without success:
<%= yield :page_scripts %>
<% content_for :page_scripts do %>
<script>
document.addEventListener('turbolinks:load', function(){
...
You can add the listener only if the element exists.
if (dashboard_settings)
dashboard_settings.addEventListener('ajax:complete', function(){...})
Or, if you wan't to add it only for a specific page, you could add a yield statement on your layout and only set it's content con the desired page
#layout
<html>
<body>
...
</body>
<%= yield(:after_body) -%>
</html>
#your view
<%= content_for :after_body do -%>
<script>
document.addEventListener('turbolinks:load', function(){
dashboard_settings.addEventListener('ajax:complete', function(){...})
})
</script>
<%- end -%>

Turbolinks + lightbox (Rails 5)

I know this question has been asked to death but I still can't resolve things in my app. I'm running a rails 5 app (with turbolinks and the fancybox lightbox gem) and have some page-specific javascript to display images on model show pages:
application.html.erb
<head>
...
<%= javascript_include_tag "application", "data-turbolinks-track" => true %>
...
</head>
<body>
...
<%= yield :page_scripts %>
</body>
show.html.erb
<% content_for :page_scripts do %>
<script type="text/javascript">
$(document).on('turbolinks:load', function() {
$("a.fancybox").fancybox();
})
</script>
<% end %>
I still get the classic response whereby the image loads up nicely upon first page visit or page refresh. Navigating away and back again stops the lightbox from working (seems to just hang in my case).
How do I use the 'turbolinks:load' feature to get this to work properly?
just use {parent: 'body'} and it will fix the issue with Turbolink.
$("a.fancybox").fancybox({parent: 'body'});
credit: Kirill Platonov
You can change into below code.
$(document).ready(function() {
document.addEventListener("turbolinks:load", function() {
$("a.fancybox").fancybox();
})
});
I hope this help you. You can go to details through this link turbolinks.
I'm not sure if this fully qualifies as an answer, but given that the scenario in this link was very similar:
https://github.com/fancyapps/fancybox/issues/1413
....I ended up following their advice and removing turbolinks from the show page using data-turbolinks="false".
I am not familiar with tubolinks, but keep in mind that $("a.fancybox").fancybox() simply binds click event to current elements. So, you probably have to reattach it after navigating. Another possible solution would be to use event delegation using selector option, example (using v3.1):
$().fancybox({
selector : '[data-fancybox="images"]',
animationEffect : "fade"
});

Best way to include javascript in Rails via content_for

I have some pages in my Rails application that need one off bits of javascript to be included, ideally just before the </body> tag. There is no real need to have this javascript included on EVERY page since most don't use it. I've found a way to make this work, but I think the code is terrible.
How would you do the same thing or how would you refactor the existing code?
View simplified, sample code on gist.github.com:
https://gist.github.com/scottswezey/ffc7bf52041b976b710a
(Or see the same code below:)
application.html.erb (Layout):
<!DOCTYPE html>
<html lang="en">
<head>
...
</head>
<body>
...
<script>
$(function() {
<%= yield(:js) %>
});
</script>
</body>
</html>
some_view_file.html.erb (View):
<%
str = <<END_OF_STRING
$('.modal').modal()
END_OF_STRING
content_for :js do
str.html_safe
end
%>
Don't ever do it this way: JavaScript doesn't belong in HTML. Just put an appropriate <script> tag in the page, referring to an external JS file, something like this:
application.html.haml
!!!
%html
%head
= yield :javascript
%body
= yield
view file
- content_for :javascript do
= javascript_include_tag 'modal'
app/assets/modal.js
$(document).ready(function() {
$('.modal').modal();
}
This keeps everything nicely separated.
Is it possible to add a .js.erb partial with your JS and render it under the body?
edit: Check out this answer: https://stackoverflow.com/a/10113547/1283742

rails to_json.html_safe Uncaught SyntaxError: Unexpected identifier

I have in my stock rails 3.1 app's projects#index.html.erb a javascript section to reads like this:
<script type="text/javascript" charset="utf-8">
var projects = <%= #projects.to_json.html_safe -%>
$(function () {
// some other code...
});
</script>
However, Chrome's console complains that "Uncaught SyntaxError: Unexpected identifier"
now, if I remove anything that follows the "var projects" line like this
<script type="text/javascript" charset="utf-8">
var projects = <%= #projects.to_json.html_safe -%>
</script>
everything is fine and when one typed "projects" in the Chrome javascript console, I do get the list of objects
[ object, object, object, ..., object ]
I cannot work out what the issue is here, can you help?
Thanks!
try adding a ; semicolon at the end of statement

Resources