Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

If, else, for and arguments #1457

Closed
muuvmuuv opened this issue Dec 18, 2018 · 16 comments
Closed

If, else, for and arguments #1457

muuvmuuv opened this issue Dec 18, 2018 · 16 comments

Comments

@muuvmuuv
Copy link

muuvmuuv commented Dec 18, 2018

Is your feature request related to a problem? Please describe.

I'm currently working on a project with a friend and we have started creating some email template for different use cases. Some of this templates are very flexible to contain things like dynamic lists that are filled by the user input. So we came up with the problem, that MJML has no tags or something like that to loop through a set of arrays items or even render a simple string in a tag. We program our API in python and using jinja2 templating, therefore we need to pass the pre-rendered jinja2 template with jinja2 if else clauses and variables to MJML CLI. This is a step we would like to avoid.

Describe the solution you'd like

It would be nice if MJML supports something that allows passing arguments to the template that will be rendered. e.g.:

the {{}} Syntax is a syntax from Jinja2, Twig and others use this too and I think its pretty innovative as it differs from HTML

<mjml lang="de">
<!-- OR -->
<mjml lang="lang.short"> <!-- would check if its a available variable-->
  <mj-head>
    <mj-title><mj-var name="title"/></mj-title>
    <!-- OR -->
    <mj-preview>{{ title }}</mj-preview>
  </mj-head>
  <mj-body background-color="#FFFFFF" width="600px">

    <mj-section full-width="full-width" background-color="#26292c">
      {% if menu | length > 0 %}
      <!-- OR -->
      <mj-if rule="menu.length > 0">
      <mj-column width="70%" vertical-align="middle">
        <mj-text align="right">
          {% for item in menu %}
          <!-- OR -->
          <mj-for array="menu.length > 0">
            <a href="{{ item.link }}">{{ item.text }}</a>
            <!-- OR -->
            <mj-link href="item.link"><mj-var name="item.text"/></mj-link>
          {% endfor %}
          <!-- OR -->
          </mj-for>
        </mj-text>
      </mj-column>
      {% endif %}
      <!-- or -->
      </mj-if>
    </mj-section>

  </mj-body>
</mjml>

Describe alternatives you've considered

Currently, as I have written above, we use Jinja2 (flask) to prerender the templates and pass it to MJML CLI. Additionally a Python renderer would be awesome too.

@iRyusa
Copy link
Member

iRyusa commented Dec 18, 2018

Hi @muuvmuuv

It has been discussed multiple time here : #1403 #451 #727 #743

We don't really want to bloat the core engine, as MJML is a Markup Language and not a templating language. We prefer to allow users to plug any templating language to MJML instead of restricting to only one.

@iRyusa iRyusa closed this as completed Dec 18, 2018
@ngarnier ngarnier pinned this issue Dec 18, 2018
@muuvmuuv
Copy link
Author

Ah yeah I understand, so we will continue using Jinja2, thanks!

@connecteev
Copy link

