Cum se utilizează Rails Helpers: O demonstrație Bootstrap Carusel

Publicat: 2022-03-11

Una 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.

Un plan care arată un dreptunghi centrat (diapozitiv) cu trei cercuri mici în partea de jos (indicatori). Este flancat de două dreptunghiuri subțiri cu săgeți stânga și, respectiv, dreapta (controale).
Părțile unui carusel Bootstrap.

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ă.