How to properly load jQuery alongside Stimulus in Rails 7 - javascript

I am learning Rails 7 with the new default Importmap jsmp syntax. I have tried to follow what few tutorials are out there, but am still unable to get either jQuery or Stimulus to respond to a simple alert function on page load.
I started over with a bare-bones Rails 7 application, ran bundle install, followed by rails turbo:install, rails stimulus:install and created a Static controller to route to a Landing page at Static#Landing.
My `landing.html.erb' file contains:
<h2>Landing</h2>
<p>
<script>
document.write("Vanilla JS is working...");
</script>
</p>
I get the expected string output, so I know Javascript is enabled in the Browser.
My importmap.rb file contains:
# Pin npm packages by running ./bin/importmap
pin "application", preload: true
pin "#hotwired/turbo-rails", to: "turbo.min.js", preload: true
pin "#hotwired/stimulus", to: "stimulus.min.js", preload: true
pin "#hotwired/stimulus-loading", to: "stimulus-loading.js", preload: true
pin "jquery", to: "library/jquery.js" #"https://ga.jspm.io/npm:jquery#3.6.0/dist/jquery.js"
pin "jquery-ui-dist", to: "library/jquery.js" #"https://ga.jspm.io/npm:jquery-ui-dist#1.13.1/jquery-ui.js"
pin "jqtree", to: "https://ga.jspm.io/npm:jqtree#1.6.2/lib/tree.jquery.js"
pin_all_from "app/javascript/controllers", under: "controllers"
The only lines I added were the 3 invoking a form of jQuery.
I next added the 3 import statements for jQuery, jQuery-ui-dist, and jqtree to the application.js file. The complete file contains:
// Javascript document
// Document Name: application.js
// Configure your import map in config/importmap.rb. Read more: https://github.com/rails/importmap-rails
import "#hotwired/turbo-rails"
import "jQuery"
import "jquery-ui-dist"
import "jqtree"
import "controllers"
// Configure Stimulus development experience
application.debug = false
window.Stimulus = application
// NOTE: make jquery global
window.$ = window.jQuery = jQuery;
$(function(){
window.alert("It looks like jQuery is too.");
});
export { application }
I next edited my app/assets/config/manifest.js file to include the following:
//= link_tree ../images
//= link_tree ../icons
//= link_directory ../stylesheets .css
//= link_tree ../../javascript .js
//= link_tree ../../javascript/library .js
//= link_tree ../../../vendor/javascript .js
However, even though inline <script>s work as outlined above, I get no response from either jQuery or Stimulus to the alert function in application.js. When I check my source code, I see the following:
<!DOCTYPE html>
<html>
<head>
<title>Testingjquery</title>
<meta name="viewport" content="width=device-width,initial-scale=1">
<meta name="csrf-param" content="authenticity_token" />
<meta name="csrf-token" content="LkQZA1mB-KU38K8S1lg_BMJWPx4HLonSyQ0s8W2vCotmw3aHIXZNDnPcJCtbgeXSTWrEuWJBZWlkOZcLe7Ru9w" />
<link rel="stylesheet" href="/assets/application-e0cf9d8fcb18bf7f909d8d91a5e78499f82ac29523d475bf3a9ab265d5e2b451.css" data-turbo-track="reload" />
<script type="importmap" data-turbo-track="reload">{
"imports": {
"application": "/assets/application-79a33b4392f09ada2a09f9c0b7de4d6479378090d98fa671caaef897dcec0de9.js",
"#hotwired/turbo-rails": "/assets/turbo.min-e5023178542f05fc063cd1dc5865457259cc01f3fba76a28454060d33de6f429.js",
"#hotwired/stimulus": "/assets/stimulus.min-b8a9738499c7a8362910cd545375417370d72a9776fb4e766df7671484e2beb7.js",
"#hotwired/stimulus-loading": "/assets/stimulus-loading-1fc59770fb1654500044afd3f5f6d7d00800e5be36746d55b94a2963a7a228aa.js",
"jquery": "/assets/library/jquery-58fa327d47526faff06ea7057a0022e9c42b2ca3a9aeea413f0e18176a63cd9f.js",
"jquery-ui-dist": "/assets/library/jquery-58fa327d47526faff06ea7057a0022e9c42b2ca3a9aeea413f0e18176a63cd9f.js",
"jqtree": "https://ga.jspm.io/npm:jqtree#1.6.2/lib/tree.jquery.js",
"controllers/application": "/assets/controllers/application-368d98631bccbf2349e0d4f8269afb3fe9625118341966de054759d96ea86c7e.js",
"controllers/hello_controller": "/assets/controllers/hello_controller-549135e8e7c683a538c3d6d517339ba470fcfb79d62f738a0a089ba41851a554.js",
"controllers": "/assets/controllers/index-2db729dddcc5b979110e98de4b6720f83f91a123172e87281d5a58410fc43806.js"
}
}</script>
<link rel="modulepreload" href="/assets/application-79a33b4392f09ada2a09f9c0b7de4d6479378090d98fa671caaef897dcec0de9.js">
<link rel="modulepreload" href="/assets/turbo.min-e5023178542f05fc063cd1dc5865457259cc01f3fba76a28454060d33de6f429.js">
<link rel="modulepreload" href="/assets/stimulus.min-b8a9738499c7a8362910cd545375417370d72a9776fb4e766df7671484e2beb7.js">
<link rel="modulepreload" href="/assets/stimulus-loading-1fc59770fb1654500044afd3f5f6d7d00800e5be36746d55b94a2963a7a228aa.js">
<script src="/assets/es-module-shims.min-d89e73202ec09dede55fb74115af9c5f9f2bb965433de1c2446e1faa6dac2470.js" async="async" data-turbo-track="reload"></script>
<script type="module">import "application"</script>
</head>
<body>
<h2>Landing</h2>
<p>
<script>
document.write("Vanilla JS is working...");
</script>
</p>
</body>
</html>
As you can see, jQuery is not being loaded at all and Stimulus is being loaded, but is unresponsive to any code references. I am new to Stimulus, intermediate with Javascript/jQuery and new to Rails 7. From what I have read, I like the conceptualization behind these radical shifts in Rails methodology, but so far, haven't been able to get it to work.
The files are all in their appropriate places according to the references to them. For illustration, this is my file tree:
-app
-assets
-config
-manifest.js
-javascript
-controllers
-application.js
...
-library
-jquery.js
-jquery-ui.js
-application.js
...
-config
-importmap.js
...
The final application.js file contains no code that I changed and is as follows:
import { Application } from "#hotwired/stimulus"
const application = Application.start()
// Configure Stimulus development experience
application.debug = false
window.Stimulus = application
// NOTE: make jquery global
window.$ = window.jQuery = jQuery;
export { application }
Granted, there are two application.js files that Importmaps/Stimulus/Turbo seem to rely upon, and perhaps I inserted some code into the wrong application.js file. If so, it was because the tutorials that I was following didn't specify or clarify which file to insert certain code into.
Does anyone see a fundamental error in my code or can suggest another approach? Thanks in advance.

Related

How to import or use external JS files and functions in my angular web application

I want to integration external code (html, js and css files) into my angular web application.
in this external code, the HTML files is just like this:
index.html
<html>
<header>
</header>
<body>
</body>
<script src="app/components/landing-page/js/bootstrap.min.js"></script>
<script src="app/components/landing-page/js/owl.carousel.min.js"></script>
<script src="app/components/landing-page/js/cbpAnimatedHeader.js"></script>
<script src="app/components/landing-page/js/theme-scripts.js"></script>
<!-- IE10 viewport hack for Surface/desktop Windows 8 bug -->
<script src="app/components/landing-page/js/ie10-viewport-bug-workaround.js"></script>
<script type="text/javascript" src="app/components/landing-page/js/imageComparisonSlider.js"></script>
<script>
/*Execute a function that will execute an image compare function for each element with the img-comp-overlay class:*/
initComparisons();
</script>
<html>
as you see, there are several javascript files, and a funciton initComparisons() will be called.
If I only double click index.html, everything works fine. But I copy this html code in one component.html, that was not working. I can not see any animation.
I have googled some solutions,
and I have change my angular.json file just like this:
"scripts": [
"src/app/components/landing-page/js/imageComparisonSlider.js",
"src/app/components/landing-page/js/owl.carousel.min.js",
"src/app/components/landing-page/js/cbpAnimatedHeader.js",
"src/app/components/landing-page/js/theme-scripts.js"
]
and also import all js files in index.html in my angular web application
<script src="app/components/landing-page/js/imageComparisonSlider.js"></script>
<script src="app/components/landing-page/js/owl.carousel.min.js"></script>
<script src="app/components/landing-page/js/theme-scripts.js"></script>
<script src="app/components/landing-page/js/cbpAnimatedHeader.js"></script>
and in the component.ts, I also do this:
import initComparisons from './js/imageComparisonSlider.js';
ngOnInit() {
this.isLoggedIn = this.authService.isLoggedIn;
initComparisons();
}
I added some code in stackblitz;
https://stackblitz.com/edit/angular-qowfwy?file=angular.json
but it was not working.
can somebody help me and give me some suggestion.
Best Regards,
Leo
If you want to use external js in your angular project you have to import in your angular.json in the "scripts": [] area that will be allow you to bring the js and make the build after without problem.
After putting the external scripts in angular.json (paths correct and everything), in component you should
declare const initComparisons;
// ...
ngOnInit() {
initComparisons();
}

Can't understand laravel Mix works on my custom js/css files

I'm new to Laravel and whole framework stuff.
I do (may) understand a part of how the page rendered via laravel, but even after extensive search, I do not understand how laravel mix works.
Suppose that there is a page requires a global js and css library (lets specify jQuery and bootstrap)
Also the page requires custom js file like someJsTools.js.
Elementary speaking, in the past, those files referenced via <script src="blah"></script> and <link rel="blah" /> inside head tag and I used to it. In this env, all I have to do is specify those tags page by page.
// pageA requires jQuery.js, bootstrap.css and one CUSTOM JS file imatrouble.js
<head>
<link rel="stylesheet" herf="bootstrap.css"/>
<script src="jquery.js"></script>
<script src="imatrouble.js"></script>
</head>
//pageB requires jQuery.js, bootstrap.css and two custom js files.
<head>
<link rel="stylesheet" herf="bootstrap.css"/>
<script src="jquery.js"></script>
<script src="imatrouble.js"></script>
<script src="withimatroubleimadisasterlikewhateveryoucanimagine.js"></script>
</head>
PageA and PageB both requires common jQuery.js and bootstrap.css file. From what I learn, laravel Mix combine all js files into one and I don't get it here.
Problem 1 - One file do everything?
If it is true that "mix" things all together as one file, then how this one file could handle all of this different requirements seperatelly? I believe that my knowledge is wrong and its from my incorrect understanding of laravel mix and perhaps webpack mechanism.
Problem 2 - How can I manage all different page and every different situation?
Whether the problem above is originated from my missunderstanding or not, I cannot figure out what part of I (will) do could cause differences between pages. If mix only works for common global library, then all I have to do is just load custom js/css files manually. I currently assume that it is highly unlikely.
Please, someone help me to escape this chaos.
Have a good day.
It is purely based on your requirements. It depends on how you are customising your assets file.
For example :
Jquery, Angular,Bootstrap,Font Awesome is common for all your pages. So what I usually do is. I combine all css files to one file and all js files to one. Like below..
CSS mix
elixir(function(mix) {
mix.styles([
"libraries/bootstrap.css",
"libraries/font-awesome.min.css",
"custom/default.css",
], 'public/assets/css/common.css');
});
JS mix
elixir(function(mix) {
mix.scripts([
"libraries/jquery-1.10.2.js",
"libraries/bootstap.js"
"libraries/angular.js",
"libraries/angular-animate.js",
"custom/defaut.js"
], 'public/assets/js/common.js');
});
Suppose some pages need specific dependency[product, orders...etc]. For instance if product page needs wow.js, product.js and wow.css,product.css
CSS mix
elixir(function(mix) {
mix.styles([
"libraries/wow.css",
"custom/product.css",
], 'public/assets/css/product.css');
});
JS mix
elixir(function(mix) {
mix.scripts([
"libraries/wow.js",
"custom/product.js"
], 'public/assets/js/product.js');
});
So final laravel mix file looks like below
gulpfile.js
var elixir = require('laravel-elixir');
elixir.config.sourcemaps = true;
/**
* Global CSS MIX
*/
elixir(function(mix) {
mix.styles([
"libraries/bootstrap.css",
"libraries/font-awesome.min.css",
"custom/default.css",
], 'public/assets/css/common.css');
});
/**
* Global JS MIX
*/
elixir(function(mix) {
mix.scripts([
"libraries/jquery-1.10.2.js",
"libraries/bootstap.js"
"libraries/angular.js",
"libraries/angular-animate.js",
"custom/defaut.js"
], 'public/assets/js/common.js');
});
/**
* Product CSS MIX
*/
elixir(function(mix) {
mix.styles([
"libraries/wow.css",
"custom/product.css",
], 'public/assets/css/product.css');
});
/**
* Product JS MIX
*/
elixir(function(mix) {
mix.scripts([
"libraries/wow.js",
"custom/product.js"
], 'public/assets/js/product.js');
});
Now all your assets files are ready. Now you need to include wherever you want.
Suppose on your homepage you only requires common.js and common.css files.
homepage.blade.php
<head>
<link rel="stylesheet" href="{{ asset('assets/css/common.css') }}"/>
<script type="text/javascript" src="{{ asset('assets/css/common.js') }}"></script>
</head>
On the product page, you require both common and product assets file dependency. Include like below
product.blade.php
<head>
<link rel="stylesheet" href="{{ asset('assets/css/common.css') }}"/>
<link rel="stylesheet" href="{{ asset('assets/css/product.css') }}"/>
<script type="text/javascript" src="{{ asset('assets/js/common.js') }}"></script>
<script type="text/javascript" src="{{ asset('assets/js/product.js') }}"></script>
</head>

Require css files with require-css.js in specific order

I have the following structure:
components/
----requirejs
----require-css
----flatUi
--------/css
------------flat.css
menuJSlib/
----js/
--------menu.js
----css/
--------menu.css
samples
----demo/
--------js/
------------main.js
--------css/
------------main.css
--------index.html
I try to load all content stuff in folder menuJSlib with requirejs and require-css in my demo project.
In my index.html in my demo I include requirejs library.
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8" />
<title></title>
</head>
<body>
<script data-main="js/main" src="../../components/requirejs/require.js"></script>
</body>
</html>
In main.js file in my demo project I have the following code:
require.config({
baseUrl: './../../',
map: {
'*': {
'css': 'components/require-css/css'
}
},
paths: {
GameMenu: 'lib/js/menu',
'layout': 'samples/requirejs_demo/css/main'
}
});
require(['GameMenu', 'css!layout'], function(GameMenu) {
}
And the code in menu.js in my menuJSlib folder is:
define('GameMenu', ['css!components/flat-ui/dist/css/flat-ui.min.css',
'css!lib/css/main.css'], function() {
return GameMenu;
});
How you can see the menu.js have dependencies to css files. Ok all that stuffs works fine, but the problem is that I can find a way to order the css files currectly in this case on dependencies.
As you can see, I try to make order, which get the all depends in menu.js, and next load the main.css in demo folder. In that way main.css in demo will owveride some css rules, which I needed. But the order aways follows:
demo/css/main.css
flatui/css/flat.css
menuJslib/css/main.css
I wont to make this order, without remove the css dependencies in menu.js
flatui/css/flat.css
menuJslib/css/main.css
demo/css/main.css
Is this is posible in that case?

Ace editor "define is not defined"

I'm trying to add the ace editor to my app. I downloaded it from github, dropped the "ace/lib/ace" directory into my app's directory, included:
<script src="ace/lib/ace/ace.js" type="text/javascript" charset="utf-8"></script>"
in my body tag and:
editor = ace.edit "editor"
in my script tag. I've tried to load the page in Chrome and Firefox and I get "define is not defined" in ace.js:46. The line in ace.js is:
define(function(require, exports, module) {
Does anyone know why ace is expecting the define() function to exist and why it's not finding it? Here's my source:
<html>
<body>
<div id="editor">some text</div>
<script src="ace/lib/ace/ace.js" type="text/javascript" charset="utf-8"></script>
<script>
var editor = ace.edit("editor");
</script>
</body>
</html>
I hacked it by putting window.define = ace.define; in my DOMload handler.
If you already have the source, then it is pretty easy to do still. Just go in the directory where you copied all the ace source.
Then, do:
npm install
node Makefile.dryice.js
See the wiki for additional details
https://github.com/ajaxorg/ace/wiki/Building-ace
You are getting this error because the RequireJS JavaScript library has not been included in your page.
To fix this either use an ace build or include RequireJS in your page.
If you choose to include RequireJS your html fragment will look something like this:
<!-- Editor will go here -->
<div id="editor"></div>
<!-- Load RequireJS -->
<script src="lib/requirejs/require.js"></script>
<!-- Initialize ace -->
<script>
// Tell RequireJS where ace is located
require.config({
paths: {
'ace': 'lib/ace'
}
});
// Load the ace module
require(['ace/ace'], function(ace) {
// Set up the editor
var editor = ace.edit('editor');
editor.setTheme('ace/theme/monokai');
editor.getSession().setMode('ace/mode/javascript');
// etc...
});
</script>
In React, if at all you are importing anything from ace-builds, your import order matters.
It should be like this
import AceEditor from 'react-ace';
import 'ace-builds/src-noconflict/mode-json';
Not like this
import 'ace-builds/src-noconflict/mode-json';
import AceEditor from 'react-ace';
Alternatively you can use a cdn
http://cdnjs.com/libraries/ace/
http://www.jsdelivr.com/#!ace
And replace
<script src="/ace-builds/src-noconflict/ace.js" type="text/javascript" charset="utf-8"></script>
With something like
<script src="http://cdnjs.cloudflare.com/ajax/libs/ace/1.1.3/ace.js" type="text/javascript" charset="utf-8"></script>

Building Dojo 1.7 for Mobile PhoneGap App

I am trying to build dojo 1.7 to use in my phonegap application. I am currently using dojo 1.6.1. I built my current dojo.js file by going to build.dojotoolkit.org and selecting everything under dojox.mobile as well as a dojo.store.JsonRest module. That works great.
My issue is trying to create a profile file to create a build similiar to the one I got from the dojo build website.
I downloaded dojo 1.7 stable release src.
I went into the buildScripts folder from the command line and tried to run a build with the following command:
>build profile=path/myMobileProfile.js action=release releaseName=test
I used the sample profile from the profiles folder:
dependencies = {
stripConsole: "normal",
layers: [
{
name: "dojo.js",
customBase: true,
dependencies: [
"dojox.mobile.parser",
"dojox.mobile",
"dojox.mobile.compat"
]
},
{
name: "../dojox/mobile/_compat.js",
layerDependencies: [
"dojo.js"
],
dependencies: [
"dojox.mobile._compat"
]
}
],
prefixes: [
[ "dijit", "../dijit" ],
[ "dojox", "../dojox" ]
]
}
It built with no errors. The dojo.js generated from the build was then dropped into my phonegap application. I changed my index file to the following just for testing:
<!DOCTYPE HTML>
<html>
<head>
<title>PhoneGap</title>
<meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1, minimum-scale=1, user-scalable=no"/>
<meta http-equiv="cache-control" content="no-cache"/>
<meta http-equiv="pragma" content="no-cache"/>
<link rel="stylesheet" href="http://ajax.googleapis.com/ajax/libs/dojo/1.6/dojox/mobile/themes/android/android.css" type="text/css" media="screen" title="no title">
<script type="text/javascript" src="libs/dojo/dojo/dojo.js" djConfig="parseOnLoad:true"></script>
<script type="text/javascript" charset="utf-8" src="phonegap-1.1.0.js"></script>
</head>
<body style="background-color:white">
Phonegap
</body>
</html>
Everytime I run the app I get a white page. When I replace the dojo.js file with my working copy I see the Phonegap output.
I would like to be able to use dojo 1.7 mobile and some of the new features such as the SpinWheel.
Can someone please help me with my build?
Thanks
I'm having the same type of issues. I think it has to do with the new AMD loader.
It seems as if the parser is not parsing the declarative widgets but rather is waiting to do it on demand or that it just never gets called.
I did find some docs that mention we should use dojo/ready, but couldn't get it to work with it and phoneGap. The same code works fine on a desktop without phoneGap, which is weird.
See live docs: http://livedocs.dojotoolkit.org/dojo/ready
As well as: http://livedocs.dojotoolkit.org/loader/amd
"To put the loader in the AMD mode, set the async configuration variable to truthy:
<script data-dojo-config="async:1" src="path/to/dojo/dojo.js"></script>
<script>
// ATTENTION: nothing but the AMD API is available here
</script>
Note that you can only set the async flag before dojo.js is loaded, and that in AMD mode, neither Dojo nor any other library is automatically loaded - it is entirely up to the application to decide which modules/libraries to load."
For me this Profile works fine with dojo 1.7 and PhoneGap:
dependencies = {
selectorEngine: "acme",
layers: [
{
// This is a specially named layer, literally 'dojo.js'
// adding dependencies to this layer will include the modules
// in addition to the standard dojo.js base APIs.
name: "dojo.js",
dependencies: [
"dijit._Widget",
"dijit._Templated",
"dojo.fx",
"dojo.NodeList-fx",
//this wasn't included in the standard build but necessary
"dojo._firebug.firebug",
//my used dojo requirements
"dojox.mobile.parser",
"dojox.mobile",
"dojox.mobile.Button",
"dojox.mobile.SwapView",
"dojox.mobile.ScrollableView",
"dojox.mobile.TabBar",
"dojox.mobile.SpinWheelTimePicker",
"dojox.mobile.compat"
]
}
],
prefixes: [
["dijit", "../dijit" ],
["dojox", "../dojox" ]
]
}
But with this Profil the CSS Files are not included, so you have to copy all the CSS folder structure.
My HTML file looks like this:
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 5.0//EN" "http://www.w3.org/TR/html5/strict.dtd">
<html>
<head>
<meta name="viewport" content="width=device-width,initial-scale=1,maximum-scale=1,minimum-scale=1,user-scalable=no"></meta>
<meta name="apple-mobile-web-app-capable" content="yes"></meta>
<title>dojox.mobile Demo</title>
<link href="css/themes/iphone/iphone.css" rel="stylesheet" type="text/css" />
<script type="text/javascript" src="phonegap.js" charset="utf-8"></script>
<script type="text/javascript" src="dojo.js" djConfig="isDebug:true, parseOnLoad:true"></script>
<script type="text/javascript">
require([
"dojox/mobile/parser", // (Optional) This mobile app uses declarative programming with fast mobile parser
"dojox/mobile", // (Required) This is a mobile app.
"dojox/mobile/Button",
//Some other dojo Widgets
"dojox/mobile/compat" // (Optional) This mobile app supports running on desktop browsers
],
function(parser, mobile, compat){
//Optional module aliases that can then be referenced inside callback block
}
// Do something with mobile api's. At this point Dojo Mobile api's are ready for use.
);
//to make sure dojo and PhoneGap was loaded use this
document.addEventListener("deviceready", init(), false);
function init(){
dojo.ready(function(){
//do something
});
}
</script>
</head>
<body>
</body>
HTH
This problem is solved with Dojo 1.7.2
Also found this: http://livedocs.dojotoolkit.org/dojo/parser
and tried to force parse on the entire dom or just the specific element, but still nothing.
I did find the following that may shed some more light on this issue: "Dojo and PhoneGap? both have their own event to acknowledge when the page is ready. We are finding that the dojo.ready is too early for things like deviceDetection APis when running within a PhoneGap? container and it would be better off being done inside of the PG deviceReady method. ..."
Full thread can be found here: http://bugs.dojotoolkit.org/ticket/14062
It is discussing dojo 1.6.1, but sounds like some changes in dojo 1.7 may suffer more severe reactions. There is a suggested workaround, but I'm not sure it will solve the 1.7 issue.

Categories