Markup
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 %>">
<card-panel>
<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>
</add-button>
</section>
<section slot="items">
<% tasks.each do |task| %>
<task-list-item task-data="<%= task.to_json"></task-list-item>
<% end %>
</section>
</list>
</card-panel>
</task-grid>
</folder-page>
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.