I'm trying to use EJS to pre-parse a template, which then gets used as an EJS template in WebPack. To make this work, I thought I could use a custom delimiter for the pre-processing, but it appears to still be using the default delimiter (possibly in addition to the custom one).
If I
npx ejs prague-a.ejs -o ../prague-a.ejs -m\| (also -m'|')
it results in
node_modules/ejs/lib/ejs.js:361
throw err;
^
ReferenceError: ejs:4
2| <html lang="en-US" class="no-js">
3| <head>
>> 4| <% htmlWebpackPlugin.options.inlineHeadScripts.forEach((script) => { %>
5| <script>
6| <%
7| const re = new RegExp(script);
htmlWebpackPlugin is not defined
My template is
<!DOCTYPE html>
<html lang="en-US" class="no-js">
<head>
// I want this to stay, so it can be processed by WebPack
<% htmlWebpackPlugin.options.inlineHeadScripts.forEach((script) => { %>
<script>
<%
const re = new RegExp(script);
const target = htmlWebpackPlugin.files.js.filter((file) =>
file.match(re))[0];
%>
<%= compilation.assets[target
.substr(htmlWebpackPlugin.files.publicPath.length)].source()
%>
</script>
<% }) %>
...
<style type="text/css">
// this is what I want EJS to process
<|- include('build/atf') |>
</style>
Is there a way to get this to work?
Related
I'm contemplating moving the tempating language of my express app from Jade to Handlbars, and I'm wondering if there is an equivalent to the Jade extend directive in handlebars.
As i can see, in the repository of handlebars tells you that exist a dependencie for handlebars that can enable you to extends blocks. You can found more information here and here.
layout.hbs
<!doctype html>
<html lang="en-us">
<head>
{{#block "head"}}
<title>{{title}}</title>
<link rel="stylesheet" href="assets/css/screen.css" />
{{/block}}
</head>
<body>
<div class="site">
<div class="site-hd" role="banner">
{{#block "header"}}
<h1>{{title}}</h1>
{{/block}}
</div>
<div class="site-bd" role="main">
{{#block "body"}}
<h2>Hello World</h2>
{{/block}}
</div>
<div class="site-ft" role="contentinfo">
{{#block "footer"}}
<small>© 2013</small>
{{/block}}
</div>
</div>
{{#block "foot"}}
<script src="assets/js/controllers/home.js"></script>
{{/block}}
</body>
</html>
Here we define a basic layout where you can extend others html from this one.
page.html
{{#extend "layout"}}
{{#content "head" mode="append"}}
<link rel="stylesheet" href="assets/css/home.css" />
{{/content}}
{{#content "body"}}
<h2>Welcome Home</h2>
<ul>
{{#items}}
<li>{{.}}</li>
{{/items}}
</ul>
{{/content}}
{{#content "foot" mode="prepend"}}
<script src="assets/js/analytics.js"></script>
{{/content}}
{{/extend}}
In this file you set all the data you want to extend from layout.
The .js file
var handlebars = require('handlebars');
var layouts = require('handlebars-layouts');
// Register helpers
handlebars.registerHelper(layouts(handlebars));
// Register partials
handlebars.registerPartial('layout', fs.readFileSync('layout.hbs', 'utf8'));
// Compile template
var template = handlebars.compile(fs.readFileSync('page.html', 'utf8'));
// Render template
var output = template({
title: 'Layout Test',
items: [
'apple',
'orange',
'banana'
]
});
1. Require handlebars and handlebars-layout
2. Register the helper in the handlebar as a layout.
3. Register partials set the file layout.hbs as a "module" called 'layout', then in the page.html you set the extension from 'layout'
4. Compile in template the extension page.html.
5. Render template passing data from js to the file.
For those looking for a webpack solution. I leave a snippet of code with my configuration:
webpack.config.js
...
const fs = require("fs")
const HandlebarsPlugin = require("handlebars-webpack-plugin")
const HandlebarsLayouts = require('handlebars-layouts');
module.exports = {
...,
plugins: [
...,
new HandlebarsPlugin({
...,
onBeforeSetup: function(Handlebars){
Handlebars.registerHelper(HandlebarsLayouts(Handlebars));
Handlebars.registerPartial('default', fs.readFileSync('path/to/layout.hbs', 'utf8'));
}
})
]
}
References:
Handlebars Webpack Plugin
Handlebars Layouts
I'm confused as to where exactly place the script tags in my Rails app to have a Google map embed in a page. I'm following the directions from the Google API https://developers.google.com/maps/documentation/javascript/tutorial
The code looks like this:
<!DOCTYPE html>
<html>
<head>
<meta name="viewport" content="initial-scale=1.0, user-scalable=no" />
<style type="text/css">
html { height: 100% }
body { height: 100%; margin: 0; padding: 0 }
#map-canvas { height: 100% }
</style>
<script type="text/javascript"
src="https://maps.googleapis.com/maps/api/js?key=AIzaSyAK04aYSxZArPElFFZfo3o2p-CbviPzfhc&sensor=false">
</script>
<script type="text/javascript">
function initialize() {
var mapOptions = {
center: new google.maps.LatLng(-34.397, 150.644),
zoom: 8,
mapTypeId: google.maps.MapTypeId.ROADMAP
};
var map = new google.maps.Map(document.getElementById("map-canvas"),
mapOptions);
}
google.maps.event.addDomListener(window, 'load', initialize);
</script>
</head>
<body>
<div id="map-canvas"/>
</body>
</html>
I'm placing the first script tag within my application.html.erb file like so:
<%= javascript_include_tag "https://maps.googleapis.com/maps/api/js?key=AIzaSyAK04aYSxZArPElFFZfo3o2p-CbviPzfhc&sensor=false" %>
I have a Venue model and a venues controller.
I want the map to appear in the Venues show action. So I place the second script inside venue.js. My show.html.erb looks like this:
<div class="center">
<h1><%= #venue.name %></h1>
<address><%= #address %></address>
<div id="map-canvas"/>
</div>
But no map appears when I load this page.
A very easy way to do this is to simply embed the map in an iframe. Using .haml for markup,
%iframe.google-location{ frameborder: '0', scrolling: 'no', src: 'https://www.google.com/maps/embed/v1/place?q=place_id:{google_generated_placeId}' }
the class 'google-location' (or whatever you want to name it) only needs to have the size of the iframe.
(make sure to have a valid api key)
I am rendering partial on click using jquery. Here is the code for it
$('.actCell').click(function (){
alert('Load was performed.');
$.get('/policies/new', function(data) {
$('.policyAddForm').html(data);
});
});
Here is the controller code
def new
#policy = Policy.new
respond_to do |format|
format.html # new.html.erb
format.json { render json: #policy }
end
end
This is my application.html
<head>
<meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1">
<title>Passport</title>
<meta name="description" content="">
<meta name="viewport" content="width=device-width">
<%= stylesheet_link_tag "application", :media => "all" %>
</head>
<body>
<div class="header">
<div class="container">
<img src="/assets/ph_logo.png">
<div class="user"><%=current_user.email%> <div class="userDrop"></div></div>
<div class="userDropMenu">
<div class="userActRow">change password</div>
<div class="userActRow">sign out</div>
</div>
</div>
</div>
<div class="nav">
<div class="container">
<%= yield %>
</div>
</div>
<%= javascript_include_tag "application" %>
<%= yield :javascript %>
</body>
</html>
With above code the problem is my layout get messed up. Div I updated with partial form shows header so it is layout in a layout but my client_validation_gem properties work. as soon as I say layout false my layout issue get fixed but I lose the client_side_validation gem properties. I dont know what is the issue and I am really frustrated. Any help or direction will be very helpful. Thanks,
Looks like you're not loading the rails.validations and rails.validations.custom js files if you use layout: false. You may have to include them in the partial (something like this).
= javascript_include_tag 'rails.validations'
Edit
Just saw that you're adding the form dynamically. You'll also need to trigger client-side validations like this after adding the form:
$('form[data-validate]').validate();
Finally I figured out. I have to remove rails.validations.js file because it was outdated. And add '$('form[data-validate]').validate();' to js file as #David suggested. Thanks for help
express version is 3.2.5, ejs version is 0.8.4
I want to serve stylesheets/style.css
I am doing this way in app.js
app.use(express.static(path.join(__dirname, 'public')));
app.use(app.router);
// render article.ejs
app.get('/u/:name/:day/:title', function(req,res){
Post.getOne(req.params.name, req.params.day, req.params.title, function(err, post){
if(err){
req.flash('error',err);
return res.redirect('/');
}
res.render('article',{
title: req.params.title,
post: post,
user: req.session.user,
success: req.flash('success').toString(),
error: req.flash('error').toString()
});
});
});
my structure is header.ejs
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8" />
<title>Blog</title>
<link rel="stylesheet" href="stylesheets/style.css">
</head>
<body>
article.ejs content is
<%- include header %>
<p class="info">
author:<%= post.name %> |
date:<%= post.time.minute %>
</p>
<p><%- post.post %></p>
<%- include comment %>
<%- include footer %>
When I access http://localhost:3000/
GET /stylesheets/style.css 304 9ms
while access localhost:3000/u/username/date/postname
GET /u/username/date/stylesheets/style.css 404 10ms
why it shows this /u/username/date/stylesheets/style.css directory
Any ideas?
You don't start your stylesheets path with a /. So it's loading "stylesheets/style.css" relative to whichever HTML file requested it.
In my express app,
I've changed the view engine to ejs.
Does anyone know if it is still possible to take advantage of view templates?
Actually after Express 3.X is not support layout.ejs, if you want to use the layout, following steps should be done by yourself:
add dependency "express-partials": "*" in you package.json file
"dependencies": {
"express": "3.1.0",
"ejs": "*",
"express-partials": "*"
}
execute npm install to install the latest version of express-partials
require express-partials in your app.js
var partials = require('express-partials');
add code app.use(partials()); under the app.set('view engine', 'ejs'); in app.js file
after that, you can design you layout.ejs and add <%- body%> block in your layout.ejs file, and that's enough and working well.
You can do it with this module...
https://github.com/aseemk/express-blocks
layout.ejs
<html>
<body>
<% include nav %>
<h1><%= title %></h1>
<%- body %>
</body>
</html>
login.ejs
<% layout('layout') -%>
<form>...</form>
nav.ejs
<nav>
<% if ( session.logged_in ) { %>
account
logout
<% } else { %>
signup
login
<% } %>
home
</nav>
I was using express-partials, but I found express-blocks to be better for ejs and express 3.x. using partials was a pain to have to pass data in everytime. With <% include whatever %> the data is already accessible.
In your routes file, you can render like this:
exports.login.get = function(req, res){
res.locals.session = req.session;
res.render('login', { title: 'Login to your account' });
};
Express.js leaves that up to the template engine if I remember correctly. So if ejs doesn't support layouts then you are out of luck.
Install ejs-blocks package npm install ejs-blocks
In your app.js file import the package const engine = require('ejs-blocks');
and configure view engine
//settings
app.set('views', path.join(__dirname, 'views'));
app.engine('ejs', engine);
app.set('view engine', 'ejs');
In express by default the name of the file is layout.ejs So create a file called layout.ejs in your views folder with the next content:
views/layout.ejs
<html>
<head>
<title><%= title %></title>
</head>
<body>
<%- body %>
</body>
<%- blocks.js %>
</html>
<%= title %> value is obtained when the file is called from route
router.get("/", (req, res) => {
res.render("index", { title: "Hello world!" });
});
Each of your views must import the layout.ejs file in the first line, for example
views/index.ejs
<% layout('layout') -%>
<h1>Article title</h1>
<p>Content</p>
...
<% block('js', `<script>
...
</script>`) -%>
You can use block for insert code from the view to the main layout.