Jak korzystać z pomocników Rails: pokaz karuzeli Bootstrap
Opublikowany: 2022-03-11Jedną z najczęściej niewłaściwie używanych, niezrozumianych i zaniedbywanych ze wszystkich wbudowanych struktur Rails jest pomocnik widoku . Znajdujący się w twoim katalogu app/helpers i generowany domyślnie z każdym nowym projektem Railsowym, helpery często mają złą reputację jako miejsce zrzucania jednorazowych metod używanych w całej warstwie widoku aplikacji. Niestety, same Railsy zachęcają do tego braku struktury i słabej organizacji, domyślnie włączając wszystkich pomocników do każdego widoku, tworząc zanieczyszczoną globalną przestrzeń nazw.
Ale co, jeśli twoi pomocnicy mogliby być bardziej semantyczni, lepiej zorganizowani, a nawet wielokrotnego użytku w różnych projektach? Co by było, gdyby mogły być czymś więcej niż tylko jednorazowymi funkcjami rozsianymi po całym widoku, ale potężnymi metodami, które z łatwością generowały złożone znaczniki, pozostawiając widoki wolne od warunkowej logiki i kodu?
Zobaczmy, jak to zrobić, budując karuzelę obrazów, za pomocą znanego frameworka Twitter Bootstrap i dobrego, staromodnego programowania obiektowego.
Kiedy używać pomocników Rails
Istnieje wiele różnych wzorców projektowych, których można użyć w warstwie widoku Rails: prezentery, dekoratory, podszablony, a także pomocniki, żeby wymienić tylko kilka. Moja prosta zasada jest taka, że pomocnicy świetnie sprawdzają się, gdy chcesz wygenerować znaczniki HTML, które wymagają określonej struktury, określonych klas CSS, logiki warunkowej lub ponownego użycia na różnych stronach.
Najlepszym przykładem potęgi helperów Rails jest FormBuilder ze wszystkimi powiązanymi z nim metodami generowania pól wejściowych, znaczników wyboru, etykiet i innych struktur HTML. Te pomocne metody generują dla Ciebie znaczniki ze wszystkimi odpowiednimi atrybutami ustawionymi prawidłowo. Taka wygoda jest powodem, dla którego wszyscy zakochaliśmy się w Railsach.
Korzyści z używania dobrze przygotowanych pomocników są takie same, jak w przypadku każdego dobrze napisanego, czystego kodu: enkapsulacja, redukcja powtórzeń kodu (DRY) i utrzymywanie logiki poza widokiem.
Anatomia karuzeli Bootstrap na Twitterze
Twitter Bootstrap to szeroko stosowana platforma front-endowa, która ma wbudowaną obsługę popularnych komponentów, takich jak modalne, zakładki i karuzele obrazów. Te komponenty Bootstrap są świetnym przypadkiem użycia dla niestandardowych pomocników, ponieważ znaczniki są wysoce ustrukturyzowane, wymagają prawidłowego ustawienia pewnych klas, identyfikatorów i atrybutów danych, aby JavaScript działał, a ustawienie tych atrybutów wymaga trochę logiki warunkowej.
Karuzela Bootstrap 3 ma następujące znaczniki:
<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>Jak widać, istnieją trzy główne struktury: (1) wskaźniki (2) przesuwanie obrazu (3) elementy sterujące przesuwaniem.
Celem jest stworzenie pojedynczej metody pomocniczej, która pobiera kolekcję obrazów i renderuje cały komponent karuzeli, zapewniając, że data, id , atrybuty href i klasy CSS są ustawione poprawnie.
Pomocnik
Zacznijmy od podstawowego zarysu pomocnika:
# 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 pomocnicza carousel_for zwróci pełne znaczniki karuzeli dla podanych adresów URL obrazów. Zamiast tworzyć zestaw indywidualnych metod do renderowania każdej części karuzeli (co wymagałoby od nas przekazania kolekcji obrazów i innych informacji stanowych do każdej metody), utworzymy nową, zwykłą, starą klasę Ruby o nazwie Carousel do reprezentują dane karuzeli. Ta klasa pokaże metodę html , która zwraca w pełni wyrenderowany znacznik. Inicjujemy go kolekcją obrazów adresów URL images oraz widokiem kontekstowym view .
Zauważ, że parametr view jest instancją ActionView , w której są mieszane wszystkie helpery Rails. Przekazujemy go do naszej instancji obiektu, aby uzyskać dostęp do wbudowanych w Rails metod pomocniczych, takich jak link_to , content_tag , image_tag i safe_join , których będziemy używać do budowania znaczników w klasie. Dodamy również makro delegate , dzięki czemu będziemy mogli wywoływać te metody bezpośrednio, bez odwoływania się do 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 Wiemy, że karuzela składa się z trzech oddzielnych komponentów, więc wytnijmy metody, które ostatecznie dadzą nam znaczniki dla każdego z nich, a następnie niech metoda html połączy je w znacznik div kontenera, stosując niezbędne klasy Bootstrap dla samej karuzeli.

