-
Notifications
You must be signed in to change notification settings - Fork 4
编写 Tao Component
假设我们已经有一个 todos#index
页面,这个页面的内容是一个 Todo 的列表:
# app/views/todos/index.html.erb
<%= render @todos %>
现在我们希望将 Todo 抽象为独立的组件,这样在 _todo.html.erb
这个 partial 模版里面,只需要调用 Todo 组件对应的 view helper:
# app/views/todos/_todo.html.erb
<%= tao_todo_item todo %>
首先,我们需要创建服务器端对应的 Component 类:
# app/components/todos/item_componnent.rb
class Todos::ItemComponent < ApplicationComponent
attr_reader :todo
def initialize view, todo, options = {}
super view, options
@todo = todo
end
end
这个 Component 类会帮助我们动态定义对应的 view helper 方法,方法的默认命名规则是 Component.tag_name.underscore
,而 tag_name
的定义是:
# from TaoOnRails::Components::Base
def self.tag_name
@tag_name ||= "#{self.tag_prefix}-#{self.component_name.to_s.dasherize}"
end
def self.component_name
@component_name ||= self.name.underscore.split('/').map(&:singularize).join('_')
.gsub(/(.+)_component$/, '\1')
.gsub(/^#{Regexp.quote(self.tag_prefix.to_s.underscore)}_(.+)/, '\1')
end
def self.tag_prefix
:tao
end
Todos::ItemComponent
对应的 view helper 名称就是 tao_todo_item
,其中 tao
这个前缀是可以在 ApplicationComponent 或者 ItemComponent 里面重新定义的,比如:
class Todos::ItemComponent < ApplicationComponent
...
def self.tag_prefix
:awesome
end
end
Component 类的另一个作用是决定组件的渲染逻辑,默认的渲染逻辑会尝试在 app/views/components/todos/
文件夹里面查找名称为 _item.html.erb
的 partial 模版,如果找到了就渲染这个模版,如果没有找到就渲染一个默认的 Custom Element 容器,例如:
<tao-todo-item>
<!-- 如果调用 view helper 的时候提供了block,那么 block 的内容会渲染在这里 -->
</tao-todo-item>
所以现在我们需要在 app/views/components/todos/
文件夹里面创建 _todo.html.erb
模版,然后在这里面编写 Todo 组件的 HTML:
# app/views/components/todos/_todo.html.erb
<tao-todo-item id="todo-item-<%= component.todo.id %>">
<%= check_box_tag nil, '1', component.todo.completed, class: 'todo-checkbox' %>
<span class="todo-content"><%= component.todo.content %></span>
<tao-todo-item>
Component 对象的实例会作为 local variable 传入这个 partial,所以模版里面可以使用 component
实例的任何 public attribute/method。假设我们现在有一个产品需求是,Todo 内容里面的 “#XXX#” 格式会定义任务的 Tag,Tag 需要渲染为链接,我们可以通过 Component 类的两个 public method 来实现这个需求:
# app/components/todos/item_componnent.rb
class Todos::ItemComponent < ApplicationComponent
...
def tags
end
def content_without_tags
end
end
然后修改 _todo.html.erb
:
# app/views/components/todos/_todo.html.erb
<tao-todo-item id="todo-item-<%= component.todo.id %>">
<%= check_box_tag nil, '1', component.todo.completed, class: 'todo-checkbox' %>
<div class="tags">
<% component.tags.each do |tag| %>
<%= link_to tag, '#', class: 'tag' %>
<% end %>
</div>
<div class="content"><%= component.todo.content %></div>
<tao-todo-item>