I have a rails 6, tailwind app and it works fine in development.
But when I push it to heroku, the asset compilation seems not to work.
The result is that there is no CSS in the production app.
I managed to replicate the same error on my local machine by doing:
RAILS_ENV=production bundle exec rake assets:precompile
Then I also have no css shown on my local app.
When I then do
RAILS_ENV=development bundle exec rake assets:precompile
it works again.
here is my bin/webpack file:
ENV["RAILS_ENV"] ||= ENV["RACK_ENV"] || "development"
ENV["NODE_ENV"] ||= "development"
require "pathname"
ENV["BUNDLE_GEMFILE"] ||= File.expand_path("../../Gemfile",
Pathname.new(__FILE__).realpath)
require "bundler/setup"
require "webpacker"
require "webpacker/webpack_runner"
APP_ROOT = File.expand_path("..", __dir__)
Dir.chdir(APP_ROOT) do
Webpacker::WebpackRunner.run(ARGV)
end
Here is my application.html.erb:
<!DOCTYPE html>
<html>
<head>
<title></title>
<meta name="viewport" content="width=device-width,initial-scale=1">
<%= csrf_meta_tags %>
<%= csp_meta_tag %>
<link rel="stylesheet" href="https://rsms.me/inter/inter.css"
<%= stylesheet_link_tag 'application', media: 'all', 'data-turbolinks-track': 'reload' %>
<%= stylesheet_pack_tag 'application', 'data-turbolinks-track': 'reload' %>
<%= javascript_pack_tag 'application', 'data-turbolinks-track': 'reload' %>
</head>
<body>
<%= flash[:alert] %>
<%= flash[:notice] %>
<%= yield %>
<%= render 'layouts/footer' %>
</body>
</html>
Here is my postcss.config.js
let environment = {
plugins: [
require('tailwindcss')('./app/javascript/stylesheet/tailwind.config.js'),
require('postcss-import'),
require('postcss-flexbugs-fixes'),
require('postcss-preset-env')({
autoprefixer: {
flexbox: 'no-2009'
},
stage: 3
})
]
};
// Add everything below!
if (process.env.RAILS_ENV === 'production') {
environment.plugins.push(
require('#fullhuman/postcss-purgecss')({
content: [
'./app/**/.html.erb',
'./app/helpers/**/*.rb',
'./app/javascript/**/*.js',
'./app/javascript/**/*.jsx',
],
defaultExtractor: (content) => content.match(/[A-Za-z0-9-_:/]+/g) || []
})
)
}
module.exports = environment;
And here is my tailwind.config.js
module.exports = {
purge: [
'./app/**/*.html.erb',
'./app/helpers/**/*.rb',
'./app/javascript/**/*.js'
],
darkMode: false, // or 'media' or 'class'
theme: {
extend: {
fontFamily: {
sans: ['Inter var'],
},
},
},
variants: {
extend: {},
},
plugins: [],
}
And my package.json
{
"name": "myapp",
"private": true,
"dependencies": {
"#rails/actioncable": "^6.0.0",
"#rails/activestorage": "^6.0.0",
"#rails/ujs": "^6.0.0",
"#rails/webpacker": "5.2.1",
"tailwindcss": "npm:#tailwindcss/postcss7-compat",
"turbolinks": "^5.2.0",
"webpack": "^4.0.0"
},
"version": "0.1.0",
"devDependencies": {
"autoprefixer": "^9",
"postcss": "^7",
"webpack-dev-server": "^3.11.2"
},
"workspaces": {
"nohoist": [
"**/tailwindcss",
"**/#tailwindcss/**"
]
}
}
Related
I am trying to send email that has to contain static image. The problem involves the way how to pass src for image. I mean for now I have to provide full path with origin like http://localhost:3000/logo.png and it works. But is this possible to provide only relative path for instance src=logo.png and it would work the same way?
Here is my hbs file:
<h2 class="header">Welcome {{ name }},</h2>
<p>Please the link click below to confirm your email address</p>
<p>
Confirm your email
</p>
<p>If you did not request this email you can safely ignore it.</p>
<br>
<footer >
<p>Best regards, <br><br> Bookify team</p>
<div class="image-container">
<img src="http://localhost:3000/logo.png" alt="Bookify logo">
</div>
</footer>
main.ts:
app.useStaticAssets(join(__dirname, '..', 'public'));
app.setBaseViewsDir(join(__dirname, '..', 'views'));
app.setViewEngine('hbs');`
app.module.ts:
ServeStaticModule.forRoot({
rootPath: join(__dirname, '..', 'public'),
}),
nest-cli.json:
"compilerOptions": {
"assets": [
{
"include": "../views/**/*.hbs",
"outDir": "dist/views"
},
{
"include": "../public/**/*.{png, jpg}",
"outDir": "dist/public"
}
],
"watchAssets": true
}
Configuration for mailermodule:
export const mailerConfig: MailerAsyncOptions = {
useFactory: () =>
({
transport: {
host: process.env.SMTP_HOST,
port: process.env.SMTP_PORT,
secure: false,
auth: {
user: process.env.SMTP_AUTH_USER,
pass: process.env.SMTP_AUTH_PASSWORD,
},
},
defaults: {
from: process.env.WEBSITE_EMAIL,
},
template: {
dir: join(__dirname, '../../views'),
adapter: new HandlebarsAdapter(),
options: {
strict: true,
},
},
} as MailerOptions),
};
I have this piece of code in my index.html that includes EJS in the head:
<%= (#meta_tags || {}).each do |name, val| %>
<meta name="<%= name %>" content="<%= val %>">
<%= end %>
<title>Dashboard</title>
<%= favicon %>
<%= csrf_meta_tag %>
<%= stylesheet_link_tag "https://fonts.googleapis.com/css?family=Lato:300,400,400i,700|Oswald:400,500&display=swap"%>
<%= stylesheet_link_tag current_stylesheet_url %>
Until Angular 11 it was compiling perfect. After I recently updated to Angular 12, this is what I'm getting in the public/index.htm file:
</head><body><%= (#meta_tags || {}).each do |name, val| %>
<meta name="<%= name %>" content="<%= val %>">
<%= end %>
<title>Dashboard</title>
<%= favicon %>
<%= csrf_meta_tag %>
<%= stylesheet_link_tag "https://fonts.googleapis.com/css?family=Lato:300,400,400i,700|Oswald:400,500&display=swap"%>
<%= stylesheet_link_tag current_stylesheet_url %>
These are my angular compiler options:
"configurations": {
"production": {
"budgets": [
{
"type": "anyComponentStyle",
"maximumWarning": "6kb"
}
],
"optimization": true,
"outputHashing": "all",
"sourceMap": true,
"namedChunks": true,
"aot": true,
"buildOptimizer": true,
"extractLicenses": true,
"vendorChunk": false,
"fileReplacements": [
{
"replace": "src/environments/environment.ts",
"with": "src/environments/environment.prod.ts"
}
]
}
What configuration am I missing so that I can correctly compile my EJS in the index.html file again as I was normally doing in previous versions of angular?
Thanks!
I am starting a new Rails 7 app and recently found out that we can't use ujs anymore :( I found a plugin called mrujs which is working correctly to send my forms remotely to the server. I am also using stimulus to handle various javascript functions on the page.
The issue that I'm having is my response back after ajax:success processes is not iterable:
TypeError: object is not iterable (cannot read property Symbol(Symbol.iterator))
Below is my HTML, Rails, and Stimulus Code:
HTML
<%= form_with model: Article.new, html: { data: { remote: true, type: "html", "action": "ajax:success->modal-forms#onPostSuccess ajax:error->modal-forms#onPostError" } } do |f| %>
<%= f.label :title, "Article Title" %>
<%= f.text_field :title, class: "form-control" %>
<%= f.submit "Save Article", class: "btn btn-primary" %>
<% end %>
Ruby / Rails
def create
#article = Article.create(article_params)
if #article.errors.any?
render partial: 'error', article: #article, status: :bad_request
else
render #article
end
end
This returns a basic html page that would be inserted into another location within the page.
<li><%= #article.title %></li>
Stimulus Action
onPostSuccess(event) {
event.preventDefault()
const [data, status, xhr] = event.detail
// This is where I get the issue of 'Not Iterable'
}
event.detail gives me the not iterable error. Does anyone know how I can get my response from rails to be formatted so the [data, status, xhr] section will actually work?
If hotwire or turbo is needed for this an example would be extremely helpful :)
Thanks
Eric
This may not be the correct way but it does work:
html
<div data-container="remote">
<%= form_with model: Person.new, html: { data: { "action": "submit->remote#submit" } } do |f| %>
<%= f.text_field :first_name %>
<%= f.submit :submit %>
<% end %>
</div>
peole rails controller
def create
#person = Person.new(person_params)
if #person.save
render json: #person
else
render partial: 'people/person', locals: { person: #person }, status: :unprocessable_entity
end
end
remote stimulus controller
submit(event) {
event.preventDefault()
fetch(event.target.action, {
method: 'POST',
body: new FormData(event.target),
}).then(response => {
console.log(response);
if(response.ok) {
return response.json()
}
return Promise.reject(response)
}).then(data => {
console.log(data)
}).catch( error => {
console.warn(error)
});
}
My aim is to create navigation from data that I pass into htmlWebpackPlugin options, and I want to create it using for loop. Every time I try, I got errors like Cannot read property 'length' of undefined. Plus, I use ejs-compiled-loader because I need to use <%- include path/to/template %>. And I don't use express (and I would use it if only there is no other ways).
index.ejs:
<!-- index.ejs -->
<!doctype html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width">
<title><%= htmlWebpackPlugin.options.title %></title>
</head>
<body>
<%- include app/templates/header %>
</body>
</html>
header.ejs:
<!-- header.ejs -->
<div class="header">
<div class="header-inner">
<div class="header-logo"><img src="../../assets/images/logo.png" alt="Logo"></div>
<div class="nav">
<ul>
<% for (var i = 0; i < htmlWebpackPlugin.options.navItems.length; i++) { %>
<li><a href="<%= htmlWebpackPlugin.options.navItems[i].href %>">
<%= htmlWebpackPlugin.options.navItems[i].title %>
</a></li>
<% } %>
</ul>
</div>
</div>
</div>
webpack.config.js:
module.exports = {
// ...
plugins: [
new webpack.DefinePlugin({
IS_DEV: IS_DEV
}),
new HtmlWebpackPlugin({
template: '!!ejs-compiled-loader!./index.ejs',
title: appHtmlTitle
}),
// HEADER
new HtmlWebpackPlugin({
template: path.join(__dirname, 'app/templates/header.ejs'),
// template: '!!ejs-compiled-loader!./app/templates/header.ejs' tried as well
navItems: [
{
href: './',
title: 'startseite'
},
{
href: './offers.html',
title: 'angebote'
},
{
href: './about.html',
title: 'über uns'
},
{
href: './contact.html',
title: 'kontakt'
}
],
test: 'Test'
})
],
module: {
rules: [
// BABEL
{
test: /\.js$/,
loader: 'babel-loader',
exclude: /(node_modules)/,
options: {
compact: true
}
},
// STYLES
{
test: /\.css$/,
use: [
'style-loader',
{
loader: 'css-loader',
options: {
sourceMap: IS_DEV
}
},
]
},
// CSS / SASS
{
test: /\.scss/,
use: [
'style-loader',
{
loader: 'css-loader',
options: {
sourceMap: IS_DEV
}
},
{
loader: 'sass-loader',
options: {
sourceMap: IS_DEV,
includePaths: [dirAssets]
}
}
]
},
// IMAGES
{
test: /\.(jpe?g|png|gif)$/,
loader: 'file-loader',
options: {
name: '[path][name].[ext]'
}
}
]
}
};
What am I doing wrong? If ejs-compiled-loader doesn't have access to passing params, what are the easy-maintaining alternatives?
P.s. This is my first question asked here, please don't judge too strict. Thanks in advance.
The following is code which the question author, Alex Naidovich, edited into their question (everything below "SOLVED" in that revision) and indicated it resolved the problem. It's copyright 2018 Alex Naidovich and is under a CC BY-SA 4.0 license.
nav.js:
module.exports = [
{
href: './',
title: 'Home'
}, {
href: './about.html',
title: 'About Us'
}, {
href: './contact.html',
title: 'Contact Us'
}
]
webpack.config.js:
const NAV = require('path/to/nav.js'); // without extension
module.exports = {
// ...
plugins: {
// ...
new HtmlWebpackPlugin({
template: '!!ejs-compiled-loader!./index.ejs',
filename: 'index.html',
title: appHtmlTitle,
NAV: NAV // <- added NAV into plugin
}),
}
// ...
};
index.ejs:
<!doctype html>
<!-- html and head tags stuff -->
<body>
<% var NAV = htmlWebpackPlugin.options.NAV %> <!-- added this -->
<%- include app/templates/header %>
</body>
</html>
header.ejs:
<div class="header">
<div class="header-inner">
<div class="nav">
<ul>
<!-- header.ejs successfully gets variable NAV from index.ejs -->
<% NAV.forEach(function(navItem) { %>
<li>
<%= navItem.title %>
</li>
<% }) %>
</ul>
</div>
</div>
</div>
That should definitely work.
I just ran into something similar, and for my project it was due to using webpack 4. I had better luck with compile-ejs-loader.
I haven't gone as far as trying to pass in an object for the templates, all I needed was ejs include support.
Below is my code in full for both the application controller and the application.html.erb, I really can't work this out, what is wrong?! this accepts standard values in the high chart, for example, just straight numbers but once I try to call values stored in the array it rejects it. I don't know why. Thanks
class ApplicationController < ActionController::Base
require 'nokogiri'
require 'open-uri'
protect_from_forgery
before_filter :set_locale
def index
#doc= Nokogiri::XML(open("http://api.worldbank.org/countries/BRA/indicators/1.0.HCount.2.5usd?per_page=10&date=1960:2014"))
m = #doc.xpath("//wb:value").collect(&:text)
#values = Array.new
m.each do |m|
#values << m
end
end
end
complete application.html.erb code below:
<!DOCTYPE html>
<html>
<head>
<title>Project</title>
<%= stylesheet_link_tag 'application', media: 'all', 'data-turbolinks-track' => true %>
<%= javascript_include_tag 'application', 'data-turbolinks-track' => true %>
<%= javascript_include_tag :defaults %>
<%= csrf_meta_tags %>
<script src="http://code.highcharts.com/highcharts.js"></script>
<script src="http://code.highcharts.com/modules/exporting.js"></script>
<script>
$(function () {
$('#container').highcharts({
chart: {
type: 'column'
},
title: {
text: 'Brazil Poverty Headcount'
},
subtitle: {
text: 'Source: WorldBank.com'
},
xAxis: {
categories: [
'2009',
'2008',
'2007',
'2006',
'2005',
'2004',
'2003',
'2002',
'2001',
]
},
yAxis: {
min: 0,
title: {
text: 'X-Axis Data'
}
},
tooltip: {
headerFormat: '<span style="font-size:10px">{point.key}</span><table>',
pointFormat: '<tr><td style="color:{series.color};padding:0">{series.name}: </td>' +
'<td style="padding:0"><b>{point.y:.1f} mm</b></td></tr>',
footerFormat: '</table>',
shared: true,
useHTML: true
},
plotOptions: {
column: {
pointPadding: 0.2,
borderWidth: 0
}
},
series: [{
name: 'Brazil',
data: <%=#values.inspect%>
}]
});
});
</script>
</head>
<body>
<div style= "margin:50px">
<div id="container" style="min-width: 310px; height: 600px; margin: 15 auto"></div>
<div id ="signing" style="width:200px; height:200px; float:right;">
<% if current_user %>
Logged in as <%= current_user.email %>
<%= link_to "Log Out", logout_path %>
<% else %>
<%= link_to "Sign Up", signup_path %>
<%= link_to "Log In", login_path %>
<% end %>
</div>
<div id="admin" style="width:100px; height:100px; float:right;">
<%= link_to "Admin", new_admin_user_session_path %>
</div>
<div>
Language:
<%= link_to_unless_current "English", locale: "en" %> |
<%= link_to_unless_current "Spanish", locale: "sp" %>
</div>
<%= yield %>
</div>
</body>
</html>
I have actually deployed your app and try the following.
Create controller chart:
rails g controller chart
Edit app/controllers/chart_controller.rb and add the following:
def index
#doc = Nokogiri::XML(open("http://api.worldbank.org/countries/BRA/indicators/1.0.HCount.2.5usd?per_page=10&date=1960:2014"))
m = #doc.xpath("//wb:value").collect(&:text)
#values = Array.new
m.each do |m|
#values << m.to_f
end
end
Now create file views/chart/index.html.erb and paste following:
<!DOCTYPE html>
<html>
<head>
<title>Maxo</title>
<%= stylesheet_link_tag 'application', media: 'all', 'data-turbolinks-track' => true %>
<%= javascript_include_tag 'application', 'data-turbolinks-track' => true %>
<%= javascript_include_tag "http://code.highcharts.com/highcharts.js" %>
<%= javascript_include_tag"http://code.highcharts.com/modules/exporting.js" %>
<%= csrf_meta_tags %>
<script>
$(function () {
$('#container').highcharts({
chart: {
type: 'column'
},
title: {
text: 'Brazil Poverty Headcount'
},
subtitle: {
text: 'Source: WorldBank.com'
},
xAxis: {
categories: [
'2009',
'2008',
'2007',
'2006',
'2005',
'2004',
'2003',
'2002',
'2001',
]
},
yAxis: {
min: 0,
title: {
text: 'X-Axis Data'
}
},
tooltip: {
headerFormat: '<span style="font-size:10px">{point.key}</span><table>',
pointFormat: '<tr><td style="color:{series.color};padding:0">{series.name}: </td>' +
'<td style="padding:0"><b>{point.y:.1f} mm</b></td></tr>',
footerFormat: '</table>',
shared: true,
useHTML: true
},
plotOptions: {
column: {
pointPadding: 0.2,
borderWidth: 0
}
},
series: [{
name: 'Brazil',
data: <%= #values.inspect %>
}]
});
});
</script>
</head>
<body>
<div style= "margin:50px">
<div id="container" style="min-width: 310px; height: 600px; margin: 15 auto"></div>
</div>
<%= yield %>
</div>
</body>
</html>
Also make sure that you have proper routing rule added to: config/routes.rb. For just simplicity you could just add:
root :to => 'chart#index'
In that case when you localhost:3000, you will get the chart/index view automatically. I have tested it and it works fine.
Just for any case views/layouts/application.html.erb should look:
<!DOCTYPE html>
<html>
<head>
<title>Test2</title>
<%= stylesheet_link_tag 'application', media: 'all', 'data-turbolinks-track' => true %>
<%= javascript_include_tag 'application', 'data-turbolinks-track' => true %>
<%= csrf_meta_tags %>
</head>
<body>
<%= yield %>
</body>
</html>