Rails 7 Importmap Bootstrap - ruby-on-rails

So, I've set up my rails 7 application by doing the following.
rails new .
To add bootstrap I've implemented it with importmap (no webpacker) as following
./bin/importmap pin bootstrap
which loaded these lines (I added the preload: true)
pin 'bootstrap', to: https://ga.jspm.io/npm:bootstrap#5.1.3/dist/js/bootstrap.esm.js', preload: true
pin '#popperjs/core', to: 'https://ga.jspm.io/npm:#popperjs/core#2.11.2/lib/index.js', preload: true
then on my application.js, I added
import "bootstrap"
import "#popperjs/core"
and I was able to use the toast element by doing as follow
# toast_controller.js
import { Controller } from "#hotwired/stimulus"
import * as bootstrap from 'bootstrap'
// Connects to data-controller="toast"
export default class extends Controller {
connect() {
const toast = new bootstrap.Toast(this.element, {
delay: 5000,
animation: true,
autohide: true
})
toast.show()
}
}
and it was working well, But I started facing issues with bootstrap when trying to use the tooltip on my menu
# layout_controller.js
import { Controller } from "#hotwired/stimulus"
import * as bootstrap from 'bootstrap'
// Connects to data-controller="toast"
export default class extends Controller {
connect() {
[].slice.call(document.querySelectorAll('[data-bs-togle-secondary="tooltip"]'))
.map(function (tooltipTriggerEl) {
return new bootstrap.Tooltip(tooltipTriggerEl, {placement: "right"})
})
}
}
the reason for it is because popperjs uses process.env.NODE_ENV which doesn't exist since I didn't set up webpacker
so I had to do something ugly on my application layout and add it like this
<script>
const process = {}
process.env = {}
process.env.NODE_ENV = "<%= Rails.env %>"
</script>
Is there a better approach/fix for this kind of issue?

At the moment of writing this 11/04/2022 there is not a clear solution for now and your mentioned approach with defining in const process in tag works, one issue with that is that with importmaps+turbolink approach it will rise "Uncaught SyntaxError: redeclaration of const process".
Probably for now it's best to just follow this thread https://github.com/rails/importmap-rails/issues/65 as this issue is mentioned in the comments there. One of the quick fixes mentioned there is similar to yours: https://github.com/rails/importmap-rails/issues/65#issuecomment-999939960. With a combined solution of yours and the one in the comments, it seems that this works best for now and there is no redeclaration error.
<script>window.process = { env: { NODE_ENV: "#{Rails.env}" } }</script>
If the solution is introduced for this issue either on importmaps or popperjs side, then please update this comment or introduce a new answer.

Related

Import googlemaps via importmaps in Rails 7

What is the way to go to load GoogleMaps via importmaps in Rails7?
I've seen folks import it via the script-tag like this:
<script src="https://maps.googleapis.com/maps/api/js?key=<%= Rails.application.credentials[:p10_google_maps_js_api_key] %>&callback=initMap" async defer data-turbolinks-eval="false"></script>
but I would like to take advantage of the new importmap feature of Rails 7. Unfortunately I don't understand how i could trigger the initMap callback without using it as a script.
So there is a Stimulus component that you could use stimulus-places-autocomplete. However, you could easily implement this yourself and save you the trouble of pulling in a dependency.
At the end of the day, the google callback needs to be fired off. The way most have gotten around this is by creating an event and attaching that to the window. You then add a data-action to your views controller div that will look for this even and fire a callback of its own. That callback being an initializer within your Stimulus controller itself.
##########places_controller.rb##########
import { Controller } from "#hotwired/stimulus";
export default class extends Controller {
static targets = ["street", "city", "state", "zip"];
connect() {
// Can also initialize this with a CustomEvent
const event = new Event('google-maps-callback', {
bubbles: true,
cancelable: true,
})
window.dispatchEvent(event);
}
// additional actions
}
##########index.html.erb##########
<div
data-controller="places"
data-action="google-maps-callback#window->places#initMap"
>
<%# view code here %>
</div>
This still is not a solution that I love. However, its the workaround that we have for now.

RAILS 6 - how to use EasyAutocomplete

I try the last hours to integrate EasyAutocomplete into RAILS 6. But not so easy.
What I did :
Install Javascript Package with yarn:
# yarn add easy-autocomplete
Add this in the file app/javascript/packs/application.js
import “easy-autocomplete”
Add this in the file app/assets/stylesheets/application.css
*= require easy-autocomplete
*= require easy-autocomplete.themes
Then start the Rails Server and refresh the Web Page.
Then try to use it. Go into the developer console and type :
var options = {
data: ["otto", "hans", "paula"]
};
$("#task_name_search_field").easyAutocomplete(options);
task_name_search_field was previously defined as id :
<input type="search" class="form-control" id="task_name_search_field">
I got this message:
TypeError: $(...).EasyAutocomplete is not a function
Any idea ?
I had the same problem. Turbolinks does not give access to the script, the code needs to be run after it is loaded, something like this:
document.addEventListener("turbolinks:load", function() {
var options = {
data: ["otto", "hans", "paula"]
};
$("#task_name_search_field").easyAutocomplete(options);
});
In order for the autocomplete to work, you need to add a script file to the application.js like this:
require("packs/youfile")
If it helps you, here is an example of my code:
document.addEventListener("turbolinks:load", function() {
$input = $("[data-behavior='autocomplete']")
var options = {
getValue: "full_name",
url: function(phrase) {
return "/search.json?q=" + phrase;
},
categories: [
{
listLocation: "cameras",
header: "<strong>Cameras</strong>",
}
],
list: {
onChooseEvent: function() {
var url = $input.getSelectedItemData().url
$input.val("")
Turbolinks.visit(url)
}
}
}
$input.easyAutocomplete(options)});
I suppose you wouldn't have included jquery in your application.js. You need to do explicitly as it doesn't get included automatically with rails 6 app.
I ran into a similar issue. I'm a n00b at Webpacker, but by default it doesn't seem to compile in the same order in which plugins are included.
To get around the issue I did this workaround which just wraps the plugin code in an anonymous jQuery function, like so:
(function($) {
//Eac plugin code
})(jQuery);
https://github.com/pawelczak/EasyAutocomplete/issues/200#issuecomment-212277620
Maybe there is a way in the configs to force compilation in the correct order. This looks promising https://stackoverflow.com/a/43005332/148390
Add script-loader to package.json then add
import 'script-loader!jquery/dist/jquery.min'
in app/javascripts/application.js
Check in your browser that $().jquery yields a proper result.

