So verwenden Sie Rails-Helfer: Eine Bootstrap-Karussell-Demonstration
Veröffentlicht: 2022-03-11Eine der am meisten missbrauchten, missverstandenen und vernachlässigten aller in Rails eingebauten Strukturen ist der View-Helfer . Helfer, die sich in Ihrem app/helpers
-Verzeichnis befinden und standardmäßig mit jedem neuen Rails-Projekt generiert werden, haben oft den schlechten Ruf, ein Abladeplatz für einmalige Methoden zu sein, die über die gesamte Ansichtsschicht der Anwendung hinweg verwendet werden. Leider fördert Rails selbst diesen Mangel an Struktur und schlechte Organisation, indem standardmäßig alle Helfer in jede Ansicht aufgenommen werden, wodurch ein verschmutzter globaler Namensraum entsteht.
Aber was wäre, wenn Ihre Helfer semantischer, besser organisiert und sogar projektübergreifend wiederverwendbar wären? Was wäre, wenn es sich nicht nur um einmalige Funktionen handeln könnte, die über die Ansicht verteilt sind, sondern um leistungsstarke Methoden, die mühelos komplexes Markup generieren und Ihre Ansichten frei von bedingter Logik und Code lassen?
Sehen wir uns an, wie dies beim Erstellen eines Bildkarussells mit dem vertrauten Twitter-Bootstrap-Framework und etwas guter altmodischer objektorientierter Programmierung funktioniert.
Wann Sie Rails-Helfer verwenden sollten
Es gibt viele verschiedene Design-Patterns, die im View-Layer von Rails verwendet werden können: Presenter, Decorators, Partials sowie Helfer, um nur einige zu nennen. Meine einfache Faustregel lautet, dass Helfer hervorragend funktionieren, wenn Sie HTML-Markup generieren möchten, das eine bestimmte Struktur, bestimmte CSS-Klassen, bedingte Logik oder die Wiederverwendung auf verschiedenen Seiten erfordert.
Das beste Beispiel für die Leistungsfähigkeit von Rails-Helfern ist der FormBuilder
mit all seinen zugehörigen Methoden zum Generieren von Eingabefeldern, Auswahl-Tags, Labels und anderen HTML-Strukturen. Diese hilfreichen Methoden generieren für Sie Markup mit allen relevanten Attributen, die richtig eingestellt sind. Wegen solcher Bequemlichkeit haben wir uns alle in erster Linie in Rails verliebt.
Die Vorteile der Verwendung gut gestalteter Helfer sind die gleichen wie bei jedem gut geschriebenen, sauberen Code: Kapselung, Reduzierung der Codewiederholung (DRY) und Ausblenden der Logik.
Anatomie eines Twitter-Bootstrap-Karussells
Twitter Bootstrap ist ein weit verbreitetes Front-End-Framework mit integrierter Unterstützung für gängige Komponenten wie Modals, Tabs und Bildkarussells. Diese Bootstrap-Komponenten sind ein großartiger Anwendungsfall für benutzerdefinierte Helfer, da das Markup stark strukturiert ist und erfordert, dass bestimmte Klassen, IDs und Datenattribute korrekt festgelegt werden, damit das JavaScript funktioniert, und das Festlegen dieser Attribute erfordert ein wenig bedingte Logik.
Ein Bootstrap 3-Karussell hat das folgende Markup:
<div class="carousel slide" data-ride="carousel"> <!-- Indicators --> <ol class="carousel-indicators"> <li data-target="#carousel-example-generic" data-slide-to="0" class="active"></li> <li data-target="#carousel-example-generic" data-slide-to="1"></li> <li data-target="#carousel-example-generic" data-slide-to="2"></li> </ol> <!-- Wrapper for slides --> <div class="carousel-inner"> <div class="item active"> <img src="..." alt="..."> </div> <div class="item"> <img src="..." alt="..."> </div> ... </div> <!-- Controls --> <a class="left carousel-control" href="#carousel-example-generic" data-slide="prev"> <span class="glyphicon glyphicon-chevron-left"></span> </a> <a class="right carousel-control" href="#carousel-example-generic" data-slide="next"> <span class="glyphicon glyphicon-chevron-right"></span> </a> </div>
Wie Sie sehen können, gibt es drei Hauptstrukturen: (1) die Anzeigen (2) die Bildfolien (3) die Foliensteuerungen.
Das Ziel ist es, eine einzige Hilfsmethode zu erstellen, die eine Sammlung von Bildern nimmt und diese gesamte Karussellkomponente rendert, um sicherzustellen, dass data, id
, href
-Attribute und CSS-Klassen alle richtig eingestellt sind.
Der Helfer
Beginnen wir mit einem grundlegenden Überblick über den Helfer:
# app/helpers/carousel_helper.rb module CarouselHelper def carousel_for(images) Carousel.new(self, images).html end class Carousel def initialize(view, images) @view, @images = view, images end def html # TO FILL IN end private attr_accessor :view, :images end end
Die Hilfsmethode carousel_for
gibt das vollständige Karussell-Markup für die angegebenen Bild-URLs zurück. Anstatt eine Reihe von einzelnen Methoden zu erstellen, um jeden Teil des Karussells zu rendern (was erfordern würde, dass wir die Bildsammlung und andere zustandsbehaftete Informationen an jede Methode weitergeben), erstellen wir eine neue, ganz normale Ruby-Klasse namens Carousel
to repräsentieren die Karusselldaten. Diese Klasse stellt eine html
-Methode bereit, die das vollständig gerenderte Markup zurückgibt. Wir initialisieren es mit der Sammlung von Bild-URLs images
und der View-Context- view
.
Beachten Sie, dass der Parameter view
eine Instanz von ActionView
ist, in die alle Rails-Hilfsprogramme eingemischt sind. Wir übergeben es an unsere Objektinstanz, um Zugriff auf die integrierten Hilfsmethoden von Rails wie link_to
, content_tag
, image_tag
und safe_join
zu erhalten, die wir verwenden werden, um das Markup innerhalb der Klasse aufzubauen. Wir fügen auch das delegate
-Makro hinzu, damit wir diese Methoden direkt aufrufen können, ohne auf view
zu verweisen:
def html content = view.safe_join([indicators, slides, controls]) view.content_tag(:div, content, class: 'carousel slide') end private attr_accessor :view, :images delegate :link_to, :content_tag, :image_tag, :safe_join, to: :view def indicators # TO FILL IN end def slides # TO FILL IN end def controls # TO FILL IN end
Wir wissen, dass ein Karussell aus drei separaten Komponenten besteht, also lassen Sie uns Methoden herausschneiden, die uns schließlich das Markup für jede Komponente liefern, und dann die html
-Methode mit einem Container- div
-Tag verbinden, wobei die erforderlichen Bootstrap-Klassen für das Karussell selbst angewendet werden.