safe_join to poręczna wbudowana metoda, która łączy ze sobą kolekcję ciągów i wywołuje w wyniku html_safe . Pamiętaj, że mamy dostęp do tych metod poprzez parametr view , który przekazaliśmy podczas tworzenia instancji.
Najpierw zbudujemy wskaźniki:
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 Wskaźniki to prosta uporządkowana lista ol , która zawiera element listy li dla każdego obrazu w kolekcji. Aktualnie aktywny wskaźnik obrazka potrzebuje active klasy CSS, więc upewnimy się, że jest ona ustawiona dla pierwszego stworzonego przez nas wskaźnika. To świetny przykład logiki, która normalnie musiałaby znajdować się w samym widoku.
Zwróć uwagę, że wskaźniki muszą odwoływać się do unikalnego id elementu karuzeli zawierającego (w przypadku, gdy na stronie jest więcej niż jedna karuzela). Możemy łatwo wygenerować ten id w inicjatorze i używać go w pozostałej części klasy (w szczególności we wskaźnikach i kontrolkach). Wykonanie tego programowo w metodzie pomocniczej zapewnia, że id będzie spójny we wszystkich elementach karuzeli. Wiele razy mała literówka lub zmiana id w jednym miejscu, ale nie w innych, spowoduje pęknięcie karuzeli; tutaj tak się nie stanie, ponieważ wszystkie elementy automatycznie odwołują się do tego samego id .
def initialize(view, images) # ... @uid = SecureRandom.hex(6) end attr_accessor :uidDalej są slajdy obrazu:
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 Po prostu iterujemy po każdym z obrazów, które przekazaliśmy do instancji Carousel i tworzymy odpowiednie znaczniki: tag obrazu opakowany w div z klasą CSS item , ponownie upewniając się, że dodajemy active klasę do pierwszej, którą tworzymy.
Na koniec potrzebujemy kontrolek Poprzedni/Następny:
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 Tworzymy linki, które kontrolują ruch karuzeli tam iz powrotem między obrazami. Zwróć uwagę na użycie uid ponownie; nie musisz się martwić, że nie użyjesz właściwego identyfikatora we wszystkich różnych miejscach w strukturze karuzeli, jest on automatycznie spójny i niepowtarzalny.
Skończony produkt:
Dzięki temu nasz pomocnik karuzeli jest kompletny. Oto w całości:
# 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 endPomocnik w akcji:
Na koniec, aby dotrzeć do celu, spójrzmy na szybki przykład tego, jak ten pomocnik może ułatwić nam życie. Załóżmy, że budujemy stronę internetową z ofertami wynajmu mieszkań. Każdy obiekt Apartment posiada listę adresów URL obrazów:
class Apartment def image_urls # ... end end Dzięki naszemu pomocnikowi karuzeli możemy renderować całą karuzelę Bootstrap za pomocą jednego wywołania carousel_for , całkowicie usuwając z widoku dość złożoną logikę:
<% apartment = Apartment.new %> # ... <%= carousel_for(apartment.image_urls) %> Nie wiesz, kiedy używać helperów widoku Railsów? Oto demonstracja.
Ćwierkać
Wniosek
Korzystając z tej prostej, ale potężnej techniki, przenieśliśmy znaczną ilość znaczników i logiki z warstwy widoku do funkcji pomocniczej, której można używać do renderowania komponentów karuzeli w dowolnym miejscu za pomocą wywołania carousel_for(some_images) . Ten ogólny helper może być używany we wszystkich projektach Rails, gdy używasz Twitter Bootstrap. Co najważniejsze, masz teraz w swoim zestawie narzędzi nowe narzędzie, którego możesz używać również do komponentów specyficznych dla projektu.
Tak więc następnym razem, gdy będziesz pisać i ponownie wpisywać ten sam rodzaj znaczników i osadzać logikę warunkową w swoich widokach, sprawdź, czy funkcja pomocnicza tylko czeka na napisanie, aby ułatwić Ci życie.
