MJML: mj-include fails to read file - javascript

Firstly, I am new at emails markup. So I decided to use mjml to construct a letter but I faced the problem - markup doesn't being rendered properly:
MJML doesn't include components and I can't understand why. Project was created using npm package mjml-component-boilerplate because I wanted to register my own components.
my index.mjml
<mj-body>
<mj-include path="./components/blocks/header.mjml" />
<mj-include path="./components/blocks/main.mjml" />
<mj-include path="./components/blocks/footer.mjml" />
</mj-body>
part of output index.html (the same with main.mjml and footer.mjml)
<div>
<!-- mj-include fails to read file : ./components/blocks/header.mjml at D:\projects\ctc_projects\chetv\site\reshala_promo\email\components\blocks\header.mjml -->
header.mjml
<mj-section>
<mj-column>
<mj-div css-class="header">
<mj-image
src="/assets/images/logo.png"
alt="ЧЕ!"
title="https://chetv.ru/"
href="https://chetv.ru/"
target="__blank"
css-class="header__logo"
/>
</mj-div>
</mj-column>
</mj-section>
and, for a case, if it will make any sense my MjDiv component
import { registerDependencies } from 'mjml-validator'
import { BodyComponent } from 'mjml-core'
registerDependencies({
'mj-body': ['mj-div'],
'mj-column': ['mj-div'],
'mj-section': ['mj-div'],
'mj-div': [
'mj-text', 'mj-image', 'mj-accordion',
'mj-carousel', 'mj-divider', 'mj-group',
'mj-navbar', 'mj-raw', 'mj-social',
'mj-div', 'mj-a', 'mj-p', 'mj-h1'
]
});
export default class MjDivComponent extends BodyComponent {
static endingTag = true;
static allowedAttributes = {
'css-class': 'css-class',
'style': 'style'
};
render() {
return `<div
${this.htmlAttributes({
class: this.getAttribute('css-class'),
style: this.getAttribute('style')
})}
>${this.getContent()}</div>`;
}
}

