rails 5 webpack 4 handling css files - ruby-on-rails

I have a rails 5 app with webpacker. I am trying to use webpack to both bundle my css files and my js files.
In my javascripts/packs, I have both an application.js and application.scss files. I added bootstrap as dependencies to my package.json and am now trying to import both bootstrap css and js into my rails app. in application.js I have :
console.log('Hello World from Webpacker')
import 'jquery'
import 'bootstrap/dist/js/bootstrap';
In application.scss I have :
#import '~bootstrap/dist/css/bootstrap';
I cant figure out how to import both scss and js at the same time. In application.html.erb I have both
<%= stylesheet_pack_tag 'application' %>
<%= javascript_pack_tag 'application' %>
However it's ever the js or the css that is loaded but never both !
I guess there's a problem in the way webpack is configured but I cant figure it out. How do I configure webpack correctly to work with my rails 5 app and bundle both my css and js correctly.
Here's a link to the project :
https://github.com/davidgeismar/Visitors

For loading webpacker stylesheet to your Rails application first you should remove app/javascript/packs/application.scss file. Because webpacker separate the stylesheet code with same js file name with stylesheet extensions.
Example - Your webpacker main js file is - app/javascript/packs/application.js and you should import all css and js files inside. So you don't need keep app/javascript/packs/application.scss file, but webpacker generate an application.css file from extracting the code from application.js.
Here's the modified code from your rails app github repo. First remove the app/javascript/packs/application.scss
#app/javascript/packs/application.js
console.log('Hello World from Webpacker')
import 'jquery'
import 'bootstrap/dist/js/bootstrap';
//import 'bootstrap/dist/css/bootstrap.css';
// Added js code for checking webpacker is working fine or not.
document.addEventListener('DOMContentLoaded', () => {
document.querySelector('#content-from-webpacker').innerHTML = '<p class="h1">h1. Bootstrap heading</p><h1 class="display-1">Display 1</h1>'
})
Here's view code for loading webpacker.
#app/views/layout/application.html.erb
<body>
<%= render 'shared/navbar' %>
<%= yield %>
<!-- This id(content-from-webpacker) is used for displaying content from webpacker -->
<div id="content-from-webpacker"></div>
<%= javascript_pack_tag 'application' %>
<%= stylesheet_pack_tag 'application' %>
<%= javascript_include_tag 'application' %>
</body>
I hope it should work. Also I made this changes to your github repo and send a pull request.

Related

Error: Cannot find module 'bootstrap-sprockets'

Although running bootstrap with rails is working fine, the JS functionality isn't working for me. For example, I can't collapse navbar, but I can run $() in my browser console.
My /app/javascript/packs/application.js looks like this:
// This file is automatically compiled by Webpack, along with any other files
// present in this directory. You're encouraged to place your actual application logic in
// a relevant structure within app/javascript and only use these pack files to reference
// that code so it'll be compiled.
require("#rails/ujs").start()
require("turbolinks").start()
require("#rails/activestorage").start()
require("channels")
require("jquery")
require('bootstrap-sprockets')
require('popper.js')
require('turbolinks')
// As instructed in https://github.com/twbs/bootstrap-rubygem#a-ruby-on-rails
//= require bootstrap-sprockets
// But the above is not working
My /app/assets/stylesheets/application.scss looks like this:
#import "bootstrap" ;
body {
margin: 0 ;
}
In my routes.rb, I have root to: 'pages#home'. The home.html.erb looks like this:
<div class="cover-container d-flex w-100 h-100 p-3 mx-auto flex-column">
<main role="main" class="inner cover">
<h1 class="cover-heading">Cover your page.</h1>
<p class="lead">Cover is a one-page template for building simple and beautiful home pages. Download, edit the text, and add your own fullscreen background photo to make it your own.</p>
<p class="lead">
Learn more
</p>
</main>
<footer class="mastfoot mt-auto">
<div class="inner">
<p>Cover template for Bootstrap, by #mdo.</p>
</div>
</footer>
</div>
When I run the server, and visit the home page, an error is showing up on the browser's console:
Error: Cannot find module 'bootstrap-sprockets'
My gem file has these entries for jquery, bootstap:
gem 'jquery-rails', '~> 4.3', '>= 4.3.5'
gem "bootstrap", "~> 4.4"
gem 'bootstrap-sass'
I can't use features like navbar collapse in bootstrap. What's wrong?
I had same problems with collapse not working, and latest version of jQuery and bootstrap.
Had to change to:
"bootstrap": "^4.4.1",
"jquery": "3.4.1"
But this was with rails 6.0.1, so using ujs. Check those versions.