+1 on this...a very barebones if-else conditional (like vue.js's v-if and v-else) would be really great.

@iamandrewluca
Copy link
Contributor

iamandrewluca commented Apr 3, 2020

You can do it with bare javascript, there is no need for a template language.

const shouldClick = Math.random() > 0.5

console.log(`
<mjml>
  <mj-body>
    <mj-button font-family="Helvetica" background-color="#f45e43" color="white">
        ${shouldClick ? 'Don\'t click me!' : 'Click me!'}
    </mj-button>
  </mj-body>
</mjml>
`)

@connecteev
Copy link

Here's a solution that worked for me (after lots of trial and error). I am using the MJML app:

   <mj-section full-width="full-width" background-color="#f3f3f3">
     <mj-column width="100%">
        <mj-text font-size="16px" color="#666666" font-family="Ubuntu" line-height="24px">
          <ul>
		         @foreach ($listOfMissedEpisodes as $missedEpisodeName => $missedEpisodeUrl)
            <li>
              <a href="{{ $missedEpisodeUrl }}">
              	{{ $missedEpisodeName }}
              </a>
            </li>
	           @endforeach
          </ul>
        </mj-text>
      </mj-column>
  </mj-section>

@codenamezjames
Copy link

Here is my version using handlebars.js

const path = require('path')
const mjml2html = require('mjml')
const Handlebars = require("handlebars")

module.exports = function ({ file, data }) {
  data = data || {}
  const mjml = fs.readFileSync(path.resolve(__dirname, file), 'utf8')
  const template = Handlebars.compile(mjml)
  const { html } = mjml2html(template(data))
  return html
}

@vh13294
Copy link

vh13294 commented Dec 27, 2020

@codenamezjames ,
If you are using react-mjml you could using native es6 import export without fs read file.

it also integrates nicely with typescript, type safe props, easy components reuse without extra configuration.

@adkron
Copy link

adkron commented Sep 1, 2021

I have this same issue. It isn't that I don't want to use my own templating it is that the mjml preprocessing strips my tags.

<%= if true do %>
  <mj-text>Hello</mj-text>
<% end %>

In this case none of those lines are in the output.

<%= if true do %>
  <p>Hello</p>
<% end %>

In this case the mjml processor preserves my conditional block.

I found a workaround with the help of @chadfennell.

<!-- <%= if true do %> -->
  <mj-text>Hello</mj-text>
<!-- <% end %> -->

@iamandrewluca
Copy link
Contributor

iamandrewluca commented Sep 1, 2021

@adkron you should pass first through template engine, and get final mjml, then pass final mjml through mjml

I think you are doing mjml, then template engine

@iRyusa
Copy link
Member

iRyusa commented Sep 1, 2021

You can just wrap them into mj-raw instead of comment blocks too

@adkron
Copy link

adkron commented Sep 1, 2021

Oh, I run it through the mjml at compile-time, and all the other tags I have are dynamic at runtime. I do the mjml engine first because the template engine I use compiles the templates into functions at compile time so running it through mjml at runtime would be slower and outside the norm for my system.
Thanks for the mj-raw tip too.

@olefrank
Copy link

olefrank commented Sep 16, 2022

See django-mjml for an example.

I found this example

<mj-column>
    {# test_comment $}

    {% for item in items %}
        <mj-text align="center">{{ item }}</mj-text>
    {% endfor %}

    <mj-button background-color="#ffcc00" font-size="15px">Test button</mj-button>
</mj-column>

UPDATE!
Template tags is left out when exporting to HTML. The solution is to wrap template tags in <mj-raw> tags:

<mj-column>
    <mj-raw>
        {# test_comment $}
        {% for item in items %}
    </mj-raw>
    <mj-text align="center">{{ item }}</mj-text>
    <mj-raw>
        {% endfor %}
    </mj-raw>
    <mj-raw>
    <mj-button background-color="#ffcc00" font-size="15px">Test button</mj-button>
</mj-column>

Beware of possible problems with minifying template tags like this according to docs.

@PierreDelville
Copy link

PierreDelville commented Jun 6, 2023

To surround twig tags when generating twig files

.mjmlconfig.js
const options = {
preprocessors: [(rawMJML) => {
return rawMJML.replace(/{% (.*) %}/g, '{% $1 %}')
}],
"packages": []
}

module.exports = options

and in cli
mjml path_to_template.mjml --config.mjmlConfigPath=path/.mjmlconfig.js

@Mihailoff
Copy link

Mihailoff commented Nov 18, 2023

Before you decide on templating engine, here is a plain JS:

import { escape } from 'html-escaper'

const condition = (cond, then) => cond ? then : ''

export default ({
  title,
  list = []
}) => `
  <mjml>
     <mj-body>
         ${condition(title, `
           <mj-text>${escape(title)}</mj-text>
           <mj-divider />
         `)}
         ${list.map(item => `<mj-text>${escape(item.label)}</mj-text>`)}
    </mj-body>
  </mjml>
`

@nicolasconnault
Copy link

We want to give our end users the ability to edit their own transactional email templates, but some of the data being provided to the template is optional.
I don't like the idea of putting conditional logic in the hands of non-technical end users, but something akin to Mailchimp's conditional merge tags would solve my use case.

I'm using https://github.com/zalify/easy-email as the client-facing template editor.

@m-Ryan
Copy link
Contributor

m-Ryan commented Mar 22, 2024

If you use easy-email, integrating with other template engines is quite straightforward. If you simply want to provide developers with an MJML editing experience, you can try smart-mjml.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests