Comments (24)
Here is another tip, you don't even need to define a module with templates. From Phoenix v1.7, you can use ~H directly, especially if you are not using layouts:
defmodule Hub.Notifier do
use HubWeb, :html
require Logger
def deliver_invitation(org, inviter, email) do
assigns = %{org: org, inviter: inviter, email: email}
email_body = ~H"""
<p>Hi <%= @email %>,</p>
...
"""
deliver(email, "You're invited to join #{org.name}", email_body)
end
defp deliver(recipient, subject, body) do
email =
Swoosh.Email.new(
to: recipient,
from: {"Livebook Teams", "[email protected]"},
subject: subject,
html_body:
body
|> Phoenix.HTML.html_escape()
|> Phoenix.HTML.safe_to_string()
)
case Mailer.deliver(email) do
{:ok, _} ->
{:ok, email}
{:error, reason} ->
Logger.warning("Sending email failed: #{inspect(reason)}")
{:error, reason}
end
end
end
So I think there are strong arguments that perhaps this lib is no longer necessary indeed. :)
from phoenix_swoosh.
I am upgrading my small app to 1.7.0-rc.2 at the moment. I'm dropping View
entirely, so I wanted to get emails using the new component rendering as well. It didn't look like that would be possible with Phoenix.Swoosh
, so I dropped it and wrote some simple little helpers instead. I'm not sure I found the absolute best way to do this, but it might give you some idea of what sort of integration Swoosh could provide.
Instead of EmailView
I set up a new component module and imported my templates. I moved my layout template into the same dir for convenience. I converted my templates to .heex
and updated them to use new components like <.link>
, but otherwise left them the same.
defmodule AppWeb.EmailHTML do
use AppWeb, :html
embed_templates "../templates/email/*"
end
I added a couple helper methods to my email module to render the heex templates into an HTML string that I could pass to Swoosh.
defmodule AppWeb.Emails.UserEmail do
import Swoosh.Email
defp render_with_layout(email, heex) do
html_body(
email,
render_component(AppWeb.EmailHTML.layout(%{email: email, inner_content: heex}))
)
end
defp render_component(heex) do
heex |> Phoenix.HTML.Safe.to_iodata() |> IO.chardata_to_string()
end
def send_my_email(foo) do
new()
|> subject("Here's my email")
|> render_with_layout(AppWeb.EmailHTML.my_email(%{foo: foo}))
end
end
That's all it took, and it seems to be working well! I like the simplicity of having everything in components. If Swoosh provided some convenience helpers for the rendering piece, that would be pretty handy.
from phoenix_swoosh.
Also something to consider for a new view-less version: a fresh start under the name of swoosh_phoenix
could be helpful to avoid possible confusion about this package being a core Phoenix component such as phoenix_template
, phoenix_live_view
, etc.
from phoenix_swoosh.
To add mjml
and text
formats (adapted from above ☝️):
# email.ex
defmodule Email do
import Phoenix.Template, only: [embed_templates: 2]
embed_templates("templates/email/*.mjml", suffix: "_mjml")
embed_templates("templates/email/*.text", suffix: "_text")
end
# user_notifier.ex
defmodule UserNotifier do
import Swoosh.Email
def welcome(user) do
assigns = %{name: user.name}
new()
|> html_body_with_layout(Email.welcome_mjml(assigns))
|> text_body_with_layout(Email.welcome_text(assigns))
end
defp html_body_with_layout(email, inner_content) do
body =
%{email: email, inner_content: inner_content}
|> Email.layout_mjml()
|> to_binary()
|> to_html()
html_body(email, body)
end
defp text_body_with_layout(email, inner_content) do
body =
%{email: email, inner_content: inner_content}
|> Email.layout_text()
|> to_binary()
text_body(email, body)
end
defp to_binary(rendered), do: rendered |> Phoenix.HTML.Safe.to_iodata() |> IO.iodata_to_binary()
defp to_html(mjml_binary), do: with({:ok, html} <- Mjml.to_html(mjml_binary), do: html)
end
from phoenix_swoosh.
This thread will eventually shape the next version of the library 🙂
from phoenix_swoosh.
Here is an interesting article to keep the discussion going: https://andrewian.dev/blog/phoenix-email-defaults
from phoenix_swoosh.
@preciz this can be different for every repo depending on the setup and when the project was generated.
from phoenix_swoosh.
@preciz this can be different for every repo depending on the setup and when the project was generated.
You are right, I intended to bring it closer to somebody who is looking at a newly generated 1.7 Phoenix codebase, I edit my comment.
from phoenix_swoosh.
Hi! Thanks for opening this issue, very helpful to read during a Phoenix 1.7 upgrade for a project using phoenix_swoosh
.
In reply to this part of the conversation:
> I'd be happy to see something that simplifies picking a correct language variant of the template
This is probably the only value left.
I'd like to add that phoenix_swoosh
is also very convenient for sending text
and html
versions of the same email.
The new embed_templates/2 function does support multiple formats, but a suffix
must be added to distinguish text
from html
versions.
This leaves a bit more work to do compared to the automatic dual HTML/text format detection phoenix_swoosh
provides when using an atom as template
value in render_body/3.
from phoenix_swoosh.
I also updated a small app to 1.7 wanting to drop the Phoenix.View
dependency. MyAppWeb.EmailHTML
contains my email templates, MyAppWeb.Layouts
contains my root email template, and I used import Phoenix.Template
to get render_to_string
, to do something like this:
def generate_email("update_email", user, %{"url" => url}) do
user
|> base_email(dgettext("emails", "Update your email"))
|> render_body(:update_email, %{user: user, url: url})
end
defp render_body(email, template, assigns) do
html_heex = apply(EmailHTML, String.to_existing_atom("#{template}_html"), [assigns])
html = render_to_string(Layouts, "email_html", "html", email: email, inner_content: html_heex)
text_heex = apply(EmailHTML, String.to_existing_atom("#{template}_text"), [assigns])
text = render_to_string(Layouts, "email_text", "text", email: email, inner_content: text_heex)
email |> html_body(html) |> text_body(text)
end
And then my EmailHTML
:
defmodule MyAppWeb.EmailHTML do
use MyAppWeb, :html
embed_templates "email_html/*.html", suffix: "_html"
embed_templates "email_html/*.txt", suffix: "_text"
end
Edit: Updated from original approach because it was a good idea, and I even checked that it worked 😄
from phoenix_swoosh.
Cheers @josevalim. Maybe Phoenix can have an official comprehensive Notifier guide, then we can formally retire this lib :)
from phoenix_swoosh.
Good idea. If anyone would like to contribute one, feel free to PR one and ping me, I will be glad to review and guide its way in!
from phoenix_swoosh.
In addition to a dedicated notifier guide, it might be an idea to enhance the mix phx.gen.notifier
mix task with an example of how you'd render the template using the ~H sigil directly.
from phoenix_swoosh.
I haven't got around to test the changes yet, so no idea.
PRs are welcome, if people think there is necessary and/or beneficial change.
from phoenix_swoosh.
@iangreenleaf Yeah I've been wondering, after seeing the embed_templates/2, how much value Phoenix.Swoosh
can still provide.
Thanks a lot for sharing!
from phoenix_swoosh.
@iangreenleaf Yeah I've been wondering, after seeing the embed_templates/2, how much value
Phoenix.Swoosh
can still provide.
I'd say more, but at the very least it can act as a compatibility layer. For large and complex email communication cases that's already a lot of value
from phoenix_swoosh.
I'd say more, but at the very least it can act as a compatibility layer. For large and complex email communication cases that's already a lot of value
Thanks. This can be accomplished by pinning an existing version. I was more wondering if I was to release a 2.0 of this package that is designed with phoenix 1.7 as a starting point, what more this package can provide to the users. 🤔
from phoenix_swoosh.
I understand. I meant the 1.7+ and getting away with "Views" as this is probably what will have to happen anyway. Something that would keep the existing codebase work after migrating to a Phoenix version, which no longer has them in their current form.
Well, probably with only minor modifications at the top of each related file. Whether you find that worth the effort that's of course a different story.
Also, I am not sure if there's good enough support for multiple languages. I'd be happy to see something that simplifies picking a correct language variant of the template as well as an ability to build the final body from several templates. Can be for a multilingual message for example or for building larger messages from smaller building blocks
from phoenix_swoosh.
an ability to build the final body from several templates
Can be for a multilingual message for example or for building larger messages from smaller building blocks
With embed_templates
macro, this is already handled by compiling bits and pieces into a module of functions and call them from your *.heex
or even just *.eex
files.
I'd be happy to see something that simplifies picking a correct language variant of the template
This is probably the only value left.
from phoenix_swoosh.
@iangreenleaf thx for the code example, if somebody has the email
layout in the new my_app_web/components/layouts
folder:
AppWeb.EmailHTML.layout
should be AppWeb.Layouts.email
defp render_with_layout(email, heex) do
html_body(
email,
render_component(AppWeb.Layouts.email(%{email: email, inner_content: heex}))
)
end
from phoenix_swoosh.
Also something to consider for a new view-less version: a fresh start under the name of
swoosh_phoenix
could be helpful to avoid possible confusion about this package being a core Phoenix component such asphoenix_template
,phoenix_live_view
, etc.
That is true. I sometimes think that too. (I didn't create this project)
from phoenix_swoosh.
@ftes that's pretty comprehensive! Cheers
from phoenix_swoosh.
Very good discussion folks. Just one quick addition, you can use Phoenix.Template.render_to_string in some of the cases above so you encapsulate the Safe.to_iodata conversion. :)
from phoenix_swoosh.
Something like this seems to do the trick.
defmodule PhxMail.Notifier do
use PhxMail, :html
import Swoosh.Email
require EEx
def welcome(assigns) do
mjml = """
<mjml>
<mj-body>
<mj-section padding-top="30px" padding-bottom="30px">
<mj-column>
<mj-text>Hello {{first_name}},</mj-text>
<mj-text>Welcome!</mj-text>
</mj-column>
</mj-section>
</mj-body>
</mjml>
"""
{:ok, html} = Mjml.to_html(mjml)
template = html_to_heex(html)
final_email = EEx.eval_string(template, assigns: assigns)
new()
|> to({assigns.first_name <> " " <> assigns.last_name, assigns.email})
|> from({"ABC", "[email protected]"})
|> subject("Welcome!")
|> html_body(final_email)
end
defp html_to_heex(html) do
~r/{{\s*([^}^\s]+)\s*}}/
|> Regex.replace(html, fn _, variable_name ->
"<%= @#{variable_name} %>"
end)
end
end
from phoenix_swoosh.
Related Issues (17)
- Error when following User_Email example HOT 1
- Release a version based on phoenix 1.3.0-rc HOT 2
- respect the bang syntax HOT 1
- Not yet published? HOT 2
- Implement .render_subject/2 and .render_subject/3 HOT 3
- Mismatch of File doc and Hexdoc HOT 1
- A heads up: we have extract phoenix_view into a separate project HOT 2
- would it be possible to do another release ? HOT 4
- phoenix 1.6-rc0 - render_body with atom do nothing HOT 13
- Upgrading to Swoosh 0.13 HOT 2
- How is the struct %Email{} available? HOT 3
- ArgumentError: 1st argument: not an iodata term HOT 6
- phoenix_html 4.0 compatibility HOT 2
- render_body exception when using email as an assign. HOT 1
- Incorrect Swoosh.Email struct keys set HOT 5
- Why does it need to be in application HOT 2
Recommend Projects
-
React
A declarative, efficient, and flexible JavaScript library for building user interfaces.
-
Vue.js
🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
-
Typescript
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
-
TensorFlow
An Open Source Machine Learning Framework for Everyone
-
Django
The Web framework for perfectionists with deadlines.
-
Laravel
A PHP framework for web artisans
-
D3
Bring data to life with SVG, Canvas and HTML. 📊📈🎉
-
Recommend Topics
-
javascript
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
-
web
Some thing interesting about web. New door for the world.
-
server
A server is a program made to process requests and deliver data to clients.
-
Machine learning
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
-
Visualization
Some thing interesting about visualization, use data art
-
Game
Some thing interesting about game, make everyone happy.
Recommend Org
-
Facebook
We are working to build community through open source technology. NB: members must have two-factor auth.
-
Microsoft
Open source projects and samples from Microsoft.
-
Google
Google ❤️ Open Source for everyone.
-
Alibaba
Alibaba Open Source for everyone
-
D3
Data-Driven Documents codes.
-
Tencent
China tencent open source team.
from phoenix_swoosh.