Cum se utilizează Rails Helpers: O demonstrație Bootstrap Carusel
Publicat: 2022-03-11Una dintre cele mai greșit utilizate, neînțelese și neglijate dintre toate structurile încorporate Rails este ajutorul de vizualizare . app/helpers
și generați în mod implicit cu fiecare proiect Rails nou, ajutoarele au adesea o reputație proastă pentru că sunt un teren de gunoi pentru metodele unice utilizate în întregul strat de vizualizare al aplicației. Din păcate, Rails însuși încurajează această lipsă de structură și organizare slabă prin includerea implicită a tuturor ajutoarelor în fiecare vizualizare, creând un spațiu de nume global poluat.
Dar dacă ajutoarele dvs. ar putea fi mai semantice, mai bine organizate și chiar reutilizabile în cadrul proiectelor? Ce se întâmplă dacă ar putea fi mai mult decât funcții unice presărate în întreaga vizualizare, ci metode puternice care au generat cu ușurință un marcaj complex, lăsând vizualizările libere de logica și codul condiționat?
Să vedem cum să facem acest lucru atunci când construim un carusel de imagini, cu cadrul familiar Twitter Bootstrap și o programare bună, de modă veche, orientată pe obiecte.
Când să folosiți ajutoarele Rails
Există multe modele de design diferite care pot fi utilizate în stratul de vizualizare al Rails: prezentatori, decoratori, parțiali, precum și ajutoare, pentru a numi doar câteva. Regula mea simplă este că ajutoarele funcționează excelent atunci când doriți să generați un marcaj HTML care necesită o anumită structură, clase CSS specifice, logică condiționată sau reutilizare pe diferite pagini.
Cel mai bun exemplu al puterii ajutoarelor Rails este demonstrat de FormBuilder
cu toate metodele sale asociate pentru generarea de câmpuri de intrare, etichete de selectare, etichete și alte structuri HTML. Aceste metode utile generează markup pentru dvs. cu toate atributele relevante setate corect. Comoditate ca acesta este motivul pentru care ne-am îndrăgostit cu toții de Rails în primul rând.
Beneficiile utilizării unor ajutoare bine concepute sunt aceleași ca orice cod bine scris și curat: încapsulare, reducerea repetiției codului (DRY) și păstrarea logicii în afara vederii.
Anatomia unui carusel Twitter Bootstrap
Twitter Bootstrap este un cadru frontal utilizat pe scară largă, care vine cu suport încorporat pentru componente comune, cum ar fi modale, file și carusele de imagini. Aceste componente Bootstrap sunt un caz de utilizare grozav pentru asistenții personalizați, deoarece marcajul este foarte structurat, necesită setarea corectă a anumitor clase, ID-uri și atribute de date pentru ca JavaScript să funcționeze, iar setarea acelor atribute necesită un pic de logică condiționată.
Un carusel Bootstrap 3 are următorul marcaj:
<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>
După cum puteți vedea, există trei structuri principale: (1) indicatorii (2) diapozitivele de imagine (3) comenzile de diapozitive.
Scopul este de a putea construi o singură metodă de ajutor care preia o colecție de imagini și redă întreaga componentă a caruselului, asigurându-se că datele, id
-ul, atributele href
și clasele CSS sunt toate setate corect.
Ajutorul
Să începem cu o schiță de bază a asistentului:
# 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
Metoda de ajutor carousel_for
va returna marcarea completă a caruselului pentru adresele URL ale imaginilor date. În loc să construim o suită de metode individuale pentru a reda fiecare porțiune a caruselului (ceea ce ne-ar cere să transmitem colecția de imagini și alte informații cu stare la fiecare metodă), vom crea o nouă clasă Ruby simplă, veche, numită Carousel
pentru reprezintă datele caruselului. Această clasă va expune o metodă html
care returnează marcajul redat complet. O inițializam cu colecția de images
URL-uri de imagine și vizualizarea contextului view
.
Rețineți că parametrul de view
este o instanță a ActionView
, în care sunt amestecați toți ajutoarele Rails. Îl transmitem instanței obiectului nostru pentru a obține acces la metodele de ajutor încorporate ale lui Rails, cum ar fi link_to
, content_tag
, image_tag
și safe_join
, pe care le vom folosi pentru a construi marcajul în cadrul clasei. Vom adăuga, de asemenea, macrocomanda delegate
, astfel încât să putem apela aceste metode direct, fără a ne referi la view
:
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
Știm că un carusel este compus din trei componente separate, așa că haideți să excludem metodele care ne vor oferi în cele din urmă marcajul pentru fiecare, apoi să punem metoda html
să le unească într-o etichetă div
container, aplicând clasele Bootstrap necesare pentru carusel în sine.

safe_join
este o metodă încorporată la îndemână care concatenează o colecție de șiruri împreună și apelează html_safe
pe rezultat. Amintiți-vă, avem acces la acele metode prin intermediul parametrului de view
, pe care l-am transmis când am creat instanța.
Vom construi mai întâi indicatorii:
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
Indicatorii sunt o listă simplă ol
care are un element de li
pentru fiecare imagine din colecție. Indicatorul de imagine activ în prezent are nevoie de clasa CSS active
, așa că ne vom asigura că este setat pentru primul indicator pe care îl creăm. Acesta este un exemplu grozav de logică care ar trebui să fie în mod normal în viziunea în sine.
Observați că indicatorii trebuie să facă referire la id
-ul unic al elementului carusel care îl conține (în cazul în care există mai mult de un carusel pe pagină). Putem genera cu ușurință acest id
în inițializator și îl putem folosi în restul clasei (în special în cadrul indicatorilor și controalelor). Făcând acest lucru programatic în cadrul unei metode de ajutor, se asigură că id
-ul va fi consecvent între elementele caruselului. Sunt de multe ori când o mică greșeală de tipar sau schimbarea id
-ului într-un loc, dar nu în celelalte, va face ca un carusel să se spargă; asta nu se va întâmpla aici, deoarece toate elementele fac referire automat la același id
.
def initialize(view, images) # ... @uid = SecureRandom.hex(6) end attr_accessor :uid
Urmează diapozitivele cu imagini:
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
Pur și simplu repetăm fiecare dintre imaginile pe care le-am transmis instanței Carousel
și creăm marcajul corespunzător: o etichetă de imagine înfășurată într-un div
cu item
clasă CSS, asigurându-ne din nou că adăugăm clasa active
la prima pe care o creăm.
În cele din urmă, avem nevoie de controalele Previous/Next:
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
Creăm legături care controlează mișcarea caruselului înainte și înapoi între imagini. Observați din nou utilizarea uid
; nu trebuie să vă faceți griji că nu folosiți ID-ul potrivit în toate locurile diferite din structura caruselului, este automat consecvent și unic.
Produsul finit:
Cu asta, asistentul nostru de carusel este complet. Iată-l în întregime:
# 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
Ajutorul în acțiune:
În cele din urmă, pentru a înțelege ideea, să ne uităm la un exemplu rapid despre modul în care acest ajutor ne poate ușura viața. Să presupunem că construim un site web pentru anunțurile de închiriere de apartamente. Fiecare obiect Apartment
are o listă cu adresele URL ale imaginii:
class Apartment def image_urls # ... end end
Cu ajutorul nostru carusel, putem reda întregul carusel Bootstrap cu un singur apel la carousel_for
, eliminând complet logica destul de complexă din vizualizare:
<% apartment = Apartment.new %> # ... <%= carousel_for(apartment.image_urls) %>
Nu sunteți sigur când să utilizați ajutoarele pentru vizualizarea șinelor? Iată o demonstrație.
Tweet
Concluzie
Folosind această tehnică simplă, dar puternică, am mutat ceea ce ar fi o cantitate semnificativă de markup și logică din stratul de vizualizare și într-o funcție de ajutor care poate fi folosită pentru a reda componentele carusel oriunde cu doar un apel carousel_for(some_images)
. Acest ajutor generic poate fi folosit în toate proiectele dvs. Rails ori de câte ori utilizați Twitter Bootstrap. Cel mai important, acum aveți un nou instrument în setul dvs. de instrumente pe care îl puteți utiliza și pentru componente specifice proiectului.
Așadar, data viitoare când te trezești să tastezi și să retasezi același tip de marcare și să încorporezi logica condiționată în vederile tale, vezi dacă o funcție de ajutor așteaptă să fie scrisă pentru a-ți face viața mai ușoară.