safe_join
ist eine praktische eingebaute Methode, die eine Sammlung von Strings miteinander verkettet und html_safe
für das Ergebnis aufruft. Denken Sie daran, dass wir Zugriff auf diese Methoden über den view
-Parameter haben, den wir übergeben haben, als wir die Instanz erstellt haben.
Wir bauen zuerst die Indikatoren auf:
def indicators items = images.count.times.map { |index| indicator_tag(index) } content_tag(:ol, safe_join(items), class: 'carousel-indicators') end def indicator_tag(index) options = { class: (index.zero? ? 'active' : ''), data: { target: uid, slide_to: index } } content_tag(:li, '', options) end
Die Indikatoren sind eine einfache geordnete Liste ol
mit einem Listenelement li
für jedes Bild in der Sammlung. Der derzeit aktive Bildindikator benötigt die active
CSS-Klasse, daher stellen wir sicher, dass sie für den ersten Indikator, den wir erstellen, festgelegt ist. Dies ist ein großartiges Beispiel für Logik, die normalerweise in der Ansicht selbst enthalten sein müsste.
Beachten Sie, dass die Indikatoren auf die eindeutige id
des enthaltenen Karussellelements verweisen müssen (falls es mehr als ein Karussell auf der Seite gibt). Wir können diese id
einfach im Initialisierer generieren und im Rest der Klasse verwenden (insbesondere innerhalb der Indikatoren und der Steuerelemente). Wenn Sie dies programmgesteuert innerhalb einer Hilfsmethode tun, wird sichergestellt, dass die id
über alle Karussellelemente hinweg konsistent ist. Es kommt oft vor, dass ein kleiner Tippfehler oder das Ändern der id
an einer Stelle, aber nicht an anderen, dazu führt, dass ein Karussell kaputt geht; das wird hier nicht passieren, weil alle Elemente automatisch auf dieselbe id
verweisen.
def initialize(view, images) # ... @uid = SecureRandom.hex(6) end attr_accessor :uid
Als nächstes folgen die Bildfolien:
def slides items = images.map.with_index { |image, index| slide_tag(image, index.zero?) } content_tag(:div, safe_join(items), class: 'carousel-inner') end def slide_tag(image, is_active) options = { class: (is_active ? 'item active' : 'item'), } content_tag(:div, image_tag(image), options) end
Wir iterieren einfach über jedes der Bilder, die wir an die Carousel
-Instanz übergeben haben, und erstellen das richtige Markup: ein Bild-Tag, das in ein div
mit der item
-CSS-Klasse eingeschlossen ist, wobei wir wiederum darauf achten, die active
Klasse der ersten, die wir erstellen, hinzuzufügen.
Zu guter Letzt brauchen wir die Previous/Next-Steuerelemente:
def controls safe_join([control_tag('left'), control_tag('right')]) end def control_tag(direction) options = { class: "#{direction} carousel-control", data: { slide: direction == 'left' ? 'prev' : 'next' } } icon = content_tag(:i, nil, class: "glyphicon glyphicon-chevron-#{direction}") control = link_to(icon, "##{uid}", options) end
Wir erstellen Verknüpfungen, die die Hin- und Herbewegung des Karussells zwischen den Bildern steuern. Beachten Sie erneut die Verwendung von uid
; Sie brauchen sich keine Sorgen zu machen, dass an all den verschiedenen Stellen innerhalb der Karussellstruktur nicht die richtige ID verwendet wird, sie ist automatisch konsistent und eindeutig.
Das fertige Produkt:
Damit ist unser Karussell-Helfer komplett. Hier ist es in voller Länge:
# app/helpers/carousel_helper.rb module CarouselHelper def carousel_for(images) Carousel.new(self, images).html end class Carousel def initialize(view, images) @view, @images = view, images @uid = SecureRandom.hex(6) end def html content = safe_join([indicators, slides, controls]) content_tag(:div, content, id: uid, class: 'carousel slide') end private attr_accessor :view, :images, :uid delegate :link_to, :content_tag, :image_tag, :safe_join, to: :view def indicators items = images.count.times.map { |index| indicator_tag(index) } content_tag(:ol, safe_join(items), class: 'carousel-indicators') end def indicator_tag(index) options = { class: (index.zero? ? 'active' : ''), data: { target: uid, slide_to: index } } content_tag(:li, '', options) end def slides items = images.map.with_index { |image, index| slide_tag(image, index.zero?) } content_tag(:div, safe_join(items), class: 'carousel-inner') end def slide_tag(image, is_active) options = { class: (is_active ? 'item active' : 'item'), } content_tag(:div, image_tag(image), options) end def controls safe_join([control_tag('left'), control_tag('right')]) end def control_tag(direction) options = { class: "#{direction} carousel-control", data: { slide: direction == 'left' ? 'prev' : 'next' } } icon = content_tag(:i, '', class: "glyphicon glyphicon-chevron-#{direction}") control = link_to(icon, "##{uid}", options) end end end
Der Helfer im Einsatz:
Um es noch einmal deutlich zu machen, schauen wir uns abschließend ein kurzes Beispiel an, wie dieser Helfer unser Leben leichter machen kann. Nehmen wir an, wir bauen eine Website für Wohnungsvermietungen. Jedes Apartment
-Objekt hat eine Liste der Bild-URLs:
class Apartment def image_urls # ... end end
Mit unserem Karussell-Helfer können wir das gesamte Bootstrap-Karussell mit einem einzigen Aufruf von carousel_for
rendern, wodurch die ziemlich komplexe Logik vollständig aus der Ansicht entfernt wird:
<% apartment = Apartment.new %> # ... <%= carousel_for(apartment.image_urls) %>
Sie sind sich nicht sicher, wann Sie Rails-Ansichtshelfer verwenden sollen? Hier ist eine Vorführung.
Twittern
Fazit
Mit dieser einfachen, aber leistungsstarken Technik haben wir eine beträchtliche Menge an Markup und Logik aus der Ansichtsebene in eine Hilfsfunktion verschoben, die zum Rendern von Karussellkomponenten überall mit nur einem carousel_for(some_images)
verwendet werden kann . Dieser generische Helfer kann in all Ihren Rails-Projekten verwendet werden, wenn Sie Twitter Bootstrap verwenden. Das Wichtigste ist, dass Sie jetzt ein neues Tool in Ihrem Werkzeugkasten haben, das Sie auch für projektspezifische Komponenten verwenden können.
Wenn Sie also das nächste Mal dieselbe Art von Markup eingeben und wiederholen und bedingte Logik in Ihre Ansichten einbetten, prüfen Sie, ob eine Hilfsfunktion nur darauf wartet, geschrieben zu werden, um Ihnen das Leben zu erleichtern.