This is an actual piece of view code that I wrote recently:

<%= render "folder_tasks/task_grid", folder: folder, task_type: task_type, scope: scope, sort_order: sort_order do %>
  <%= card_panel do %>
    <% if tasks.empty? %>
      <%= action_bar style: :between do %>
        <%= t "tasks.there_are_no_tasks", tasks: task_type.plural %>
        <%= link_to icon_for("add_task", t("tasks.add_task", task_type: task_type)), new_folder_task_type_task_path(folder), class: "btn btn-outline-primary" if can? :add_task, folder %>
      <% end %>
    <% else %>
      <% tasks.each do |task| %>
        <%= render "folder_tasks/task", task: task, folder: folder, task_type: task_type %>
      <% end %>
    <% end %>
  <% end %>
<% end %>

If you know Rails, you'll see that there's a container partial - "task_grid", that draws out the container for a set of tasks (which in turn calls another partial - "folder_layout" which draws out the tabs relevant to that folder, all of which is rendered within the application layout, which includes a header, footer and sidebar).

Within the grid, we have a "card_panel" that contains an "action_bar" that has a "new task" link. The action bar is only drawn if there are no tasks; if there are tasks, then each one is drawn out using it's own "task" partial.

This is relatively clean for a Rails app - certainly a lot better than some of the projects that I've inherited off other developers.

But I think it should look like this:

<folder-page folder-data="<%= folder.to_json %>">
  <task-grid task-type-data="<%= task_type.to_json %>" scope="<%= scope %>" sort-order="<%= sort_order %>">
      <list count=<%= tasks.count %>>
        <section slot="no-items">
          <caption><%= t "tasks.there_are_no_tasks", tasks: task_type.plural %>"></caption>
          <add-button show-if=<%= can? :add_task, folder %> href="<%= new_folder_task_type_task_path(folder) %>">
            <icon type="add_task"><%= t("tasks.add_task", task: task_type.to_s)) %></icon>
        <section slot="items">
          <% tasks.each do |task| %>
            <task-list-item task-data="<%= task.to_json"></task-list-item>
          <% end %>

Each element of the page is then represented by some markup that describes what that element is. Each element knows how to display itself - a "task-list-item" is given the JSON representing that task and knows what to draw. Or even better, it's optionally given a URL, it fetches the JSON from the server and then knows what to draw. If anything, the worst thing about this version are the ERB <%= and %> tags.

For this reason, I'm now looking at web-components. Shoelaces, a library of prebuilt components looks amazing. Svelte is the new hotness for building them, but there's always been a part of me that likes Vue.

I'll let you know how I get on.

Rahoul Baruah

Rahoul Baruah

Rubyist since 1.8.6. Freelancer since 2007, dedicated to building incredible, low-cost, bespoke software for tiny businesses. Also CTO at Collabor8Online.
Leeds, England