Coder Social home page Coder Social logo

temple's Introduction

Temple

Actions Status Hex.pm

You are looking at the README for the main branch. The README for the latest stable release is located here.

Temple

Temple is an Elixir DSL for writing HTML and SVG.

Installation

Add temple to your list of dependencies in mix.exs:

def deps do
  [
    {:temple, "~> 0.12"}
  ]
end

Goals

Currently Temple has the following things on which it won't compromise.

  • Will only work with valid Elixir syntax.
  • Should work in all web environments such as Plug, Aino, Phoenix, and Phoenix LiveView.

Usage

Using Temple is as simple as using the DSL inside of an temple/1 block. The runtime result of the macro is your HTML.

See the guides for more details.

import Temple

temple do
  h2 do: "todos"

  ul class: "list" do
    for item <- @items do
      li class: "item" do
        div class: "checkbox" do
          div class: "bullet hidden"
        end

        div do: item
      end
    end
  end

  script do: """
  function toggleCheck({currentTarget}) {
    currentTarget.children[0].children[0].classList.toggle("hidden");
  }

  let items = document.querySelectorAll("li");

  Array.from(items).forEach(checkbox => checkbox.addEventListener("click", toggleCheck));
  """
end

Components

Temple components are simple to write and easy to use.

Unlike normal partials, Temple components have the concept of "slots", which are similar Vue. You can also refer to HEEx and Surface for examples of templates that have the "slot" concept.

Temple components are compatible with HEEx and Surface components and can be shared.

Please see the guides for more details.

defmodule MyAppWeb.Component do
  import Temple

  def card(assigns) do
    temple do
      section do
        div do
          slot @header
        end

        div do
          slot @inner_block
        end

        div do
          slot @footer
        end
      end
    end
  end
end

Using components is as simple as passing a reference to your component function to the c keyword.

import MyAppWeb.Component

c &card/1 do
  slot :header do
    @user.full_name
  end

  @user.bio

  slot :footer do
    a href: "https://twitter.com/#{@user.twitter}" do
      "@#{@user.twitter}"
    end
    a href: "https://github.com/#{@user.github}" do
      "@#{@user.github}"
    end
  end
end

Engine

By default, Temple will use the EEx.SmartEngine that is built into the Elixir standard library. If you are a web framework that uses it's own template engine (such as Aino and Phoenix/LiveView, you can configure Temple to it!

# config/config.exs

config :temple,
  engine: Aino.View.Engine # or Phoenix.HTML.Engine or Phoenix.LiveView.Engine

Formatter

To include Temple's formatter configuration, add :temple to your .formatter.exs.

[
  import_deps: [:temple],
  inputs: ["*.{ex,exs}", "priv/*/seeds.exs", "{config,lib,test}/**/*.{ex,exs,lexs}"],
]

Phoenix

When using Phoenix ~> 1.7, all you need to do is include :temple in your mix.exs.

If you plan on using the template structure that < 1.6 Phoenix applications use, you can use :temple_phoenix as described below.

To use with Phoenix, please use the temple_phoenix package! This bundles up some useful helpers as well as the Phoenix Template engine.

Related

temple's People

Contributors

aerosol avatar bastes avatar dependabot-preview[bot] avatar dependabot[bot] avatar exit9 avatar garlandcrow avatar github-actions[bot] avatar mhanberg avatar michallepicki avatar r11132a avatar romaintb avatar shritesh avatar zimt28 avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

temple's Issues

Sigil Support?

@mhanberg - Thanks for the wonderful library. I slowly got hang of what temple does - and - how it fits into the complete cycle.
For each small component, we need to have two files - one *.ex and *.lexs. Instead, like ~H sigil for heex or ~F sigil for surface components - will it improve Developer Experience, if there is a sigil support for temple template?

Add @props to components

I think there's no way to access the list of props given to a component. It would be nice to have something like @props available. What do you think? I'd happily send a PR.

Issue with mixing inline text and equal level components.

Describe the bug
When trying to render some components mixed into inline text, sometimes text after the inline component does not render.

To Reproduce
Assuming a component like this:

