Composants de vue simples pour Rails 5.1+, conçus pour bien aller avec elemental_styleguide. Les deux ensemble sont inspirés par les œuvres de Brad Frost et par les pensées derrière le guide de style de Lonely Planet Rizzo.
Ajoutez cette ligne à Gemfile de votre application:
gem "elemental_components"Puis exécuter:
$ bundleLes exemples fournis ici utiliseront les conventions de dénomination BEM.
Les composants vivent dans app/components . Générer un composant en exécutant:
$ bin/rails g elemental_components:component alertCela créera les fichiers suivants:
app/
components/
alert/
_alert.html.erb
alert.css
alert.js
alert_component.rb
Le générateur prend également des options --skip-css et --skip-js .
Ajoutons un marquage et 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;
} Ce composant peut désormais être rendu à l'aide de l'assistance component :
<%= component "alert" %> Afin d'exiger des actifs tels que CSS, soit les nécessite manuellement dans le manifeste, par exemple application.css :
/*
*= require alert/alert
*/ Ou nécessitent components , qui à leur tour nécessiteront les actifs pour tous les composants:
/*
*= require elemental_components
*/Il existe deux façons de transmettre des données aux composants: les attributs et les blocs de contenu. Les attributs sont utiles pour des données telles que les ID, les modificateurs et les structures de données (modèles, etc.). Les blocs de contenu sont utiles lorsque vous devez injecter le contenu HTML en composants.
Définissons quelques attributs pour le composant que nous venons de créer:
# 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" %> Pour injecter un contenu de texte ou de HTML dans notre composant, nous pouvons imprimer la méthode du contenu du composant dans notre modèle et le remplir en passant un bloc de contenu à l'assistance du composant:
<% # 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 %>Un autre bon cas d'utilisation pour les attributs est lorsque vous avez un composant soutenu par un modèle:
# 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 %> Les attributs peuvent avoir des valeurs par défaut:
# app/components/alert_component.rb %>
class AlertComponent < ElementalComponents :: Component
attribute :message
attribute :context , default : "primary"
endIl est facile de remplacer un attribut avec une logique supplémentaire:
# app/components/alert_component.rb %>
class AlertComponent < ElementalComponents :: Component
attribute :message
attribute :context , default : "primary"
def message
@message . upcase if context == "danger"
end
end Pour vous assurer que vos composants sont initialisés correctement, vous pouvez utiliser ActiveModel::Validations dans vos éléments ou composants:
# app/components/alert_component.rb %>
class AlertComponent < ElementalComponents :: Component
attribute :label
validates :label , presence : true
end Vos validations seront exécutées lors de l'initialisation des composants et augmenteront un ActiveModel::ValidationError si une validation échoue.
Les attributs et les blocs sont parfaits pour les composants ou composants simples soutenus par une structure de données, comme un modèle. D'autres composants sont de nature plus générique et peuvent être utilisés dans une variété de contextes. Souvent, ils se composent de plusieurs parties ou éléments, qui se répètent parfois, et ont parfois besoin de leurs propres modificateurs.
Prenez un composant de carte. Dans React, une approche commune consiste à créer des sous-composants:
< Card flush = { true } >
< CardHeader centered = { true } >
Header
</ CardHeader >
< CardSection size = "large" >
Section 1
</ CardSection >
< CardSection size = "small" >
Section 2
</ CardSection >
< CardFooter >
Footer
</ CardFooter >
</ Card >Il y a deux problèmes avec cette approche:
CardHeader pourrait être placé à l'extérieur d'une Card .CardHeader peut être placé en dessous, ou à l'intérieur d'un CardFooter .En utilisant ce joyau, le même composant peut être écrit comme ceci:
# 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 > Les éléments peuvent être considérés comme des sous-composants isolés, et ils sont définis sur le composant. Passer multiple: true en fait un élément répétant, et passer un bloc nous permet de déclarer des attributs sur nos éléments, de la même manière que nous déclarons les attributs sur les composants.
Afin de les remplir avec des données, nous passons un bloc à l'assistance des composants, qui donne le composant, ce qui nous permet de définir des attributs et des blocs de contenu sur l'élément de la même manière que nous faisons pour les composants:
<%= 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 %> Plusieurs appels à un élément répétitif, comme section dans l'exemple ci-dessus, ajouteront chaque section à un tableau.
Un autre bon cas d'utilisation est un composant de navigation:
# 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 %> Une alternative ici consiste à transmettre une structure de données au composant en tant qu'attribut, si aucun HTML ne doit être injecté lors du rendu du composant:
<%= component "navigation", items: items %> Les éléments peuvent également avoir des validations:
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
endLes éléments peuvent également être imbriqués, bien qu'il soit recommandé de continuer à nicher au minimum:
# app/components/card_component.rb %>
class CardComponent < ElementalComponents :: Component
...
element :section , multiple : true do
attribute :size
element :header
element :footer
end
endEn plus de déclarer les attributs et les éléments, il est également possible de déclarer des méthodes d'assistance. Ceci est utile si vous préférez garder la logique hors de vos modèles. Extraitons la logique du modificateur du modèle de composant de la carte:
# 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 %>Il est même possible de déclarer des aides sur les éléments:
# 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 %> Les méthodes d'assistance peuvent également utiliser la variable d'instance @view afin d'appeler des aides Rails tels que link_to ou content_tag .
Pour certains petits composants, tels que des boutons, il pourrait être logique de sauter complètement le partiel, afin d'accélérer le rendu. Cela peut être fait en remplaçant render sur le composant:
# 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 %> Les composants peuvent être imbriqués sous un espace de noms. Ceci est utile si vous voulez pratiquer des choses comme la conception atomique, le bemit ou tout autre schéma de classification des composants. Afin de créer un composant dans un espacé, coller dans un dossier et envelopper la classe dans un module:
module Objects
class MediaObject < ElementalComponents :: Component ; end
endEnsuite, appelez-le à partir d'un modèle comme tel:
<%= component "objects/media_object" %> Cette bibliothèque, ainsi que Elemental_styleGuide, ont été inspirées par les écrits de Brad Frost sur les guides de conception atomique et de style de vie, et Rizzo, le guide de style Lonely Planet. Les autres inspirations étaient:
Pour une liste de guides de style réel, consultez http://styleguides.io.