Rails 5.1+的简单视图组件,旨在与Elemental_styleguide配合使用。两者一起灵感来自布拉德·弗罗斯特(Brad Frost)的作品以及《孤独星球风格指南》(Rizzo)背后的想法。
将此行添加到您的应用程序的gemfile:
gem "elemental_components"然后执行:
$ bundle此处提供的示例将使用BEM命名约定。
组件生活在app/components中。通过执行来生成组件:
$ bin/rails g elemental_components:component alert这将创建以下文件:
app/
components/
alert/
_alert.html.erb
alert.css
alert.js
alert_component.rb
发电机还采用--skip-css和--skip-js选项。
让我们添加一些标记和CSS:
<% # app/components/alert/_alert.html.erb %>
< div class =" alert alert--primary " role =" alert " >
Message
</ div > /* app/components/alert/alert.css */
. alert {
padding : 1 rem ;
}
. alert--primary {
background : blue;
}
. alert--success {
background : green;
}
. alert--danger {
background : red;
}现在可以使用component助手来渲染此组件:
<%= component "alert" %> 为了需要CSS之类的资产,要么在清单中手动需要它们,例如application.css :
/*
*= require alert/alert
*/或需要components ,这又需要所有组件的资产:
/*
*= require elemental_components
*/将数据传递给组件有两种方法:属性和内容块。属性可用于数据,例如ID,修饰符和数据结构(模型等)。当您需要将HTML内容注入组件时,内容块很有用。
让我们定义一些我们刚创建的组件的属性:
# app/components/alert_component.rb %>
class AlertComponent < ElementalComponents :: Component
attribute :context
attribute :message
end <% # app/components/alert/_alert.html.erb %>
< div class =" alert alert-- <%= alert.context %> " role =" alert " >
<%= alert.message %>
</ div > <%= component "alert", message: "Something went right!", context: "success" %>
<%= component "alert", message: "Something went wrong!", context: "danger" %> 为了将一些文本或HTML内容注入我们的组件中,我们可以在模板中打印组件。包含方法,并通过将内容块传递给组件助手:填充它:
<% # app/components/alert/_alert.html.erb %>
< div class =" alert alert-- <%= alert.context %> " role =" alert " >
<%= alert.content %>
</ div > <%= component "alert", context: "success" do %>
< em > Something </ em > went right!
<% end %>属性的另一个好的用例是,当您具有由模型支持的组件时:
# app/components/comment_component.rb %>
class CommentComponent < ElementalComponents :: Component
attribute :comment
delegate :id ,
:author ,
:body , to : :comment
end <% # app/components/comment/_comment.html.erb %>
< div id =" comment- <%= comment.id %> " class =" comment " >
< div class =" comment__author " >
<%= link_to comment.author.name, author_path(comment.author) %>
</ div >
< div class =" comment__body " >
<%= comment.body %>
</ div >
</ div > <% comments.each do |comment| %>
<%= component "comment", comment: comment %>
<% end %> 属性可以具有默认值:
# app/components/alert_component.rb %>
class AlertComponent < ElementalComponents :: Component
attribute :message
attribute :context , default : "primary"
end用其他逻辑覆盖属性很容易:
# app/components/alert_component.rb %>
class AlertComponent < ElementalComponents :: Component
attribute :message
attribute :context , default : "primary"
def message
@message . upcase if context == "danger"
end
end为了确保您的组件适当初始化,您可以在元素或组件中使用ActiveModel::Validations :
# app/components/alert_component.rb %>
class AlertComponent < ElementalComponents :: Component
attribute :label
validates :label , presence : true
end您的验证将在组件初始化期间执行,并在任何验证失败时提高ActiveModel::ValidationError 。
属性和块非常适合以数据结构(例如模型)支持的简单组件或组件。其他组件本质上是更通用的,可以在各种情况下使用。它们通常由多个部分或元素组成,有时会重复,有时需要自己的修饰符。
取一个卡组件。在React中,一种常见的方法是创建子组件:
< Card flush = { true } >
< CardHeader centered = { true } >
Header
</ CardHeader >
< CardSection size = "large" >
Section 1
</ CardSection >
< CardSection size = "small" >
Section 2
</ CardSection >
< CardFooter >
Footer
</ CardFooter >
</ Card >这种方法有两个问题:
CardHeader放置在Card外。CardHeader放置在下面或在CardFooter内。使用此GEM,可以像这样编写相同的组件:
# app/components/card_component.rb %>
class CardComponent < ElementalComponents :: Component
attribute :flush , default : false
element :header do
attribute :centered , default : false
end
element :section , multiple : true do
attribute :size
end
element :footer
end <% # app/components/card/_card.html.erb %>
< div class =" card <%= "card--flush" if card.flush %> " >
<% if card.header.content? %>
< div class =" card__header <%= "card__header--centered" if card.header.centered %> " >
<%= card.header.content %>
</ div >
<% end %>
<% card.sections.each do |section| %>
< div class =" card__section <%= "card__section--#{section.size}" %> " >
<%= section.content %>
</ div >
<% end %>
<% if card.footer.content? %>
< div class =" card__footer " >
<%= card.footer.content %>
</ div >
<% end %>
</ div >可以将元素视为孤立的子组件,并且它们在组件上定义。传递multiple: true使其成为重复元素,并且通过一个块使我们可以以相同的方式声明元素上的属性。
为了用数据填充它们,我们将一个块传递给组件助手,该组件辅助器会产生组件,这使我们可以在元素上以相同的方式对元素设置属性和内容块:
<%= component "card", flush: true do |c| %>
<% c.header centered: true do %>
Header
<% end %>
<% c.section size: "large" do %>
Section 1
<% end %>
<% c.section size: "large" do %>
Section 2
<% end %>
<% c.footer do %>
Footer
<% end %>
<% end %> 对重复元素的多个调用,例如上面的示例中的section ,将每个部分附加到数组。
另一个好的用例是导航组件:
# app/components/navigation_component.rb %>
class NavigationComponent < ElementalComponents :: Component
element :items , multiple : true do
attribute :label
attribute :url
attribute :active , default : false
end
end <%= component "navigation" do |c| %>
<% c.item label: "Home", url: root_path, active: true %>
<% c.item label: "Explore" url: explore_path %>
<% end %> 这里的替代方法是将数据结构作为属性传递给组件,如果在渲染组件时不需要注入HTML:
<%= component "navigation", items: items %> 元素也可以进行验证:
class NavigationComponent < ElementalComponents :: Component
element :items , multiple : true do
attribute :label
attribute :url
attribute :active , default : false
validates :label , presence : true
validates :url , presence : true
end
end元素也可以嵌套,尽管建议将嵌套到最低限制:
# app/components/card_component.rb %>
class CardComponent < ElementalComponents :: Component
...
element :section , multiple : true do
attribute :size
element :header
element :footer
end
end除了声明属性和元素外,还可以声明助手方法。如果您希望将逻辑放在模板之外,这将很有用。让我们从卡组件模板中提取修饰符逻辑:
# app/components/card_component.rb %>
class CardComponent < ElementalComponents :: Component
...
def css_classes
css_classes = [ "card" ]
css_classes << "card--flush" if flush
css_classes . join ( " " )
end
end <% # app/components/card/_card.html.erb %>
<%= content_tag :div, class: card.css_classes do %>
...
<% end %>甚至可以在元素上宣布帮助者:
# app/components/card_component.rb %>
class CardComponent < ElementalComponents :: Component
...
element :section , multiple : true do
attribute :size
def css_classes
css_classes = [ "card__section" ]
css_classes << "card__section-- #{ size } " if size
css_classes . join ( " " )
end
end
end <% # app/components/card/_card.html.erb %>
<%= content_tag :div, class: card.css_classes do %>
...
<%= content_tag :div, class: section.css_classes do %>
<%= section %>
<% end %>
...
<% end %>助手方法还可以使用@view实例变量,以调用link_to或content_tag等导轨帮助者。
对于某些小组件(例如按钮),完全跳过部分以加快渲染速度可能是有意义的。这可以通过在组件上覆盖render来完成:
# app/components/button_component.rb %>
class ButtonComponent < ElementalComponents :: Component
attribute :label
attribute :url
attribute :context
def render
@view . link_to label , url , class : css_classes
end
def css_classes
css_classes = "button"
css_classes << "button-- #{ context } " if context
css_classes . join ( " " )
end
end <%= component "button", label: "Sign up", url: sign_up_path, context: "primary" %>
<%= component "button", label: "Sign in", url: sign_in_path %> 组件可以嵌套在名称空间下。如果您想练习诸如原子设计,bemit或任何其他组件分类方案之类的东西,这将很有用。为了创建名称尺寸的组件,请将其粘贴在文件夹中,然后将类包装在模块中:
module Objects
class MediaObject < ElementalComponents :: Component ; end
end然后从类似模板中调用它:
<%= component "objects/media_object" %> 该图书馆与Elemental_styleguide一起,灵感来自布拉德·弗罗斯特(Brad Frost)关于原子设计和生活方式指南的著作,以及《孤独星球风格指南》(Rizzo)。其他灵感是:
有关现实世界风格指南的列表,请查看http://styleguides.io。