temple do
  ~s[<div>#{@assigned_value}</div>]
  div do
   hr # or some other void tag
  end
end

The following will cause issues:

temple do
  div do
    "This is some inline text"
    c &mycomponent/1, assigned_value: "something"
    if true do
      " this will never render"
      c &mycomponent/1, assigned_value: "something else"
    end
  end
end

Expected behavior
The whole thing will render

Desktop (please complete the following information):

  • OS: MacOS 13.5.1
  • Temple Version: 0.12.0
  • Erlang: 26.1.2
  • Elixir: 1.15.7-otp-26

Additional context
I tried to reproduce this in a test case against main, but was unable to. So you may have already fixed it. I can try to reproduce it in the released version tests

DSL Escape Hatch

Is your feature request related to a problem? Please describe.
Would like an escape hatch from the DSL when necessary.

Describe the solution you'd like
I'd like to be able to call non-Temple functions that return Phoenix.HTML.safe() from within a temple/1 block:

defmodule MyHelper do
  use Temple
  alias Phoenix.HTML.Tag

  @spec help_me!(any()) :: Phoneix.HTML.safe()
  def help_me!(arg) do
    temple do
      div class: "helper" do
        help_more(arg)
      end
    end
  end

  def help_more("bad words") do
    Tag.content_tag(:span, "ah ah ah", class: "helper__message")
  end

  def help_more(message), do
    Tag.content_tag(:span, message, class: "helper__message")
  end
end

Additional context
Forgive me if this is already a feature and I've missed it in the documentation

Don't Minify

Is your feature request related to a problem? Please describe.
By default, Temple minifies markup, disregarding whitespace within blocks. For example:

temple do
  text("Hello,")
  text("World!")
end

outputs

Hello,World!

Describe the solution you'd like
All tags should be joined by an newline. Output in the above example should be:

Hello, World!

Maybe this could be a configurable option.

Additional Comments
This line in the temple/1 macro calls this util with Enum.join("").

Heex Interop Bug

Describe the bug

When attempting to call Heex Components from a temple template, I get an error the function "inner_block" cannot handle clauses with the -> operator because it is not a macro. Please make sure you are invoking the proper name and that it is a macro

triggered by this block in renderer.ex

Heex Component

  use Phoenix.Component

  def render(assigns) do
    ~H"""
    <div>This is a slot component
    <%= render_slot(@inner_block, %{}) %>
    </div>
    """
  end
end

Temple Component

defmodule TempleComponent do
  import Temple
  use TimshelWeb, :live_component
  import Phoenix.LiveView.TagEngine, only: [component: 3]

 def render(assigns) do
     temple do
        c(&SlotComponent.render/1) do:
          "slot"
         end
     end
end
end

In local deps I replaced the inner block call with the TagEngine.inner_block, similar to how surface does it which resolved the issue.

 block =
          quote do
            unquote(slot.parameter || quote(do: _)) ->
              unquote(ast)
          end

        inner_block =
          quote do
            Phoenix.LiveView.TagEngine.inner_block(unquote(slot.name), do: unquote(block))
          end

Looking at the dependencies here though, phoenix_live_view seems to be purposely not a dependency, so probably need to tweak how the inner_block ast is created.

Let me know if theres anything I can do to help! Thanks so much for your library

String interpolation escapes HTML entities

Describe the bug:

When using #{...} in a string, any &html_entity; in the string are escaped and rendered as text.

Desktop:

OS: Linux
Temple Version {:temple, "~> 0.6.0-rc.0"}
elixir 1.11.3-otp-23
erlang 23.2.3

Reproduction:

In template:

div do: "&nbsp;arst"
div do
  "&nbsp;arst"
end

div do: "&nbsp;arst#{1}"
div do
  "&nbsp;arst#{1}"
end

Rendered to view:

 arst
 arst
&nbsp;arst1
&nbsp;arst1 

Temple 0.8.0 + Phoenix 1.5 results in 'Duplicated modules' when building Mix release

I believe this is because Temple 0.8.0 specifies phoenix_view as a dependency, and Phoenix 1.5 has its own Phoenix.View. (Phoenix 1.6 extracts Phoenix.View into its own project.)

** (Mix) Duplicated modules:
        'Elixir.Phoenix.View' specified in phoenix and phoenix_view
        'Elixir.Phoenix.Template.UndefinedError' specified in phoenix and phoenix_view
        'Elixir.Phoenix.Template.ExsEngine' specified in phoenix and phoenix_view
        'Elixir.Phoenix.Template.Engine' specified in phoenix and phoenix_view
        'Elixir.Phoenix.Template.EExEngine' specified in phoenix and phoenix_view
        'Elixir.Phoenix.Template' specified in phoenix and phoenix_view

I'm using Temple 0.7.0 for now. :)

Edit: I don't know if this is worth fixing if Phoenix 1.6 will have its official release soon.

Options to conditionally show an element

Proposal

Add some reserved options to elements to conditionally render them.

# if option

div if: true do
  text "Hello, world!"
end

# <div>Hello, world!</div>

div if: false do
  text "Hello, world!"
end

# ""

# unless option

div unless: true do
  text "Hello, world!"
end

# ""

div unless: false do
  text "Hello, world!"
end

# <div>Hello, world!</div>

These would be preferred over the traditional method

if true do
  div do
    text "Hello, world!"
  end
end

DOCTYPE

Description

The HTML spec requires the DOCTYPE to be set in the beginning of the file. Temple currently does not emit this into the markup

Workarounds

E sigil

Add the following to your my_app_web.ex: import Phoenix.HTML, only: [sigil_E: 2]

Then you can add this line to the top of your layout: partial ~E"<!<DOCTYPE html>"

Phoenix.HTML.raw

You can add the following to the top of your layout: partial Phoenix.HTML.raw("<!DOCTYPE html>")

Solution

This html macro should add the doctype as well as the opening tag

undefined function render_block/2 when combining mode: :live_view and slot in component

Describe the bug

Setting config :temple, :mode, :live_view breaks slotted component.

To Reproduce

== Compilation error in file lib/bug_web/live/user_live/a_component.ex ==
** (CompileError) lib/bug_web/live/user_live/a_component.ex:29: undefined function render_block/2
    (elixir 1.11.4) src/elixir_locals.erl:114: anonymous fn/3 in :elixir_locals.ensure_no_undefined_local/3
    (stdlib 3.14.1) erl_eval.erl:680: :erl_eval.do_apply/6
    (elixir 1.11.4) lib/kernel/parallel_compiler.ex:314: anonymous fn/4 in Kernel.ParallelCompiler.spawn_workers/7
  • edit lib/bug_web/live/user_live/a_component.ex, comment out line 33
  • mix phx.server, observe no error
  • edit lib/bug_web/live/user_live/a_component.ex, uncomment line 33
  • edit config/config.ex, comment out line 29 (or set mode to :normal)
  • mix phx.server, observe no error

lib/bug_web/live/user_test/test.* and localhost:4000/test use the component.

I think I have it configured correctly, I have a working app that stops working when I flip the :mode to :live_view.

The mix file might be a bit of a mess as I threw whatever it asked to resolve the dependency tree.

Desktop

  • OS: Arch Linux
  • Temple Version 6.1
  • Elixir Version 1.11.4
  • Erlang Version 23.3.1

Dynamic attributes

Is your feature request related to a problem? Please describe.
I want to pass dynamic attributes to a node say div
So i build the attributes as a keyword list like so

attrs = [class: "abc", id: "a1", "data-a1": "ABC", "data-a2": "DEF]

And then I create the div like so

temple do
	div attrs do
		"Hello World"
	end
end

It gives a runtime error that list cannot be converted into string

Describe the solution you'd like
I don't know the solution but if attributes are keyword lists then the above should work in my opinion. Please advise if I am doing something wrong.

Describe alternatives you've considered
I can build an exhaustive list of attributes and set those not applicable to false. Those won't be rendered in the final markup. But sometimes building an exhaustive list of attributes might not be possible.

Additional context
Say you want to create some components and pass attributes dynamically to them. The attributes might not be known beforehand.

Can temple be adopted partially?

Can I migrate from eex files gradually? That is, in the templates directory, both eex and exs files can be used. Is it possible?

[bug] Don't render the inner_block assign when building element attributes

Description can be seen here: https://elixirforum.com/t/temple-html-dsl-for-elixir-and-phoenix/23940/29?u=mhanberg

this should reject the inner_block key and not render it

assigns = [class: "foo", inner_block: fn x -> "could be any type tho" end]

div assigns do
  # ...
end
<div class="foo">
  <!-- ... -->
</div>

Currently, this will attempt to render it, but a function does not implement the phoenix Safe protocol, so it fails.

We can also potentially emit a warning if you try to pass types that cannot be rendered, so that the user is aware of why it's not "working".

How to use heex and LiveView Components?

Not sure I am framing the question right.
But, the new heex syntax is it supported via temple?
<.form></.form> is the syntax for LiveView forms. Further Phoenix Component have a syntax like <.Button></.Button> How do we represent them in temple?
If they are already supported, a few examples in README will help immensely.

Does it work with the latest Liveview ?

Hey @mhanberg, I'm hitting this strange bug, what am I doing wrong here?

Describe the bug
When trying to use it with the latest liveview, it fails.

# This fails
  def temple_component(assigns) do
    temple do
      div class: "border-2 border-emerald-500 rounded m-2 p-2" do
        slot @inner_block

        ul class: "list-disc pl-6" do
        end
      end
    end
  end

Backtrace:

== Compilation error in file lib/temple_liveview_web/live/comment_live/index.ex ==
** (CompileError) lib/temple_liveview_web/live/comment_live/index.ex:103: undefined variable "changed" (context Phoenix.LiveView.Engine)
    (elixir 1.14.2) expanding macro: Kernel.var!/2
    lib/temple_liveview_web/live/comment_live/index.ex:103: TempleLiveviewWeb.CommentLive.Index.temple_component/1
    (phoenix_live_view 0.18.18) expanding macro: Phoenix.Component.render_slot/2
    lib/temple_liveview_web/live/comment_live/index.ex:103: TempleLiveviewWeb.CommentLive.Index.temple_component/1
    (temple 0.11.0) expanding macro: Temple.Renderer.compile/1
    lib/temple_liveview_web/live/comment_live/index.ex:103: TempleLiveviewWeb.CommentLive.Index.temple_component/1
    (elixir 1.14.2) expanding macro: Kernel.then/2
    lib/temple_liveview_web/live/comment_live/index.ex:103: TempleLiveviewWeb.CommentLive.Index.temple_component/1

Expected behavior
Should work

Desktop (please complete the following information):

  • OS: Mac OS Monterey
  • Temple Version : 0.11.0
  • Elixir Version : 1.14.2-otp-25
  • Erlang Version : 25.2.3

Tags passed to `phx_link` get escaped

Describe the bug
Tags passed to phx_link should generate unescaped html but don't.

To Reproduce

phx_link to: "/" do
  h1 "Hello World"
  p "Lorem Ipsum"
end

Expected behavior
The unescaped html should be wrapped in the link tag and not be escaped

Screenshots
Screen Shot 2019-08-27 at 2 03 01 PM

The `aliases` configuration does not seem to work

Describe the bug
Attempting to alias functions from this awesome library to avoid name collisions (as per documentation) did not yield the expected result.

To Reproduce

# in config/config.exs
config :temple, :aliases, link: :_link
# or
config :temple, :aliases, link: :_link
# or
config :temple, aliases: %{link: :_link}
# or
config :temple, aliases: [link: :_link]
# in lib/app_web/templates/layout/app.html.exs
_link(rel: "stylesheet", href: Routes.static_path, @conn, "/css/app.css"))

All those configs results in warnings during compilation and an error when the server attempts to start:

undefined function _link/1

Expected behavior
I would expect the <link> tag to be generated.

Desktop (please complete the following information):

  • OS: Docker's official Elixir image
  • Temple Version: 0.6.0-alpha.4
  • Elixir Version: 1.10.4 (compiled with Erlang/OTP 22)
  • Erlang Version: Erlang/OTP 22 [erts-10.7.2.3] [source] [64-bit] [smp:12:12] [ds:12:12:10] [async-threads:1] [hipe]

Phoenix Live View Compatibility

This is the tracking ticket for Phoenix Live View compatibility. I will be periodically updating it as I progress and learn more of what is involved.

Progress

  • renders a live view using temple
  • renders void elements
  • renders non-void elements
  • renders text nodes
  • renders element attrs
  • renders arbitrary expressions
  • renders a for comprehension
  • renders an if expression
  • renders an if/else expression
  • renders an unless expression
  • renders an unless/else expression
  • renders a case expression
  • renders with expression
  • renders functions that take a function callback
  • renders the live view form_for that requires the manual closing tag

SVG Support

It is common for users to inline their SVGs.

Currently with Temple, this best way to do this would be by using a component and the raw function from phoenix_html.

defcomponent :icon_heart do
    partial Phoenix.HTML.raw("""
            <svg viewBox="0 0 100 100" xmlns="http://www.w3.org/2000/svg">
              <path d="M 10,30
                      A 20,20 0,0,1 50,30
                      A 20,20 0,0,1 90,30
                      Q 90,60 50,90
                      Q 10,60 10,30 z"/>
            </svg>
            """)
  end

This is obviously suboptimal. It makes it hard to format the code, as well as programmatically set the attributes.

It would be better to be able to use macros that correspond to the svg element.

defcomponent :icon_heart do
    svg viewBox: "0 0 100 100", xmlns: "http://www.w3.org/2000/svg" do
      path d: "M 10,30
               A 20,20 0,0,1 50,30
               A 20,20 0,0,1 90,30
               Q 90,60 50,90
               Q 10,60 10,30 z"
    end
end

Goals

Using the new layout helpers does not work

The new layout helpers that were released in Phoenix 1.5.0 does not work well with Temple 0.5.

E.g. using something like:

# base.html.eex
...
<%= @inner_content %>
...

# app.html.eex
render_layout MyAppWeb.LayoutView, "base.html", assigns do
  ...
  @inner_content
  ...
end

Self closing tags cause compile error

Describe the bug
add hr, input or other self-closing tags in any html.exs files will cause compile error

To Reproduce
Steps to reproduce the behavior:

  1. Go to temple/integration_test/temple_demo/app.html.exs
  2. Add hr
  3. See error
Compiling 22 files (.ex)

== Compilation error in file lib/temple_demo_web/views/layout_view.ex ==
** (MatchError) no match of right hand side value: {[], []}
    (temple 0.6.1) lib/temple/parser/void_elements_aliases.ex:16: Temple.Parser.VoidElementsAliases.run/1
    (temple 0.6.1) lib/temple/parser.ex:132: Temple.Parser.parse/1
    (elixir 1.12.1) lib/enum.ex:3865: Enum.flat_map_list/2
    (elixir 1.12.1) lib/enum.ex:3866: Enum.flat_map_list/2
    (temple 0.6.1) lib/temple/parser/nonvoid_elements_aliases.ex:26: Temple.Parser.NonvoidElementsAliases.run/1
    (temple 0.6.1) lib/temple/parser.ex:132: Temple.Parser.parse/1
    (temple 0.6.1) lib/temple/parser/nonvoid_elements_aliases.ex:26: Temple.Parser.NonvoidElementsAliases.run/1
    (temple 0.6.1) lib/temple/parser.ex:132: Temple.Parser.parse/1
    (elixir 1.12.1) lib/enum.ex:3865: Enum.flat_map_list/2
    (temple 0.6.1) lib/temple/parser/nonvoid_elements_aliases.ex:26: Temple.Parser.NonvoidElementsAliases.run/1
    (temple 0.6.1) lib/temple/parser.ex:132: Temple.Parser.parse/1
    (elixir 1.12.1) lib/enum.ex:3865: Enum.flat_map_list/2
    (elixir 1.12.1) lib/enum.ex:3866: Enum.flat_map_list/2
    (temple 0.6.1) lib/temple/parser/nonvoid_elements_aliases.ex:26: Temple.Parser.NonvoidElementsAliases.run/1
    (temple 0.6.1) lib/temple/parser.ex:132: Temple.Parser.parse/1
    (temple 0.6.1) lib/temple/engine.ex:41: Temple.Engine.compile/2
    (phoenix 1.5.8) lib/phoenix/template.ex:351: Phoenix.Template.compile/3
    (phoenix 1.5.8) lib/phoenix/template.ex:166: anonymous fn/4 in Phoenix.Template."MACRO-__before_compile__"/2
    (elixir 1.12.1) lib/enum.ex:2356: Enum."-reduce/3-lists^foldl/2-0-"/3
    (phoenix 1.5.8) expanding macro: Phoenix.Template.__before_compile__/1

If I add attributes by Map type will cause another error.

attrs = %{
  id: "hr"
}
hr attrs

Error message:

Compiling 1 file (.ex)

== Compilation error in file lib/temple_demo_web/views/layout_view.ex ==
** (FunctionClauseError) no function clause matching in Temple.Parser.Utils.compile_attrs/1

    The following arguments were given to Temple.Parser.Utils.compile_attrs/1:

        # 1
        {:attrs, [line: 44], nil}

    Attempted function clauses (showing 3 out of 3):

        def compile_attrs([])
        def compile_attrs([attrs]) when is_list(attrs)
        def compile_attrs(attrs) when is_list(attrs)

    (temple 0.6.1) lib/temple/parser/utils.ex:10: Temple.Parser.Utils.compile_attrs/1
    (temple 0.6.1) lib/temple/parser/void_elements_aliases.ex:28: Temple.Generator.Temple.Parser.VoidElementsAliases.to_eex/1
    (temple 0.6.1) lib/temple/parser/nonvoid_elements_aliases.ex:38: anonymous fn/2 in Temple.Generator.Temple.Parser.NonvoidElementsAliases.to_eex/1
    (elixir 1.12.1) lib/enum.ex:2356: Enum."-reduce/3-lists^foldl/2-0-"/3
    (temple 0.6.1) lib/temple/parser/nonvoid_elements_aliases.ex:38: Temple.Generator.Temple.Parser.NonvoidElementsAliases.to_eex/1
    (temple 0.6.1) lib/temple/parser/nonvoid_elements_aliases.ex:38: anonymous fn/2 in Temple.Generator.Temple.Parser.NonvoidElementsAliases.to_eex/1
    (elixir 1.12.1) lib/enum.ex:2356: Enum."-reduce/3-lists^foldl/2-0-"/3
    (temple 0.6.1) lib/temple/parser/nonvoid_elements_aliases.ex:38: Temple.Generator.Temple.Parser.NonvoidElementsAliases.to_eex/1
    (elixir 1.12.1) lib/enum.ex:1553: Enum."-map/2-lists^map/1-0-"/2
    (temple 0.6.1) lib/temple/engine.ex:41: Temple.Engine.compile/2
    (phoenix 1.5.8) lib/phoenix/template.ex:351: Phoenix.Template.compile/3
    (phoenix 1.5.8) lib/phoenix/template.ex:166: anonymous fn/4 in Phoenix.Template."MACRO-__before_compile__"/2
    (elixir 1.12.1) lib/enum.ex:2356: Enum."-reduce/3-lists^foldl/2-0-"/3
    (phoenix 1.5.8) expanding macro: Phoenix.Template.__before_compile__/1
    lib/temple_demo_web/views/layout_view.ex:1: TempleDemoWeb.LayoutView (module)

It works find when just type hr id: "hr".

Component errors on missing @inner_content if additional options are given with do:

Describe the bug
Components doesn't render @inner_content if additional options are given when using do: syntax.

Desktop:

  • OS: Linux
  • Temple Version {:temple, "~> 0.6.0-rc.0"}
  • elixir 1.11.3-otp-23
  • erlang 23.2.3

Reproduction

Define the component as:

defmodule App.Component.MyComponent do
  use Temple.Component

  render do
    div class: "some classes #{assigns[:class]}" do
      @inner_content
    end
  end
end

This works:

c App.Component.MyComponent, do: "Something"

This works:

c App.Component.MyComponent, class: "-ml-4" do
    "Something"
end

This does not work:

c App.Component.MyComponent, class: "-ml-4", do: "Something"

assign @inner_content not available in eex template.

Please make sure all proper assigns have been set. If this
is a child template, ensure assigns are given explicitly by
the parent template as they are not automatically forwarded.

Available assigns: [:class, :do, :view_module, :view_template]

In a LiveView, `compact: true` does not seem to work

Describe the bug
I'm using compact: true to avoid whitespaces in the .alert tags showing the flashes. The tags still get spaces inside in a liveview.

To Reproduce

# in a non-liveview template
p compact: true, do: nil
p compact: true, do: ""

# in a liveview template
p compact: true, do: nil
p compact: true, do: ""

In the non-liveview template, the paragraphs will have no whitespaces inside in the browser.
In a liveview template, the paragraphs will have two newline chars in the browser.

Expected behavior
All examples should keep no whitespace inside

Desktop (please complete the following information):

  • OS: Ubuntu 20:04 ; in a docker container (elixir:latest at the time of writing)
  • Temple Version: master branch (at the time of writing)
  • Elixir Version: 1.10.4
  • Erlang Version: Erlang/OTP 22 [erts-10.7.2.3]

Additional context
Reading the code, I don't believe this is a purely temple-centric problem ; it might be LiveView adding whitespaces while massaging the dom or something.

Component modules seem not to work

Describe the bug
Hi, we've been puzzled when trying to use components, after being misled by the documentations (not checking the latest, our bad), we've checked the latest (for the 0.6.0-alpha4 version) yet all we get when creating a component and using it in a view is an undefined function error.

To Reproduce
We have configured a path for components:

# in config.exs
config :temple, :components_path, "./lib/our_app_web/components"
# in src/lib/our_app_web.ex
def view
  ...
  use Temple.Recompiler
end

We made a component:

# src/lib/our_app_web/components/permission_tag.html.exs
span class: "tag-permission", permission: @permission do
  @children
end

And we attempted to use it in our view:

# in src/lib/our_app_web/templates/role/index.html.exs
permission_tag permission: permission, do: ""

What what we get when compiling is:

web_1      | == Compilation error in file lib/our_app_web/views/role_view.ex ==
web_1      | ** (CompileError) lib/our_app_web/templates/role/index.html.exs:32: undefined function permission_tag/1
...

Expected behavior
The component function should be available to the view.

Screenshots
n/a

Desktop (please complete the following information):

  • OS: Linux b401d3627520 5.4.0-51-generic #56-Ubuntu SMP Mon Oct 5 14:28:49 UTC 2020 x86_64 GNU/Linux
  • Temple Version : 0.6.0-alpha4
  • Elixir Version : 1.10.4
  • Erlang Version : Erlang/OTP 22

Additional context
n/a

CSS support in the DSL?

Maybe this is something I'm missing from scanning documentation and the source code, but is there anyway to something like the script """ ... """ but for styles? Ideally I would like a way to just embed some CSS in one of those blocks and have the classes run through something like PostCSS to transform the classnames, but as a first step I would just like to define CSS in each file.

Components are broken with latest Phoenix Live.View release

Description

Components seem to not work with the latest release of Phoenix.LiveView.

I believe this is because liveview used to use a macro to render it's function components, but now it uses a regular function.

Fix

Not Sure ๐Ÿ˜ฉ.

[feature] multiple instances of the same slot name

it would be helpful to be able to define many slots with the same name and to be able to loop over them from inside the component.

This example also includes the ability to tag a slot definition with some metadata.

c Table, items: @items do
  slot :column, label: "Name", %{item: item} do
    strong do: item.name
  end

  slot :column, label: "SKU", %{item: item} do
    span do: item.sku
  end

  slot :column, label: "Manufacturer", %{item: %{manufacturer: m}} do
    a href: m.website_url, class: "text-blue-500 hover:underline" do
      m.name
    end
  end
end

# definition
defmodule Table do
  import Temple.Component

  render do
    table do
      thead do
        tr do
          for col <- @slots[:columns] do
            th do
              col.meta.label
            end
          end
        end
      end

      tbody do
        for item <- @items do
          tr do
            for col <- @slots[:column] do
              slot col, item: item
            end
          end
        end
      end
    end
  end
end

Docs are not clear about where to alias a component for use in a temple template

Describe the bug
Placing aliases (as per the component section in the documentation) to shorten component atoms in a temple view outputs that atom's string representation in the resulting html.

To Reproduce
In a temple view:

div, do: "Before"
alias WhateverWeb.Components.MyAwesomeComponent
div, do: "After"
<div>Before</div>
WhateverWeb.Components.MyAwesomeComponent
<div>After</div>

Expected behavior
The alias should not appear in the compiled html, or the documentation should point out a way to avoid this.

Desktop (please complete the following information):

  • OS: Ubuntu, with docker using elixir:1.11.2
  • Temple Version: 0.6.0-rc.0
  • Elixir Version: Elixir 1.11.2 (compiled with Erlang/OTP 23)
  • Erlang Version: Erlang/OTP 23 [erts-11.1.5] [source] [64-bit] [smp:12:12] [ds:12:12:10] [async-threads:1] [hipe]

Slots definitions aren't parsed correctly in nested components

Describe the bug

See the title

To Reproduce

The following markup will result in the Card component grabbing the slot that was meant for the LinkList.

c Card do
  c Card.Footer do
    c LinkList, socials: @user.socials do
      slot :link, %{text: text, url: url} do
        a class: "text-blue-500 hover:underline", href: url do
          text
        end
      end
    end
  end
end
This compiles into the following AST
%Temple.Parser.Components{
  assigns: [],
  children: [
    %Temple.Parser.Components{
      assigns: [],
      children: [
        %Temple.Parser.Components{
          assigns: [
            socials: {{:., [],
              [
                {:@, [context: Temple.Parser.ComponentsTest, import: Kernel],
                 [
                   {:user, [context: Temple.Parser.ComponentsTest],
                    Temple.Parser.ComponentsTest}
                 ]},
                :socials
              ]}, [no_parens: true], []}
          ],
          children: [%Temple.Parser.Text{text: "hello"}, %Temple.Parser.Empty{}],
          module: LinkList,
          slots: []
        }
      ],
      module: Card.Footer,
      slots: []
    }
  ],
  module: Card,
  slots: [
    %Temple.Parser.Slottable{
      assigns: {:%{}, [],
       [
         text: {:text, [], Temple.Parser.ComponentsTest},
         url: {:url, [], Temple.Parser.ComponentsTest}
       ]},
      content: [
        %Temple.Parser.NonvoidElementsAliases{
          attrs: [
            class: "text-blue-500 hover:underline",
            href: {:url, [], Temple.Parser.ComponentsTest}
          ],
          children: [
            %Temple.Parser.Default{
              elixir_ast: {:text, [], Temple.Parser.ComponentsTest}
            }
          ],
          name: "a"
        }
      ],
      name: :default
    }
  ]
}

Expected behavior

I believe that the correct behavior is that a slot definition should only occur in the root of the component inner content.

This is the AST that should be compiled
%Temple.Parser.Components{
  assigns: [],
  children: [
    %Temple.Parser.Components{
      assigns: [],
      children: [
        %Temple.Parser.Components{
          assigns: [
            socials: {{:., [],
              [
                {:@, [context: Temple.Parser.ComponentsTest, import: Kernel],
                 [
                   {:user, [context: Temple.Parser.ComponentsTest],
                    Temple.Parser.ComponentsTest}
                 ]},
                :socials
              ]}, [no_parens: true], []}
          ],
          children: [%Temple.Parser.Text{text: "hello"}, %Temple.Parser.Empty{}],
          module: LinkList,
          slots: [
            %Temple.Parser.Slottable{
              assigns: {:%{}, [],
               [
                 text: {:text, [], Temple.Parser.ComponentsTest},
                 url: {:url, [], Temple.Parser.ComponentsTest}
               ]},
              content: [
                %Temple.Parser.NonvoidElementsAliases{
                  attrs: [
                    class: "text-blue-500 hover:underline",
                    href: {:url, [], Temple.Parser.ComponentsTest}
                  ],
                  children: [
                    %Temple.Parser.Default{
                      elixir_ast: {:text, [], Temple.Parser.ComponentsTest}
                    }
                  ],
                  name: "a"
                }
              ],
              name: :default
            }
          ]
        }
      ],
      module: Card.Footer,
      slots: []
    }
  ],
  module: Card,
  slots: []
}

Mix Task fails without single root tag

Describe the bug
When attempting to use mix temple.convert to translate an existing HTML file into Temple syntax, the conversion fails without a single root HTML tag.

File 1

<section class="phx-hero">
  <h1><%= gettext "Welcome to %{name}!", name: "Phoenix" %></h1>
  <p>A productive web framework that<br/>
    does not compromise speed or maintainability.</p>
</section>

File 2

<section class="phx-hero">
  <h1><%= gettext "Welcome to %{name}!", name: "Phoenix" %></h1>
  <p>A productive web framework that<br/>
    does not compromise speed or maintainability.</p>
</section>
<section class="row">
  <article class="column">
    <h2>Resources</h2>
    <ul>
      <li>
        <a href="https://hexdocs.pm/phoenix/overview.html">Guides &amp; Docs</a>
      </li>
    </ul>
  </article>
</section>

To Reproduce
Steps to reproduce the behavior:

  1. Attempting to run mix temple.convert on File 2 above fails

Expected behavior
Temple syntax should be reported to StdOut

Desktop (please complete the following information):

  • OS: Mac OSX 10.15.1 Catalina
  • Temple Version: master (0.4.1)
  • Elixir Version: 1.9.1
  • Erlang Version: 22

Error

% cat apps/gf_web/lib/gf_web/templates/page/index.html.eex | mix temple.convert
** (FunctionClauseError) no function clause matching in Temple.HtmlToTemple.do_parse/2

    The following arguments were given to Temple.HtmlToTemple.do_parse/2:

        # 1
        [{"section", [{"class", "phx-hero"}], [{"h1", [], ["<%= gettext \"Welcome to %{name}!\", name: \"Phoenix\" %>"]}, {"p", [], ["A productive web framework that", {"br", [], []}, "\n    does not compromise speed or maintainability."]}]}, {"section", [{"class", "row"}], [{"article", [{"class", "column"}], [{"h2", [], ["Resources"]}, {"ul", [], [{"li", [], [{"a", [{"href", "https://hexdocs.pm/phoenix/overview.html"}], ["Guides & Docs"]}]}, {"li", [], [{"a", [{"href", "https://github.com/phoenixframework/phoenix"}], ["Source"]}]}, {"li", [], [{"a", [{"href", "https://github.com/phoenixframework/phoenix/blob/v1.4/CHANGELOG.md"}], ["v1.4 Changelog"]}]}]}]}, {"article", [{"class", "column"}], [{"h2", [], ["Help"]}, {"ul", [], [{"li", [], [{"a", [{"href", "https://elixirforum.com/c/phoenix-forum"}], ["Forum"]}]}, {"li", [], [{"a", [{"href", "https://webchat.freenode.net/?channels=elixir-lang"}], ["#elixir-lang on Freenode IRC"]}]}, {"li", [], [{"a", [{"href", "https://twitter.com/elixirphoenix"}], ["Twitter @elixirphoenix"]}]}]}]}]}]

        # 2
        0

    Attempted function clauses (showing 5 out of 5):

        def do_parse({tag, [], []}, indent)
        def do_parse({tag, attrs, []}, indent)
        def do_parse({tag, attrs, [""]}, indent)
        def do_parse({tag, attrs, children}, indent)
        def do_parse(text, indent) when is_binary(text)

    lib/temple/html_to_temple.ex:16: Temple.HtmlToTemple.do_parse/2
    lib/temple/html_to_temple.ex:11: Temple.HtmlToTemple.parse/1
    lib/mix/tasks/temple.convert.ex:19: Mix.Tasks.Temple.Convert.run/1
    (mix) lib/mix/task.ex:331: Mix.Task.run_task/3
    (mix) lib/mix/cli.ex:79: Mix.CLI.run_task/2

[feature] Pass component module as slot name

It would be cool to be able to pass a component as the slot name, allowing easier composition of shared markup.

Now

defmodule TempleExampleWeb.Components.LinkList do
  import Temple.Component

  defcomp Item do
    a class: "text-blue-500 hover:underline", href: @url do
      slot :default
    end
  end

  render do
    for {social, url} <- @socials do
      li do
        span do
          slot :link, url: url, text: social
        end
      end
    end
  end
end

# usage
c LinkList, socials: @user.socials do
  slot :link, %{text: text, url: url} do
    c LinkList.Item, url: url do
      text
    end
  end
end

Then

defmodule TempleExampleWeb.Components.LinkList do
  import Temple.Component

  defcomp Item do
    a class: "text-blue-500 hover:underline", href: @url do
      slot :default, text: @text
    end
  end

  render do
    for {social, url} <- @socials do
      li do
        span do
          slot LinkList.Item, url: url, text: social
        end
      end
    end
  end
end

# usage
c LinkList, socials: @user.socials do
  slot LinkList.Item, %{text: text} do
    text
  end
end

Something like that at least

Allow a list of elements as first param

Currently it's not possible to do this:

defcomponent :top_bar do
  div @children, class: "some classes"
end

It looks like Temple tries to convert @children to a string and in some cases succeeds, but not in a clean way. I have quite a few of these wrapper components and think it would be nice to remove those unnecessary do blocks.

Error about missing inner_block assign when attempting to render a form in a component

This is the bug I get: ** (ArgumentError) assign @inner_block not available in eex template.

This is the code:

live/page_live.ex:

      render do
        c Modal, on_cancel: "cancel" do
          "Form here:"

          c LiveFormFor,
            form:
              form_for(@changeset, "#",
                id: "create-meeting-id",
                data: [role: "standup"],
                phx_submit: "create-meeting",
                phx_hook: "ModalFocusSandbox"
              ) do
            slot :form, %{form: form} do
#etc...

components/modal.ex

defmodule App.Component.Modal do
  import Temple.Component

  render do
    div class: "opacity-80 bg-black bg-opacity-80 inset-0 absolute",
        "phx-click": @on_cancel do
      slot :default
    end
  end
end

components/form.ex

defmodule App.Component.LiveFormFor do
  import Temple.Component

  render do
    @form

    slot :form, form: @form

    "</form>"
  end
end

Now if I render the form outside of the modal component everything works fine. Similarly, if I render another modal component inside the modal component everything works fine. If I remove the slot :form ... from the form component I don't get the error.

Do you know why this is happening or what it means? Do you need any additional information? This is with 0.6.0-rc.1 with Elixir 1.11.4.

Thank you.

Attributes dynamically set to false are not discarded

Describe the bug
As per temple docs if an attribute's value is false its discarded and not rendered.
for ex

temple do
	div class: "test", a: false, b: "hello", do: "Hello"
end

would produce the following markup

<div class="test" b="hello">Hello</div>

Note that there is no attribute above with the name "a".
But if you set the value of the attribute dynamically, temple converts the value to string and outputs it into the markup.

temple do
	a = false
	div class: "test", a: a, b: "hello", do: "Hello"
end

would produce the following markup

<div class="test" a="false" b="hello">Hello</div>

To Reproduce
Set any attribute dynamically to false and check the produced markup

Expected behavior
The attribute whose value is false should get discarded from the markup

Desktop (please complete the following information):

  • OS: MacOS Monterey 12.3.1
  • Temple Version: 0.10
  • Elixir Version: 1.14
  • Erlang Version: OTP 23, erts: 11..0.2

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo 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.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.