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

Trouble composing several elements #30

Open
dylan-hoefsloot opened this issue Oct 3, 2021 · 3 comments
Open

Trouble composing several elements #30

dylan-hoefsloot opened this issue Oct 3, 2021 · 3 comments

Comments

@dylan-hoefsloot
Copy link
Contributor

Not sure If I am just misusing ratatouille or it is not possible but anyway I would like to clarify some behaviour

If I have the following for rendering two panels

def render_first do
  column(size: 12) do
    panel(title: "First", height: 4)
  end
end

def render_second do
  column(size: 12) do
    panel(title: "Second", height: 4)
  end
end

I can do it like this and both rows show up as expected:

def render(model) do
  view do
    row do render_first end
    row do render_second end
  end
end

However, if I do it like this, only the last defined row will ever show up:

def render_both do
  row do render_first end
  row do render_second end
end

def render(model) do
  view do
    render_both
  end
end

It works if I change it to the following:

def render_both do
  panel do
    row do render_first end
    row do render_second end
  end
end

def render(model) do
  view do
    render_both
  end
end

but then I have a panel and the border, which I don't want.

@christhekeele
Copy link

This is a (surprising, but logical) consequence of how Elixir return values and the Ratatouille DSL interact:

Elixir functions return the last statement from their do blocks; Ratatouille view macros extract all child macros from their do blocks.

So what you are seeing is:

def render(model) do # returns the last expr, `view`
  view do # extracts both `row`s
    row do render_first end
    row do render_second end
  end
end

vs

def render_both do # returns the last expr, the second `row`
  row do render_first end
  row do render_second end
end

def render(model) do
  view do
    render_both # inserts the last expr from `render_both`
  end
end

As you call attention to, this semantic difference in how do blocks are interpreted, while core to the DSL, does mean you have to context-switch how to correctly compose components depending on where you are doing it.

The best way I have found to work around this is by return an array of components:

def render_both do
  [
    row do render_first end,
    row do render_second end
  ]
end

To my surprise, using macros did not work. I would have imagine this succeeding, but also only renders the last row:

defmacrop render_both do
  quote do
    row do render_first end
    row do render_second end
  end
end

@mhanberg
Copy link

the issue lies in these two lines:

defp extract_children({:__block__, _meta, elements}), do: elements
defp extract_children(element), do: element

the workaround i've found is to wrap things in a viewport (I think at least, I just discovered this)

This code will not work

view do
  if true do
    row do
      column size: 4 do
        panel(title: "Col1")
      end

      column size: 4 do
        panel(title: "Col2")
      end

      column size: 4 do
        panel(title: "Col3")
      end
    end

    row do
      column size: 4 do
        panel(title: "Col1")
      end

      column size: 4 do
        panel(title: "Col2")
      end

      column size: 4 do
        panel(title: "Col3")
      end
    end
  end
end

This code will work

view do
 if true do
   viewport do
     row do
       column size: 4 do
         panel(title: "Col1")
       end

       column size: 4 do
         panel(title: "Col2")
       end

       column size: 4 do
         panel(title: "Col3")
       end
     end

     row do
       column size: 4 do
         panel(title: "Col1")
       end

       column size: 4 do
         panel(title: "Col2")
       end

       column size: 4 do
         panel(title: "Col3")
       end
     end
   end
 end
end

@christhekeele
Copy link

viewport feels like the appropriate approach here. I imagine a callout in the documentation would make for a valuable pull request.

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

3 participants