You demonstrate putting the <mj-include> components after <mj-body> (as demonstrated in https://mjml.io/documentation/#mj-include). It doesn't work for me there.
It does work for me when I put the <mj-include> elements after <mj-head>. Good luck!
I'm interpreting the rest of your post as background, not asking another question. Let me know if otherwise.
By the way: Besides stackoverflow, another great source of MJML information is https://mjml.slack.com.

<mj-section padding="0px">
<mj-column>
<mj-text align="center" padding="0px">
<link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.4.1/css/bootstrap.min.css">
</mj-text>
</mj-column>
</mj-section>

Related

Can you use functions from an imported JavaScript library such as Change Case directly in a Vue component's template?

I understand how to import and use Change Case within the <script></script> element of a Vue component, which is just the standard Javascript import covered in the Change Case Github page. However, I would like to use the Change Case functions directly in the template if possible.
Currently, it is my understanding that for dynamic content in the template, in this case generated by v-for running through an array, I must render the return value of a intermediary method from the component's methods section which applies the Change Case function. A method is required for each case type (e.g. camelCase, snakeCase, etc.) I want to render, in this instance one (capitalCase). For example:
// ...
<div
v-for="location in locations"
:key="location.name"
>
<input
type="checkbox"
:id="`select-${location.name}`"
:value="capitalCaseLocationName(location.name)"
v-model="locationsInput"
/>
<label :for="`select-${location.name}`">
{{ capitalCaseLocationName(location.name) }}
</label>
</div>
// ...
methods: {
capitalCaseLocationName(name) {
return capitalCase(name)
}
},
// ...
It would be preferable to somehow import Change Case into the template logic so I could write it like this (no intermediary methods needed):
// ...
<div
v-for="location in locations"
:key="location.name"
>
<input
type="checkbox"
:id="`select-${location.name}`"
:value="capitalCase(location.name)"
v-model="locationsInput"
/>
<label :for="`select-${location.name}`">
{{ capitalCase(location.name) }}
</label>
</div>
// ...
Any chance of that being possible?
As long as you register the imported function as a method you should be able to use it directly in the template.
According to the code, you use Options API, so something like this should do the trick:
import {capitalCase} from "change-case";
...
methods: {
capitalCase,
myOtherMethod () => {...}
}
...
And in the <template>:
<input
type="checkbox"
:id="`select-${location.name}`"
:value="capitalCase(location.name)"
v-model="locationsInput"
/>
The functions need to be defined and passed to the template, that is why even console.log won't work from a template.
You already have an answer with an example, but here's another thing you could do that might make things easier.
You can create a helper like this:
template-helpers.js
export function capitalCase(str) {
return str.split(" ").map(wrd => wrd[0].toUpperCase() + wrd.slice(1)).join(" ")
}
export default {
capitalCase
}
this would make it so that you could use it in a composition/setup like this
import templateHelpers from "../utils/template-helpers.js";
setup(){
return{
...templateHelpers
}
}
in an options API component you could just include it like this
import templateHelpers from "../utils/template-helpers.js";
// ...
methods: {
...templateHelpers,
// other methods
}
// ...
Example
by exporting functions in export default you can destructure them by using methods: { ...templateHelpers
the downside is that it would all the methods every time, but it would make for a more convenient solution. Alternatively, you can pick and chose, since the functions are also exported
import {capitalCase} from "../utils/template-helpers.js";
// ...
methods: {
capitalCase,
// other methods
}
// ...
Vue does have a way to add global definitions, but it's discouraged. This would be done by assigning it to config.globalProperties
https://vuejs.org/api/application.html#app-config-globalproperties
app.config.globalProperties.capitalCase = (str) => {
return str.split(" ").map(wrd => wrd[0].toUpperCase() + wrd.slice(1)).join(" ")

svelte prop value not working with if statement

this is my svelte component code
<script>
export let canCascade = true;
let show = true;
function cascade() {
if (canCascade) {
show = !show;
}
}
</script>
{#if show}
<div class="shade" on:click|self={cascade}>
shade
</div>
{/if}
When I use the component as <Component canCascade=false /> the 'if block' doesn't work.
But hard-coding the value inside just works fine.
Am I missing something here - some conceptual error?
Like #Corrl pointed out in the comments, you need to use {brackets}. If you don't, the variable will follow the rules of an html attribute.
Working repl https://svelte.dev/repl/d13df678eab243e9a13fb705da197219?version=3
In Svelte when we want to pass JavaScript value / expression to an attribute of a component we need to wrap the value / expression with curly brackets {}.
Otherwise, it will be used as a string.
As an example, take a look at the following code:
Component.svelte:
<script>
export let test = true;
$test: console.log(`typeof test = ${typeof test}`);
</script>
App.svelte:
<script>
import Component from "./Component.svelte";
</script>
<Component test=true />
<div />
When you will open the console inside the browser developer tools,
the output will be:
typeof test = string

Is there a way to return html in a javascript function?

i've been using vue for this project. I want to structure the app the best of my ability.
So i want to make use of classes. but this is the problem i'm facing.
my code now:
// models/page.js
export default class Page {
static createPage() {
return (
<p>hey</p>
)
}
}
// pages/index.vue
// I made $page work as a global vue prototype. this works and gives not problem
<template>
<div v-html="this.$page.createPage()"/>
</template>
but this gives the following error
I know in php u can use
function test() {
return <<<EOT
<html>
<body><h1>HELLO</h1></body>
</html>
EOT;
}
But is there any way this works in js?
export default class Page {
static createPage() {
return (
"<p>hey</p>"
)
}
}
This should work perfectly fine. It will append the HTML string in the desired place.
You have to just put you html in quotes like:
export default class Page {
static createPage() {
return (
`<p>hey</p>
<p>hey</p>
<p>hey</p>
<p>hey</p>
`
)
}
}
v-html will parse it as html.

How to make a file with variables of all project's text?

I use ReactJS to develop a web.I want to define all the texts in one class. like:
class ProjectsTexts {
static var applicationName = "my application"
static var internetConnectionError = "Please check your internet connection"
}
And then I can call them from my other classes. like:
function LoginView() {
return (
<header>
<title>(ProjectsTexts.applicationName)</title>
</header>
);
}
Now if I want to change a text everywhere, I just need to change the variable's value.
Thank you. Also, sorry about my English.
If the classes are in the same file, you can simply do -
class ProjectsTexts {
static applicationName = "my application";
static internetConnectionError = "Please check your internet connection";
}
And use inside other classes
function LoginView() {
return (
<header>
<title>{ProjectsTexts.applicationName}</title>
</header>
);
}
But, if they are in different files, you need to export and import.
class ProjectsTexts {
static applicationName = "my application";
static internetConnectionError = "Please check your internet connection";
}
export default ProjectsTexts;
And use inside other classes
import ProjectsTexts from '<path-to-file-here>';
...
function LoginView() {
return (
<header>
<title>{ProjectsTexts.applicationName}</title>
</header>
);
}
You can create a separate file for constants -
constants.js
export default {
applicationName: "my application"
internetConnectionError: "Please check your internet connection"
}
& import it anywhere you want to use.
LoginView.js
import CONST from '../constants' // check if path is correct for you.
function LoginView() {
return (
<header>
<title>{CONST.applicationName}</title>
</header>
)
}
You can make a file export it and then import it where you want to use it.
Another option is to make it globally available via a store (redux). There are other ways to do this as well.

can i use pug (ex-jade) with react framework?

i have read some of pug documentation. its said that i have to install pug first and i'm already done that. then i have to require pug in my js file.
but i don't know where to write the compile for pug file in my react files? what is the right steps to use pug in react framework?
thanks! i really appreciated any help.
here is one of my component in react that i would like to render it with pug.
import React from 'react';
import Sidebar from './Sidebar';
import Header from './header/Header';
import {tokenverify} from '../../utils/helpers';
import pug from 'pug';
class Home extends React.Component {
componentDidMount() {
const token = localStorage.getItem('token')
tokenverify(token)
.catch((res) => {
this.props.history.push('/')
})
}
render() {
return(
<div className="main-container">
<div className="col-md-1">
<Sidebar history={this.props.history} username={this.props.params.username}/>
</div>
<div className="col-md-11">
<div className="row">
<Header history={this.props.history} username={this.props.params.username} />
</div>
<div className="row">
{this.props.children}
</div>
</div>
</div>
)
}
}
export default Home
I found this project in very early phase of its development : https://github.com/bluewings/pug-as-jsx-loader.
I like it because it lets me write my dumb (presentational) react components as pug templates.
The only JSX functionality it currently supports are iterating and conditional if. Which seems good enough for writing most of the dumb components.
Here are the steps to use it
1. Install pug-as-jsx-loader
npm install pug-as-jsx-loader --save-dev
For next step you will have to eject if you are using create-react-app
2. Tell webpack how to handle pug templates.
In your webpack.config.dev.js,
{ test: /\.pug$/, use: [require.resolve('babel-loader'), require.resolve('pug-as-jsx-loader')] },
3. Import pug template in your component
import myTemplate from './mycomponent.pug'
4. Return compiled template from render function
const MyComponent = ({someProperty, someOtherProperty})=> {
return myTemplate.call({}, {
someProperty,
someOtherProperty
});
};
5. Define a pug to render component
#my-element
ul.my-list
li(key='{something.id}', #repeat='something as someProperty')
div(className='planet') {something.name}
div(className='vehicle') {something.type}
div(className='overview') {something.cost}
div(className='cancel', onClick='{()=> someOtherProperty(something)}')
div(className='no-mobile fa fa-remove')
A read about my experience : https://medium.com/p/7610967954a
With Pug, you have two options: render template to HTML string, passing the data object right away or render template to an efficient javascript function that outputs html when passed a data object.
When using pug(alone) with dynamic data, the choice is obviously to compile to function, so that data can be applied on the client.
However, React does not actually consume, or send to the client, html.
If you read an explanation of JSX, you will see that it is just HTML-lookalike syntactic sugar that gets compiled to a javascript function that programmatically creates DOM nodes (essential for the way React handles diffing and updating the page). Pug at the moment, even on the client, outputs an HTML string. Hence, the only way we will be able to use it is
dangerouslySetInnerHTML as following:
//from https://runkit.io/qm3ster/58a9039e0ef2940014a4425b/branches/master?name=test&pug=div%20Wow%3A%20%23%7Ba%7D%23%7Bb%7D
function pug_escape(e){var a=""+e,t=pug_match_html.exec(a);if(!t)return e;var r,c,n,s="";for(r=t.index,c=0;r<a.length;r++){switch(a.charCodeAt(r)){case 34:n=""";break;case 38:n="&";break;case 60:n="<";break;case 62:n=">";break;default:continue}c!==r&&(s+=a.substring(c,r)),c=r+1,s+=n}return c!==r?s+a.substring(c,r):s}
var pug_match_html=/["&<>]/;
function pug_rethrow(n,e,r,t){if(!(n instanceof Error))throw n;if(!("undefined"==typeof window&&e||t))throw n.message+=" on line "+r,n;try{t=t||require("fs").readFileSync(e,"utf8")}catch(e){pug_rethrow(n,null,r)}var i=3,a=t.split("\n"),o=Math.max(r-i,0),h=Math.min(a.length,r+i),i=a.slice(o,h).map(function(n,e){var t=e+o+1;return(t==r?" > ":" ")+t+"| "+n}).join("\n");throw n.path=e,n.message=(e||"Pug")+":"+r+"\n"+i+"\n\n"+n.message,n}function test(locals) {var pug_html = "", pug_mixins = {}, pug_interp;var pug_debug_filename, pug_debug_line;try {;var locals_for_with = (locals || {});(function (a, b) {;pug_debug_line = 1;
pug_html = pug_html + "\u003Cdiv\u003E";
;pug_debug_line = 1;
pug_html = pug_html + "Wow: ";
;pug_debug_line = 1;
pug_html = pug_html + (pug_escape(null == (pug_interp = a) ? "" : pug_interp));
;pug_debug_line = 1;
pug_html = pug_html + (pug_escape(null == (pug_interp = b) ? "" : pug_interp)) + "\u003C\u002Fdiv\u003E";}.call(this,"a" in locals_for_with?locals_for_with.a:typeof a!=="undefined"?a:undefined,"b" in locals_for_with?locals_for_with.b:typeof b!=="undefined"?b:undefined));} catch (err) {pug_rethrow(err, pug_debug_filename, pug_debug_line);};return pug_html;}
// pug source: "div Wow: #{a}#{b}"
// this would obviously be much shorter if you include pug-runtime globally in your application
function createMarkup(a,b) {
return {__html: test({a:a,b:b})};
}
function MyComponent(props) {
return <div dangerouslySetInnerHTML={createMarkup(props.a, props.b)}/>;
}
ReactDOM.render(
<MyComponent a="banana" b="&patata"/>,
document.getElementById('root')
)
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react-dom.min.js"></script>
<div id=root />
Alternatively, there are attempts to translate jade or pug syntax into react directly, such as pug-react-compiler and babel-plugin-transform-pug-to-react. It seems they solved including further react components inside the pug template, which might be a desirable tradeoff for them possibly having quirks.

Categories