Простые компоненты просмотра для Rails 5.1+, предназначенные для того, чтобы хорошо проходить с elemental_styleguide. Два вместе вдохновлены работами Брэда Фроста и мыслями, стоящим за руководством по стилю одинокой планеты Риццо.
Добавьте эту строку в 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
*/Есть два способа передачи данных в компоненты: атрибуты и блоки контента. Атрибуты полезны для таких данных, как идентификаторы, модификаторы и структуры данных (модели и т. Д.). Блоки контента полезны, когда вам нужно вводить 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 .Используя этот драгоценный камень, такой же компонент можно записать так:
# 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 , чтобы вызовать помощников Rails, таких как 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Тогда назовите это из шаблона, как SO:
<%= component "objects/media_object" %> Эта библиотека, вместе с elemental_styleguide, была вдохновлена писаниями Брэда Фроста на руководствах по атомному дизайну и стилю жизни, а также Rizzo, руководством по стилю Lonely Planet. Другими вдохновениями были:
Чтобы получить список руководств по реальному мировому стилю, ознакомьтесь с http://styleguides.io.