Einfache Ansichtskomponenten für Rails 5.1+, die so konzipiert sind, dass sie mit elemental_styleGuide gut passt. Die beiden zusammen sind von den Werken von Brad Frost und von den Gedanken hinter Lonely Planet's Style Guide Rizzo inspiriert.
Fügen Sie diese Zeile der GemFile Ihrer Anwendung hinzu:
gem "elemental_components"Und dann ausführen:
$ bundleDie hier angegebenen Beispiele verwenden die BEM -Namenskonventionen.
Komponenten live in app/components . Generieren Sie eine Komponente durch Ausführung:
$ bin/rails g elemental_components:component alertDadurch werden die folgenden Dateien erstellt:
app/
components/
alert/
_alert.html.erb
alert.css
alert.js
alert_component.rb
Der Generator nimmt auch --skip-css und --skip-js Optionen ein.
Fügen wir etwas Markup und CSS hinzu:
<% # 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;
} Diese Komponente kann jetzt mit dem component gerendert werden:
<%= component "alert" %> Um Vermögenswerte wie CSS zu verlangen, erfordern sie entweder manuell in der Manifest, z. B. application.css :
/*
*= require alert/alert
*/ Oder erfordern components , die wiederum die Vermögenswerte für alle Komponenten erfordern:
/*
*= require elemental_components
*/Es gibt zwei Möglichkeiten, Daten an Komponenten weiterzugeben: Attribute und Inhaltsblöcke. Attribute sind nützlich für Daten wie IDs, Modifikatoren und Datenstrukturen (Modelle usw.). Inhaltsblöcke sind nützlich, wenn Sie HTML -Inhalte in Komponenten injizieren müssen.
Lassen Sie uns einige Attribute für die gerade erstellte Komponente definieren:
# 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" %> Um einen Text- oder HTML -Inhalt in unsere Komponente injizieren, können wir die Komponenten -Konzent -Methode in unserer Vorlage drucken und einen Inhaltsblock an den Komponentenhelfer weitergeben:
<% # 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 %>Ein weiterer guter Anwendungsfall für Attribute ist, wenn Sie eine von einem Modell unterstützte Komponente haben:
# 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 %> Attribute können Standardwerte haben:
# app/components/alert_component.rb %>
class AlertComponent < ElementalComponents :: Component
attribute :message
attribute :context , default : "primary"
endEs ist einfach, ein Attribut mit zusätzlicher Logik zu überschreiben:
# app/components/alert_component.rb %>
class AlertComponent < ElementalComponents :: Component
attribute :message
attribute :context , default : "primary"
def message
@message . upcase if context == "danger"
end
end Um sicherzustellen, dass Ihre Komponenten ordnungsgemäß initialisiert werden, können Sie ActiveModel::Validations in Ihren Elementen oder Komponenten verwenden:
# app/components/alert_component.rb %>
class AlertComponent < ElementalComponents :: Component
attribute :label
validates :label , presence : true
end Ihre Validierungen werden während der Initialisierung der Komponenten ausgeführt und erhöhen ein ActiveModel::ValidationError , wenn eine Validierung fehlschlägt.
Attribute und Blöcke eignen sich hervorragend für einfache Komponenten oder Komponenten, die durch eine Datenstruktur wie ein Modell unterstützt werden. Andere Komponenten sind generischer Natur und können in einer Vielzahl von Kontexten verwendet werden. Oft bestehen sie aus mehreren Teilen oder Elementen, die sich manchmal wiederholen und manchmal ihre eigenen Modifikatoren brauchen.
Nehmen Sie eine Kartenkomponente. Bei React besteht ein gemeinsamer Ansatz darin, Unterkomponenten zu erstellen:
< Card flush = { true } >
< CardHeader centered = { true } >
Header
</ CardHeader >
< CardSection size = "large" >
Section 1
</ CardSection >
< CardSection size = "small" >
Section 2
</ CardSection >
< CardFooter >
Footer
</ CardFooter >
</ Card >Es gibt zwei Probleme mit diesem Ansatz:
CardHeader außerhalb einer Card platziert werden könnte.CardHeader kann unten oder in einem CardFooter platziert werden.Mit diesem Juwel kann dieselbe Komponente so geschrieben werden:
# 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 > Elemente können als isolierte Unterkomponenten betrachtet werden und sind auf der Komponente definiert. multiple: true macht es zu einem wiederholenden Element, und das Übergeben eines Blocks lässt uns Attribute für unsere Elemente deklarieren, so wie wir Attribute für Komponenten deklarieren.
Um sie mit Daten zu bevölkern, übergeben wir einen Block an den Komponentenhelfer, der die Komponente ergibt, wodurch wir Attribute und Inhaltsblöcke im Element auf die gleiche Weise festlegen können, wie wir es für Komponenten tun:
<%= 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 %> Mehrere Aufrufe eines sich wiederholenden Elements, z. section im obigen Beispiel, fügen jeden Abschnitt an ein Array hinzu.
Ein weiterer guter Anwendungsfall ist eine Navigationskomponente:
# 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 %> Eine Alternative besteht hier darin, eine Datenstruktur als Attribut an die Komponente zu übergeben, wenn bei der Rendern der Komponente kein HTML injiziert werden muss:
<%= component "navigation", items: items %> Elemente können auch Validierungen haben:
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
endElemente können auch verschachtelt werden, obwohl es empfohlen wird, zu einem Minimum zu nisten:
# app/components/card_component.rb %>
class CardComponent < ElementalComponents :: Component
...
element :section , multiple : true do
attribute :size
element :header
element :footer
end
endZusätzlich zur Deklarierung von Attributen und Elementen ist es auch möglich, Helfermethoden zu deklarieren. Dies ist nützlich, wenn Sie es vorziehen, die Logik aus Ihren Vorlagen fernzuhalten. Lassen Sie uns die Modifikator -Logik aus der Kartenkomponentenvorlage extrahieren:
# 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 %>Es ist sogar möglich, Helfer für Elemente zu erklären:
# 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 %> Helfermethoden können auch die @view -Instanzvariable verwenden, um Rails -Helfer wie link_to oder content_tag aufzurufen.
Für einige kleine Komponenten, wie z. B. Schaltflächen, könnte es sinnvoll sein, die Teilnahme insgesamt zu überspringen, um das Rendering zu beschleunigen. Dies kann durch das Überschreiben von render in der Komponente erfolgen:
# 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 %> Komponenten können unter einem Namespace verschachtelt werden. Dies ist nützlich, wenn Sie Dinge wie Atomdesign, BEMIT oder ein anderes Komponentenklassifizierungsschema üben möchten. Um eine namespaierte Komponente zu erstellen, stecken Sie sie in einen Ordner und wickeln Sie die Klasse in ein Modul ein:
module Objects
class MediaObject < ElementalComponents :: Component ; end
endNennen Sie es dann aus einer Vorlage wie so:
<%= component "objects/media_object" %> Diese Bibliothek wurde zusammen mit Elemental_StyleGuide von den Schriften von Brad Frost in Bezug auf Atomic Design und Living Style Guides und Rizzo, dem Lonely Planet Style Guide, inspiriert. Andere Inspirationen waren:
Eine Liste von Leitfäden im realen Weltstil finden Sie unter http://styleguides.io.