How to add font awesome to rails 7 (jsbundling)? - ruby-on-rails

Sorry this topic has been covered many times. However, I just created a new rails 7 app using esbuild, added font-awesome-sass to the gem file, created application.css.scss (already had application.bootstrap.scss) and put "#import 'font-awesome';" in there.
I then added the following to a view page:
<i class="fa-sharp fa-solid fa-house"></i>
<%= icon 'fa-brands', 'font-awesome' %>
No page errors, no console errors---but, the icons are not visible on the page (code is in the source). I've been mindlessly trying to figure this out---but, can not figure out why this is not working.
Have I missed any step?
I've verified installation instructions for the gem, I also tried skipping the gem and installing fontawesome via yarn. Nothing has gotten icons to show up in the view.

As are you using esbuild, you can install fontawesome with npm
yarn add #fortawesome/fontawesome-free
and import it in your javascript/application.js
import "#fortawesome/fontawesome-free/js/all";
Just remember to use only the free icons, fa-sharp is not free

I have just tried with this and everything is fine, Could you try it?
Updated Steps:
create project with cmd rails _7.0.4_ new demo-rails-with-react-frontend -j esbuild
add font awesome following this
create component to test following this
The result (image below)
my file app/javascript/components/application.tsx:
import * as React from "react";
import * as ReactDOM from "react-dom";
interface AppProps {
arg: string;
}
const App = ({ arg }: AppProps) => {
return <i class="fa-solid fa-house"></i>;
};
document.addEventListener("DOMContentLoaded", () => {
const rootEl = document.getElementById("root");
ReactDOM.render(<App arg="Rails 7 with ESBuild" />, rootEl);
});
ENV
ruby 3.0.4
rails 7.0.4
font-awesome-sass 6.2.0

Related

TinyMCE w/Rails 6 and webpacker - no icons, turbolinks

We are trying to upgrade our app to Rails 6 with Webpacker (and Stimulus). Things are going fine except for TinyMCE. We have 2 problems, I will ask them in separate SO questions.
We installed TinyMCE using
yarn add tinymce
and have version 5.3.0
In our stimulus controller header we have:
import tinymce from 'tinymce/tinymce';
import 'tinymce/themes/silver';
import 'tinymce/skins/ui/oxide/skin.min';
import 'tinymce/skins/ui/oxide/content.min';
import 'tinymce/plugins/paste';
import 'tinymce/plugins/link';
And then in the controller connect block we have:
connect() {
console.log('gonna reload');
require.context(
'!file-loader?name=[path][name].[ext]&context=node_modules/tinymce&outputPath=js!tinymce/skins',
true,
/.*/
);
tinymce.init({
selector: '.tinymce',
plugins: ['paste', 'link'],
skin: false
});
}
This is code that essentially works, except that in the console I see:
VM40 application-68201fac0dcbbcb543e0.js:213771 GET https://xxx.ngrok.io/packs/js/icons/default/icons.js net::ERR_ABORTED 404 (Not Found)
VM40 application-68201fac0dcbbcb543e0.js:224775 Failed to load icons: default from url https://xxx.ngrok.io/packs/js/icons/default/icons.js
Do we need another require.context to handle the loading of those?
It looks like TinyMCE no longer loads the icons dynamically, so Webpack must be instructed to include them in the bundle manually. Adding the icons import after the tinymce import worked for me:
import 'tinymce/icons/default';

Vue.js app works in development but not mounting template in production with Rails 5.2.0 / Webpacker - blank screen with no errors in console

