When you want to create HTML simple components, using partials.
Components are stored in app/views/components
and are named _component.html.erb
.
They can also be stored in a subdirectory of app/views/components
and are named using the component name for the directory and the partial component/_component.html.erb
.
Additional subcomponent partials can be included in the component directory allowing to break up the component into smaller pieces.
Create a button component in app/views/components/_button.html.erb
:
<button>
<%= this.text %>
</button>
Use this
to access the component instance containing locals and subcomponents.
To use the component in a view:
<%= component :button, text: 'Click Me' %>
Using with the method_missing shorthand:
<%= button text: 'Click Me' %>
Locals can also be set within the component block:
<%= component :button do |c| %>
<% c.text = 'Click Me' %>
<% end %>
Create a card component in app/views/components/card/_card.html.erb
:
<div>
<%= this.render :title %>
<%= this.text || this.yield %>
</div>
Create the title subcomponent in app/views/components/card/_title.html.erb
:
<h1>
<%= this.text %>
</h1>
You can use this.render
to render a subcomponent. The subcomponent will have access
to the locals and subcomponents passed in the view.
You can use this.yield
to render the block passed in the view.
To use the component in a view:
<%= component :card do |c| %>
<%= c.title text: 'Card Title' %>
<p>Card Text</p>
<% end %>
This will render:
<div>
<h1>Card Title</h1>
<p>Card Text</p>
</div>
Create a card component in app/views/components/card/_card.html.erb
:
<div>
<% this.copy_components :links, :mobile_links %>
<%= this.render :links %>
<%= this.render :mobile_links %>
<%= this.text || this.yield %>
</div>
Create the link subcomponent in app/views/components/card/_link.html.erb
:
<a class="desktop" href="<%= this.url %>">
<%= this.index %>: <%= this.text %>
</a>
Create the mobile_link subcomponent in app/views/components/card/_mobile_link.html.erb
:
<a class="mobile" href="<%= this.url %>">
<%= this.index %>: <%= this.text %>
</a>
You can use this.render_all
to render multiple subcomponent. The subcomponent will have access
to the locals and subcomponents passed in the view. The components index
method will be set.
To use the component in a view:
<%= component :card do |c| %>
<%= c.link text: 'Card Link 1', url: '#" %>
<%= c.link text: 'Card Link 2', url: '#" %>
<p>Card Text</p>
<% end %>
This will render:
<div>
<a class="desktop" href="#">
0: Card Link 1
</a>
<a class="desktop" href="#">
1: Card Link 2
</a>
<a class="mobile" href="#">
0: Card Link 1
</a>
<a class="mobile" href="#">
1: Card Link 2
</a>
<p>Card Text</p>
</div>
In some cases you may want to use a local variable that
clashes with existing methods on Object
. In these cases you can access
the locals hash directly.
<h1>
<%= this.local(:method) %>
</h1>
In some cases you may want to use a subcomponent that
clashes with existing methods on Object
. In these cases you can access
the subcomponents hash directly.
<%= this.components(:method) %>
This returns and Array of subcomponents. You can render the first subcomponent with
a given key using, this.render(:header)
.
<%= this.render(:header) %>
If you have multiple subcomponents with the same key, you can render them all using
this.render_all(:header)
.
<%= this.render_all(:header) %>
If you want to render all subcomponents, but with HTML between them,
you can use this.components(:actions)
.
<ul>
<% this.components(:actions).each do |sub| %>
<li><%= sub.render %></li>
<% end %>
</ul>
Add this line to your application's Gemfile:
$ bundle add subcomponent
Or install it yourself as:
$ gem install subcomponent
Bug reports and pull requests are welcome on GitHub at https://github.com/candland/subcomponent.
The gem is available as open source under the terms of the MIT License.