How to use third party plugin in rails 6? Using webpacker

How can I use the the third party plugin via webpacker. I dont want to use cloudfare external link in application.html.erb.
Why means, I just want to compile all the plugin while application loading for the initial time.
Thanks in advance :)
<!DOCTYPE html>
<html lang="en">
<head>
<title><%= yield(:title).present? ? yield(:title) : 'Tryblank' %></title>
<meta name="description" content="<%= yield(:description).present? ? yield(:description) : 'Tryblank' %>" />
<%= csrf_meta_tags %>
<%= csp_meta_tag %>
<meta name="viewport" content="width=device-width, initial-scale=1, viewport-fit=cover" />
<%= stylesheet_pack_tag 'application', media: 'all', 'data-turbolinks-track': 'reload' %>
<%= javascript_pack_tag 'application', 'data-turbolinks-track': 'reload' %>
<script src="https://cdn.jsdelivr.net/npm/sweetalert2#8"></script>
<link rel="stylesheet" type="text/css" href="https://cdnjs.cloudflare.com/ajax/libs/lightgallery/1.6.12/css/lightgallery.min.css" />
<script src="https://cdnjs.cloudflare.com/ajax/libs/lightgallery/1.6.12/js/lightgallery.js" type="text/javascript"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/lg-thumbnail/1.1.0/lg-thumbnail.min.js" type="text/javascript"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery-validate/1.19.1/jquery.validate.js" ></script>
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/bootstrap-datepicker/1.7.1/css/bootstrap-datepicker3.standalone.min.css" />
<script src="https://code.jquery.com/jquery-2.2.4.min.js" integrity="sha256-BbhdlvQf/xTY9gja0Dq3HiwQF8LaCRTXxZKRutelT44=" crossorigin="anonymous"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/twitter-bootstrap/3.3.7/js/bootstrap.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/bootstrap-datepicker/1.7.1/js/bootstrap-datepicker.min.js"></script>
</head>
<body>
<%# <p class="notice"> notice</p> <p class="alert">alert</p> %>
<%= yield %>
</body>
</html>
application.js
require("#rails/ujs").start()
require("turbolinks").start()
require("#rails/activestorage").start()
require("channels")
require("jquery")
import '../stylesheets/style'
import './default.js'
import './bootstrap_custom.js'
import './side_menu.js.erb'
//import './sweetalert.js'
import './business_hours.js'
import './admin.js'
import './services.js'
import './user_service.js'
I answered a similar question here:
https://stackoverflow.com/a/58794513/12104222
There, I explained how to include jquery, bootstrap and popper.js to a project using yarn, and then two different approaches to actually importing the libraries (or modules) to use them in your app, depending on whether you need to expose the modules as a global plugin (something you probably want only for jQuery and similar global libraries) or not, as a single import (the most common option, which I exemplified with Bootstrap).
Updated with instructions for sweetalert & lightgallery, as requested by OP:
Run $ yarn sweetalert lightgallery lg-thumbnail (or whatever plugins you may need; you can also use yarn sweetalert#2.1.2, etc if you need a specific version of the plugin). That will install the modules in your node_modules directory, and will make them available to Webpacker.
You will also need $ yarn jquery to install jquery as a node module as well.
Edit your config/webpack/environment.js so it looks like this:
# Some other previous code
environment.plugins.append('Provide',
new webpack.ProvidePlugin({
$: 'jquery',
jQuery: 'jquery'
})
)
module.exports = environment
That will ensure jQuery is exposed to every other plugin that depends on it.
Edit app/javascript/packs/application.js so it looks like this:
require("#rails/ujs").start()
require("turbolinks").start()
require("#rails/activestorage").start()
require("channels")
//require("jquery") //commented as you loaded jQuery before.
import '../stylesheets/style'
import './default.js'
import './bootstrap_custom.js'
import './side_menu.js.erb'
import swal from 'sweetalert' //Note you don't need './' in your path if you import via yarn
import 'lightgallery'
import './business_hours.js'
import './admin.js'
import './services.js'
import './user_service.js'
Import lightGallery.css in your application.scss
Remove these lines from your view, since you will be loading them using webpack:
<link rel="stylesheet" type="text/css" href="https://cdnjs.cloudflare.com/ajax/libs/lightgallery/1.6.12/css/lightgallery.min.css" />
<script src="https://cdnjs.cloudflare.com/ajax/libs/lightgallery/1.6.12/js/lightgallery.js" type="text/javascript"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/lg-thumbnail/1.1.0/lg-thumbnail.min.js" type="text/javascript"></script>
<script src="https://code.jquery.com/jquery-2.2.4.min.js" integrity="sha256-BbhdlvQf/xTY9gja0Dq3HiwQF8LaCRTXxZKRutelT44=" crossorigin="anonymous"></script>
By now, you've installed jQuery, lightgallery and sweetalert as webpack modules. I cannot guarantee, however, that this will work in your case since the code you provided looks like it's importing a lot of different plugins from many different sources (webpack, CDN, maybe also sprockets) which can (and probably will) cause conflicts between them.
If possible, I'd recommend you replace every <script> tag with the correct import method for webpack, which is just following the method I described for the plugins you mentioned in this answer, but applying them to your other libraries/plugins.
Edit 2:
Remember that, if you use Turbolinks (and it looks like you do), you need to replace this sort of code:
$(document).ready(function() {
$("#lightgallery").lightGallery();
});
With this:
$(document).on('turbolinks:load', function() {
$("#lightgallery").lightGallery();
});
Good luck!

How to configure Jasmine in Rails 6?

How do I configure Jasmine in the Rails 6 environment (where Webpack replaces the asset pipeline for Javascript) so I can test the Javascript modules I've written for my app?
I installed the jasmine gem, ran rails generate jasmine:install, and edited jasmine.yml to point to the location of my Javascript source and specs.
The problem is that I can't use import/export statements. (For example, attempting to load my first module to test results in this error in Chrome: Uncaught SyntaxError: Unexpected token 'export')
From what I can tell, I need to set up Jasmine to use babel; but, I'm not having any luck finding instructions on how to do this in the new Rails 6 layout.
Yes, you're right. The main problem of jasmine-gem is that it doesn't pipe the spec through babel. Let me post the quickest solution to your problem and after that, I will think of the possible implementation of a similar approach in jasmine-gem.
The main idea is to pipe the specs through the rails webpack as long as it has all the required babel configurations.
Install jasmine-core since we will not use jasmine-gem in this solution
yarn add jasmine-core -D
Now create two additional webpack packs.
One is for Jasmine and will contain only Jasmine and the test runner
// app/javascript/packs/jasmine.js
import 'jasmine-core/lib/jasmine-core/jasmine.css'
import 'jasmine-core/lib/jasmine-core/jasmine-html.js'
import 'jasmine-core/lib/jasmine-core/boot.js'
import 'jasmine-core/images/jasmine_favicon.png'
And the second one for your application code and the specs
// app/javascript/packs/specs.js
// First load your regular JavaScript (copy all the JavaScript imports from your main pack).
let webpackContext = require.context('../javascripts', true, /\.js(\.erb)?$/)
for(let key of webpackContext.keys()) { webpackContext(key) }
// Then load the specs
let specsContext = require.context('../spec', true, /\.js(\.erb)?$/)
for(let key of specsContext.keys()) { specsContext(key) }
Pay attention to your '../javascripts' and '../spec' paths. For me it looked like '../../assets/javascripts' and '../../../spec' respectevly.
Then add the Webpack ProvidePlugin for Jasmine (add this code to config/webpack/environment.js)
// config/webpack/environment.js
const webpack = require('webpack')
environment.plugins.prepend('Provide', new webpack.ProvidePlugin({
jasmineRequire: 'jasmine-core/lib/jasmine-core/jasmine.js',
}))
Add Jasmine ranner page to your application
# config/routes.rb
Rails.application.routes.draw do
# ...
if Rails.env.development? || Rails.env.test?
get 'jasmine', to: 'jasmine#index'
end
end
# app/controllers/jasmine_controller.rb
class JasmineController < ApplicationController
layout false
def index
end
end
# app/views/jasmine/index.html.haml
<html>
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, user-scalable=no, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>Document</title>
<%= stylesheet_pack_tag 'jasmine', :media => 'all' %>
</head>
<body>
<%= javascript_pack_tag 'jasmine' %>
<%= javascript_pack_tag 'specs' %>
</body>
</html>
Now your Jasmine should work on /jasmine route
This answer is prepared on the basis of this post, however, I've rechecked the instructions on ruby 2.6.3, rails 6.0.2, added appropriate changes to the recommendations and prove that this works.
Please, let me know if my answer was helpful for you or you need some additional information. However, I'm going to work on a solution that will succeed with jasmine gem or similar implementation.

Adding external js files on Rails 6

I created a folder named "custom" under app->javascript->packs and place the following external js:
metisMenu.min.js
startmin.js
Then on under app->javascript->packs->application.js I required the stated js plugin above:
require("#rails/ujs").start()
require("turbolinks").start()
require("#rails/activestorage").start()
require("custom/metisMenu.min").start()
require("custom/startmin").start()
require("channels")
import 'bootstrap'
import './stylesheets/application.scss'
Unfortunately, still this did not work. Any idea what am I doing wrong?
Also is it ok to place all of the custom css under app->javascript->packs->stylesheets (folder) and not on the assets->stylesheets anymore since Rails 6 is using webpacker?
Your folder needs to be structured like that:
app/javascript/packs/application.js
require("#rails/ujs").start()
require("turbolinks").start()
require("#rails/activestorage").start()
require("channels")
require("custom")
app/javascript/custom/index.js
require("myScript")
require("mySecondScript")
The myScript.js and mySecondScript.js files, for instance, will be located in the same folder ie app/javascript/custom.
You can add custom js files like this:
Add your js files to app/javascript folder
app/javascript/custom/metisMenu.min.js
app/javascript/custom/startmin.js
Require js files in app/javascript/packs/application.js
require("#rails/ujs").start()
require("turbolinks").start()
require("#rails/activestorage").start()
# custom js files
require("custom/metisMenu.min")
require("custom/startmin")
Add <%= javascript_pack_tag 'application' %> to application.html.erb
UPDATE
You can add style files like this:
Add your style files to app/javascript folder
app/javascript/stylesheets/metisMenu.min.scss
Import style files in app/javascript/packs/application.js
import "stylesheets/metisMenu.min"
Add <%= stylesheet_pack_tag 'application' %> to application.html.erb
Set extract_css: true in config/webpacker.yml

Sass support in Rails 5.2 with webpacker 3+ directly in JS

I want to include SASS support in a brand new Rails App directly importing the .scss file on my Javascript files.
So far this is what I have done:
1- Follow https://github.com/rails/webpacker to create a new webpacker rails app with react integrations. Hello world was succesfull.
I have a entry point for Webpacker in packs folder
import ReactDOM from "react-dom";
import Hello from "../hello_world";
document.addEventListener("DOMContentLoaded", () => {
ReactDOM.render(
<Hello name="React" />,
document.body.appendChild(document.createElement("div"))
);
});
3 - My Hello world component
import React from "react";
import "./test.scss";
const Hello = props => <div className="red">Hello {props.name}!</div>;
export default Hello;
4 - My sass file
.red {
background-color: red;
}
5 - view
<%= javascript_pack_tag 'hello_world_pack' %>
6 - layout template
<html>
<head>
<title>Myapp</title>
<%= csrf_meta_tags %>
<%= csp_meta_tag %>
</head>
<body>
<%= yield %>
</body>
</html>
No complaints at all but Red color is not applied. What I overlook?
NOTE: Successfully I could add SASS support but adding a my_stylehseet.scss file directly in pack folder and referencing it into my layout template with `<% = stylesheet_pack_tag 'my_stylesheet %>'. But I want that my template is stylesheet agnostic s I don't have to have severals of them.
I really think there is an more elegant way If I understood properly webpacker doc https://github.com/rails/webpacker/blob/master/docs/css.md
Maybe Am I wrong?
Just for the records the question is already answered here Rails Webpacker 3.0 import CSS file is not working and the webpacker documentation is clear about it https://github.com/rails/webpacker/blob/master/docs/css.md

Resources