I'm using Rails 5.2.0 and Webpacker gem to deploy a Vue application.
The show.html.erb file is very simple:
<div data-behavior="vue-app"><MyComponent></MyComponent></div>
And then in my entry pack, packs/my_vue_app.js:
import TurbolinksAdapter from 'vue-turbolinks';
import Vue from 'vue/dist/vue.esm'
Vue.use(TurbolinksAdapter);
import MyComponent from '../components/my_app/index.vue'
document.addEventListener('turbolinks:load', () => {
var element = $('[data-behavior="vue-app"]');
if(!element) { return };
console.log('Initializing Vue');
const app = new Vue({
el: element[0],
data: {
},
components: { MyComponent }
})
})
In development, everything works absolutely fine. The app is mounted and functional.
But in production, after the page load and JS runs, <div data-behavior="vue-app"> is removed from the paging, leaving only <!-- --> in it's place.
In the console, there are absolutely no errors. I can confirm using DevTools that the pack js file is loaded, and it was parsed, since the console.log is printed in the console.
Heck, the proof that Vue is working is that the entire <div> where it was mounted was removed from DOM after JS parsing.
The weirdest thing of all is that I could get the app to mount ONCE, by attaching a debugger on the console.log line and turning it off while the debugger paused execution. Even tough I saw the app mounting that time, I could not get it to mount later on, even fiddling with the debugger again ... it's really, really weird.
These are the versions of package.json:
"vue": "^2.5.16",
"vue-loader": "14.2.2",
"vue-template-compiler": "^2.5.16",
The Rails app is brand new, with no config other than the default.
Webpacker gem is 3.5.3 and Rails is 5.2.0.
After spending a really long time on this, I only found this github issue: https://github.com/rails/webpacker/issues/1520
EDIT: I'm providing a link to the real, production app where this bug is happening: https://planilha.tramitacaointeligente.com.br/planilhas/ED2sUXz32-R9CJKdkmtf8Q
You'll see it's not mounting. Here's the same page in development:
I eventually managed to solve it by changing how the Vue app loading was defined.
Try import Vue from 'vue' (instead of from 'vue/dist/vue.esm') and then:
const app = new Vue({
el: domElement,
render: h => h(RootComponent)
})
The comments that appear in the hello_vue.js scaffold from the Webpacker gem tell you that you can choose between using the DOM as your template OR load the component with a render function; they both do work in development, but only the latter (loading the component with a render function, using vue instead of vue/dist/vue.esm and render: h => h(RootComponent) worked for me in production.
This has been, by far, the longest, most frustrating debugging session of my life, since there are absolutely no errors in console, you just stare into a blank screen, and Vue is running since it removes the DOM element it was mounted to from the DOM.
Source of solution: https://stackoverflow.com/a/48651338/1290457 and here's the github issue (currently open) on Webpacker gem https://github.com/rails/webpacker/issues/1520
I still don't know how to use DOM as template with Vue in production tough.
I had similar problem in Rails 5.2 + webpack Vue. All was good in development, but not working in production. After hours of investigating I found the reason. It was in this recommendation from webpaker gem docs.
Adding this
Rails.application.config.content_security_policy do |policy|
if Rails.env.development?
policy.script_src :self, :https, :unsafe_eval
else
policy.script_src :self, :https
end
end
brokes production. Removing else part - fixing the situation.
Chrome silently ignoring this. Firefox shows warnings.

How to migrate requiring of select2.js from sprockets to webpacker

I'm in the process of migrating a Rails 5.1.5 project, which uses CoffeeScript, from using sprockets to using webpacker. The project also uses select2.js. With sprockets, I did the following:
Install jquery-rails (jQuery is a dependency for select2).
Put select2.js code in vendor/assets/javscripts.
In application.js.coffee, add:
#= require select2
After that I was able to use select2 to in my application.js.coffee file:
$(document).on 'turbolinks:load' ->
$('select').select2
So far I've described the pretty standard way of including/using javascript libraries with sprockets.
However, with webpacker I can't make select2 work and I'm not sure why. I have two hypothesis:
I'm not importing/requiring it properly;
it doesn't find jQuery at some point of the load process;
So for jQuery, I did the following:
yarn add jquery
included in my environment.js:
const webpack = require('webpack');
environment.plugins.append('Provide', new webpack.ProvidePlugin({
$: 'jquery',
jQuery: 'jquery'
}));
I've removed the jquery-rails gem, as well as #= require 'jquery' and tested that jquery works, so I guess I have correctly included it. However, I tried several ways of importing select2 (using es6 imports) and none of them worked. I tried:
import select2 from 'select2';
import select2 from 'select2/dist/js/select2'
import select2 from 'select2/dist/js/select2.js'
import 'select2/dist/js/select2.js'
I even tried to import it from the old vendor location by writing inside app/javascript/pack/application.js.coffee:
import '../../../vendor/assets/javascripts/select2'
I can confirm that the file contents is imported, as I put a console.log within the select2 file under node_modules/select2/dist/js/select.js and it did get printed. However, I also get the error TypeError: $(...).select2 is not a function when I execute $('select').select2() in the browser's dev tool console.
What am I missing or doing wrong?
P.S. I can provide much more info, but I didn't want my question to get any more bloated.
P.P.S. With my modest JS knowledge, I looked at the source code but couldn't recognize what exactly they are exporting and how am I supposed to import it.
I know this is an old post, but just in case someone else could benefit:
app/javascript/packs/application.js
...other requires...
require('select2')
window.Rails = Rails
import 'bootstrap'
...other imports...
import 'select2'
import 'select2/dist/css/select2.css'
$(document).on("turbolinks:load", () => {
$('.select2').select2()
})
My similar problem
I have stumble upon the same problem with another web component (Switchery):
I imported the component with yarn add switchery (no error)
I could import it correctly through WebPack with import 'switchery' (no error bundling the pack)
But when I was trying to use the Switchery object in the browser like they say in the doc:
var elem = document.querySelector('.js-switch');
var init = new Switchery(elem);
I would get the error: ReferenceError: Switchery is not defined
Note: I didn't want to install RequireJS as WebPack is supposed to do the same thing (and even better) nowadays.
My solution:
The problem was the webpack doesn't expose the pack-generated variables and classes in the global scope!
So to fix this, I needed to do two things:
Explicitly give a name to the imported class from Switchery:
import Switchery from 'switchery'
Use this Class only in the same JS file where the import was done
Testing hack:
If you want to try that out and "go back" to the mess that sprocket allowed, in the same file, you can expose "globally" the variable so you can use in from the browser:
import Switchery from 'switchery'
window.Switchery = Swicthery
now you can execute the switchery almost like in the example:
var init = new window.Switchery(elem);
Hope that helps...

React Rails Component is not defined

I'm trying to get the react-rails gem (version 2.1) working in my Rails 4.2.4 app. I've gone through the setup steps in the Readme and I'm using webpacker for the js preprocessing. I have a component inside of app/javascript/components/label.js that looks like this:
import React, {PureComponent} from 'react';
import ReactDOM from 'react-dom'
export default class Label extends PureComponent {
render () {
return (
<div>Something rendered in React</div>
)
}
}
And then I reference this in my view with the following line:
= react_component("Label")
As far as I can see from the Readme, this should be all that is necessary in order to render the component (provided the application pack is included in the layout, which it is)
= javascript_pack_tag 'application'
So I'm confused as to why I'm getting the error in my browser that the component is not defined.
Uncaught ReferenceError: Label is not defined
Opening app/javascript/packs/application.js I can see the following:
console.log('Hello World from Webpacker')
// Support component names relative to this directory:
var componentRequireContext = require.context("components", true)
var ReactRailsUJS = require("react_ujs")
ReactRailsUJS.useContext(componentRequireContext)
First I verified that the console log is displayed in the browser (it is). I'm not sure what componentRequireContext does, but if it is relative to the current file, then it seems odd that it points to components and not ../components, but changing this doesn't render the component. However, I can get the component rendering if I add the following line:
window.Label = require('../components/label.js');
I thought the React Rails gem took care of this though, provided the components were saved in the app/javascript/components directory? There's nothing in the Readme that says that I need to explicitly declare and require the component, or am I mistaken?
It looks like you have a capitalization issue. You named the file 'label.js' but you are looking for '= react_component("Label")' So it looks and doesn't find what Label is. Then when you set Label on the window then react is like "Oh ok, Label is actually label.js." and it does stuff. TLDR capitalization matters.

React.NET, Webpack - 'ReactDOM' is undefined (client side)

Following the tutorial here, I have managed to get webpack doing my bundling for me, and it is working for pre-rendering on the server side, e.g.:
#Html.React("Components.myComponent",
New With {
.initialData = Nothing
})
#Html.ReactInitJavaScript()
This is correctly displaying my component in the browser, but as soon as the client takes over I get the error 'ReactDOM' is undefined
I have installed the react-dom using npm install react-dom --save-dev
I have tried to require the ReactDOM, first in the .jsx file containing my component, then in the client.js file that webpack is building from:
//myComponent.jsx
var React = require('react');
var ReactDOM = require('react-dom');
or
//client.js
var ReactDOM = require('react-dom');
var Components = require('expose?Components!./src');
But I am still getting the same error.
If I add the react and react-dom scripts directly above my webpack compiled client javascript, then the problem goes away:
//index.html
<script src="https://www.facebook.com/react-0.14.0.min.js"></script>
<script src="https://www.facebook.com//react-dom-0.14.0.min.js"></script>
<script src="~/Scripts/webpack/build/client.bundle.js"></script>
So how can I get webpack to properly include these scripts in the client bundle?
EDIT
I did have the following externals in my webpack.config.js, but removing them doesn't seem to make any difference.
externals: {
// Use external version of React (from CDN for client-side, or
// bundled with ReactJS.NET for server-side)
react: 'React'
}
I guess, externals are there so you can use CDN based scripts, so maybe I am overthinking this one and should just leave the CDN based react scripts in my view?
You should expose react-dom globally in your client.js, so it will be available in browser in ReactDOM variable to render client-side:
require("expose?ReactDOM!react-dom");

Resources