Material UI Themes and Hyperstack

Material UI (React) uses a theaming solution where the theme object is created in JS then passed into the top level component. Creating the theme object in Opal can be complicated as the Material component expects a JS function to be passed in which actually creates the theme on the fly.
Has anyone got an example of this working well?
After some experimentation, I got this working well by mixing JS and Opal code so here is the solution in case anyone else comes up with this. (There may be a better 'more Opal' solution, so if there is please do post an alternative answer, but the one below does work well.
First import the JS libraries with webpack:
import { MuiThemeProvider, createMuiTheme } from '#material-ui/core/styles';
import indigo from '#material-ui/core/colors/indigo';
import pink from '#material-ui/core/colors/pink';
import red from '#material-ui/core/colors/red';
global.createMuiTheme = createMuiTheme;
global.MuiThemeProvider = MuiThemeProvider;
global.indigo = indigo;
global.pink = pink;
global.red = red;
Add the following to your Javascript assets:
// app/assets/javascripts/my_theme.js
const my_theme = createMuiTheme(
{ palette: {
primary: { main: pink[500] },
secondary: { main: indigo[500] }
}
});
Then provide the theme in your top-level Component code:
class MyRouter < HyperComponent
include Hyperstack::Router
render(DIV) do
MuiThemeProvider(theme: `my_theme` ) do
CssBaseline do
...
end
end
end
end

Need help importing Electron in Aurelia app

I'm using one of the skeleton-navigation, skeleton-typescript.
I'm trying to import Electron.remote so I can close the electron window from within the JS. This is what I have in config.js:
paths: {
"*": "dist/*",
"github:*": "jspm_packages/github/*",
"npm:*": "jspm_packages/npm/*",
"node_modules:*": "node_modules/*"
},
map: {
"electron": "node_modules:electron/index.js",
}
and in my JS file I import like this:
import * as electron from 'electron';
but I get error regarding fs.js not found in path:
Error: (SystemJS) XHR error (404 Not Found) loading http://localhost:9000/dist/fs.js
Can someone help on how I can fix this issue?
depends on the loader/bundler strategy you picked
electron has nodes require() defined.
you want to redefine that before booting up your app that relies on AMD require
https://github.com/electron/electron/issues/303
TL;DR
you want to assign nodes require to another variable
window.node_require = require
and then delete the original
delete require
only after this you reference a script with your app
and inside your app you use node_require() to load node modules
here is the relevant comment on: supporting electron modules in aurelia
This is how I fixed my issue for Aurelia Skeleton Typescript with JSPM & SystemJS: I put in index.html head which is my entry:
<script type="text/javascript">
window.node_require = require;
delete window.require;
delete window.exports;
delete window.module;
</script>
Then I set nodeIntegration: true for BrowserWindow.
And in my TS file:
declare global {
interface Window {
node_require: any;
}
}
var remote: any;
if (typeof window.node_require == "function") {
remote = window.node_require('electron').remote;
}
closeApp() {
var window = remote.getCurrentWindow();
window.close();
}

jQuery UI passthrough official example: ui is not defined

I copied the jQuery passthrough example from here http://angular-ui.github.com to this fiddle http://jsfiddle.net/ilyaD/Xe48t/3/ and it is not working. I am getting ui is not defined
this is the JS:
angular.module('ui.config', []).value('ui.config', {
// The ui-jq directive namespace
jq: {
// The Tooltip namespace
tooltip: {
// Tooltip options. This object will be used as the defaults
placement: 'right'
}
}
});
what did I miss?
UPADTE:
I added ui to angular.module('ui.config', ['ui']).value('ui.config', { but still not working http://jsfiddle.net/Xe48t/9/
Actually, you're not supposed to declare the value on the ui.config module. You declare it on your own app and it will take precedence:
angular.module('myApp', ['ui']).value('ui.config', {
jq: {
// whatever
}
});
Note that this is defined on myApp
It also helps if you:
run your JS in the right place (head)
have AngularJS initialize properly (using ng-app)
and load the bootstrap lib and css
http://jsfiddle.net/Xe48t/12/
Make sure you're including the AngularUI javascript file, and then you want to use the string "ui".
angular.module('ui.config', ['ui'])
You missed the ui dependency:
angular.module('ui.config', ['ui']).value('ui.config', {
http://jsfiddle.net/Xe48t/4/

Resources