Componentes de vista simple para Rails 5.1+, diseñados para ir bien con Elemental_StyleGuide. Los dos juntos están inspirados en las obras de Brad Frost y por los pensamientos detrás de la guía de estilo de Lonely Planet Rizzo.
Agregue esta línea al archivo gem de su aplicación:
gem "elemental_components"Y luego ejecutar:
$ bundleLos ejemplos proporcionados aquí utilizarán las convenciones de nombres de BEM.
Los componentes viven en app/components . Genere un componente ejecutando:
$ bin/rails g elemental_components:component alertEsto creará los siguientes archivos:
app/
components/
alert/
_alert.html.erb
alert.css
alert.js
alert_component.rb
El generador también toma opciones --skip-css y --skip-js .
Agreguemos un poco de marcado y 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;
} Este componente ahora se puede representar utilizando el component ayudante:
<%= component "alert" %> Para requerir activos como CSS, los requiera manualmente en el manifiesto, por ejemplo, application.css :
/*
*= require alert/alert
*/ O requerir components , que a su vez requerirán los activos para todos los componentes:
/*
*= require elemental_components
*/Hay dos formas de pasar datos a componentes: atributos y bloques de contenido. Los atributos son útiles para datos como IDS, modificadores y estructuras de datos (modelos, etc.). Los bloques de contenido son útiles cuando necesita inyectar contenido HTML en componentes.
Definamos algunos atributos para el componente que acabamos de crear:
# 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" %> Para inyectar algo de texto o contenido HTML en nuestro componente, podemos imprimir el método de componente .Content en nuestra plantilla y llenarlo pasando un bloque de contenido al componente ayudante:
<% # 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 %>Otro buen caso de uso para los atributos es cuando tiene un componente respaldado por un modelo:
# 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 %> Los atributos pueden tener valores predeterminados:
# app/components/alert_component.rb %>
class AlertComponent < ElementalComponents :: Component
attribute :message
attribute :context , default : "primary"
endEs fácil anular un atributo con lógica adicional:
# app/components/alert_component.rb %>
class AlertComponent < ElementalComponents :: Component
attribute :message
attribute :context , default : "primary"
def message
@message . upcase if context == "danger"
end
end Para asegurarse de que sus componentes se inicialicen correctamente, puede usar ActiveModel::Validations en sus elementos o componentes:
# app/components/alert_component.rb %>
class AlertComponent < ElementalComponents :: Component
attribute :label
validates :label , presence : true
end Sus validaciones se ejecutarán durante la inicialización de los componentes y plantearán un ActiveModel::ValidationError si alguna validación falla.
Los atributos y bloques son excelentes para componentes o componentes simples respaldados por una estructura de datos, como un modelo. Otros componentes son más genéricos de naturaleza y pueden usarse en una variedad de contextos. A menudo consisten en múltiples partes o elementos, que a veces se repiten, y a veces necesitan sus propios modificadores.
Tome un componente de tarjeta. En React, un enfoque común es crear subcomponentes:
< Card flush = { true } >
< CardHeader centered = { true } >
Header
</ CardHeader >
< CardSection size = "large" >
Section 1
</ CardSection >
< CardSection size = "small" >
Section 2
</ CardSection >
< CardFooter >
Footer
</ CardFooter >
</ Card >Hay dos problemas con este enfoque:
CardHeader podría colocarse fuera de una Card .CardHeader debajo, o dentro de un CardFooter .Usando esta gema, se puede escribir el mismo componente como este:
# 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 > Los elementos pueden considerarse como subcomponentes aislados, y se definen en el componente. Pasar multiple: true lo convierte en un elemento repetido, y pasar un bloque nos permite declarar atributos en nuestros elementos, de la misma manera que declaramos atributos en los componentes.
Para llenarlos con datos, pasamos un bloque al componente ayudante, que produce el componente, lo que nos permite establecer atributos y bloques de contenido en el elemento de la misma manera que lo hacemos para los componentes:
<%= 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 %> Múltiples llamadas a un elemento repetitivo, como section en el ejemplo anterior, agregarán cada sección a una matriz.
Otro buen caso de uso es un componente de navegación:
# 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 %> Una alternativa aquí es pasar una estructura de datos al componente como un atributo, si no es necesario inyectar HTML al renderizar el componente:
<%= component "navigation", items: items %> Los elementos también pueden tener validaciones:
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
endLos elementos también pueden estar anidados, aunque se recomienda seguir anidando al mínimo:
# app/components/card_component.rb %>
class CardComponent < ElementalComponents :: Component
...
element :section , multiple : true do
attribute :size
element :header
element :footer
end
endAdemás de declarar atributos y elementos, también es posible declarar métodos auxiliares. Esto es útil si prefiere mantener la lógica fuera de sus plantillas. Extraemos la lógica del modificador de la plantilla del componente de la tarjeta:
# 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 %>Incluso es posible declarar ayudantes en elementos:
# 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 %> Los métodos auxiliar también pueden hacer uso de la variable de instancia @view para llamar a ayudantes de rieles como link_to o content_tag .
Para algunos componentes pequeños, como los botones, podría tener sentido omitir lo parcial por completo, para acelerar la representación. Esto se puede hacer anulando render en el componente:
# 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 %> Los componentes pueden anidarse bajo un espacio de nombres. Esto es útil si desea practicar cosas como diseño atómico, bemit o cualquier otro esquema de clasificación de componentes. Para crear un componente lleno de nombres, péguelo en una carpeta y envuelva la clase en un módulo:
module Objects
class MediaObject < ElementalComponents :: Component ; end
endLuego llámalo desde una plantilla como así:
<%= component "objects/media_object" %> Esta biblioteca, junto con Elemental_StyleGuide, se inspiró en los escritos de Brad Frost sobre el diseño atómico y las guías de estilo de vida, y Rizzo, la guía de estilo Lonely Planet. Otras inspiraciones fueron:
Para obtener una lista de guías de estilo del mundo real, consulte http://styleguides.io.