Comment utiliser les assistants de rails : une démonstration de carrousel Bootstrap

Publié: 2022-03-11

L'une des structures intégrées Rails les plus mal utilisées, mal comprises et négligées est le view helper . Situés dans votre répertoire app/helpers et générés par défaut avec chaque nouveau projet Rails, les helpers ont souvent la mauvaise réputation d'être un dépotoir pour les méthodes ponctuelles utilisées sur l'ensemble de la couche de vue de l'application. Malheureusement, Rails lui-même encourage ce manque de structure et cette mauvaise organisation en incluant tous les assistants dans chaque vue par défaut, créant un espace de noms global pollué.

Et si vos assistants pouvaient être plus sémantiques, mieux organisés et même réutilisables d'un projet à l'autre ? Et s'il pouvait s'agir de plus que de simples fonctions ponctuelles disséminées dans la vue, mais de méthodes puissantes générant facilement un balisage complexe, laissant vos vues libres de logique et de code conditionnels ?

Voyons comment procéder lors de la création d'un carrousel d'images, avec le framework Twitter Bootstrap familier et une bonne programmation orientée objet à l'ancienne.

Quand utiliser les assistants Rails

Il existe de nombreux modèles de conception différents qui peuvent être utilisés dans la couche de vue de Rails : les présentateurs, les décorateurs, les partiels, ainsi que les assistants, pour n'en nommer que quelques-uns. Ma règle d'or simple est que les assistants fonctionnent très bien lorsque vous souhaitez générer un balisage HTML qui nécessite une certaine structure, des classes CSS spécifiques, une logique conditionnelle ou une réutilisation sur différentes pages.

Le meilleur exemple de la puissance des assistants Rails est démontré par le FormBuilder avec toutes ses méthodes associées pour générer des champs de saisie, sélectionner des balises, des étiquettes et d'autres structures HTML. Ces méthodes utiles génèrent un balisage pour vous avec tous les attributs pertinents définis correctement. Une telle commodité est la raison pour laquelle nous sommes tous tombés amoureux de Rails en premier lieu.

Les avantages de l'utilisation d'assistants bien conçus sont les mêmes que pour tout code propre et bien écrit : encapsulation, réduction de la répétition du code (DRY) et maintien de la logique hors de vue.

Anatomie d'un carrousel d'amorçage Twitter

Twitter Bootstrap est un framework frontal largement utilisé qui est livré avec un support intégré pour les composants courants tels que les modaux, les onglets et les carrousels d'images. Ces composants Bootstrap sont un excellent cas d'utilisation pour les assistants personnalisés car le balisage est hautement structuré, nécessite que certaines classes, ID et attributs de données soient correctement définis pour que JavaScript fonctionne, et la définition de ces attributs nécessite un peu de logique conditionnelle.

Un carrousel Bootstrap 3 a le balisage suivant :

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

Comme vous pouvez le voir, il existe trois structures principales : (1) les indicateurs (2) les diapositives d'image (3) les commandes de diapositives.

Un plan montrant un rectangle de rapport d'écran large centré (diapositive) avec trois petits cercles près de son bas (indicateurs). Il est flanqué de deux rectangles minces avec des flèches gauche et droite, respectivement (contrôles).
Les parties d'un carrousel Bootstrap.

L'objectif est de pouvoir créer une méthode d'assistance unique qui prend une collection d'images et restitue l'intégralité de ce composant carrousel, en s'assurant que les données, id , href attributs et classes CSS sont tous correctement définis.

L'aide

Commençons par un aperçu de base de l'assistant :

 # 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

La méthode d'assistance carousel_for renverra le balisage complet du carrousel pour les URL d'image données. Plutôt que de créer une suite de méthodes individuelles pour rendre chaque partie du carrousel (ce qui nous obligerait à transmettre la collection d'images et d'autres informations avec état à chaque méthode), nous allons créer une nouvelle classe Ruby ordinaire appelée Carousel to représentent les données du carrousel. Cette classe exposera une méthode html qui renvoie le balisage entièrement rendu. Nous l'initialisons avec la collection d'URL d' images et la vue contextuelle de la view .

Notez que le paramètre view est une instance de ActionView , dans laquelle tous les assistants Rails sont mélangés. Nous le transmettons à notre instance d'objet afin d'accéder aux méthodes d'assistance intégrées de Rails telles que link_to , content_tag , image_tag et safe_join , que nous utiliserons pour créer le balisage dans la classe. Nous ajouterons également la macro delegate , afin que nous puissions appeler ces méthodes directement, sans faire référence à 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

Nous savons qu'un carrousel est composé de trois composants distincts, alors supprimons les méthodes qui nous donneront éventuellement le balisage pour chacun, puis la méthode html les joindra dans une balise div de conteneur, en appliquant les classes Bootstrap nécessaires pour le carrousel lui-même.

safe_join est une méthode intégrée pratique qui concatène une collection de chaînes et appelle html_safe sur le résultat. N'oubliez pas que nous avons accès à ces méthodes via le paramètre view , que nous avons transmis lors de la création de l'instance.

Nous allons d'abord créer les indicateurs :

 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

Les indicateurs sont une simple liste ordonnée ol qui a un élément li d'élément de liste pour chaque image de la collection. L'indicateur d'image actuellement actif a besoin de la classe CSS active , nous allons donc nous assurer qu'il est défini pour le premier indicateur que nous créons. C'est un excellent exemple de logique qui devrait normalement être dans la vue elle-même.

Notez que les indicateurs doivent faire référence à l' id unique de l'élément de carrousel contenant (au cas où il y aurait plus d'un carrousel sur la page). Nous pouvons facilement générer cet id dans l'initialiseur et l'utiliser dans le reste de la classe (en particulier dans les indicateurs et les contrôles). Faire cela par programme à l'intérieur d'une méthode d'assistance garantit que l' id sera cohérent entre les éléments du carrousel. Il arrive souvent qu'une petite faute de frappe ou la modification de l' id à un endroit mais pas aux autres provoque la rupture d'un carrousel ; cela n'arrivera pas ici car tous les éléments référencent automatiquement le même id .

 def initialize(view, images) # ... @uid = SecureRandom.hex(6) end attr_accessor :uid

Viennent ensuite les diapositives d'images :

 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

Nous parcourons simplement chacune des images que nous avons transmises à l'instance Carousel et créons le balisage approprié : une balise d'image enveloppée dans un div avec la classe CSS de l' item , en veillant à nouveau à ajouter la classe active à la première que nous créons.

Enfin, nous avons besoin des contrôles Précédent/Suivant :

 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

Nous créons des liens qui contrôlent le mouvement de va-et-vient du carrousel entre les images. Notez à nouveau l'utilisation de uid ; pas besoin de s'inquiéter de ne pas utiliser le bon identifiant à tous les différents endroits de la structure du carrousel, il est automatiquement cohérent et unique.

Le produit fini :

Avec cela, notre assistant de carrousel est complet. Le voici dans son intégralité :

 # 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

L'assistant en action :

Enfin, pour enfoncer le clou, regardons un exemple rapide de la façon dont cette aide peut nous faciliter la vie. Disons que nous construisons un site Web pour les annonces de location d'appartements. Chaque objet Apartment possède une liste d'URL d'images :

 class Apartment def image_urls # ... end end

Avec notre assistant de carrousel, nous pouvons restituer l'intégralité du carrousel Bootstrap avec un seul appel à carousel_for , supprimant complètement la logique assez complexe de la vue :

 <% apartment = Apartment.new %> # ... <%= carousel_for(apartment.image_urls) %> 

Vous ne savez pas quand utiliser les assistants de vue Rails ? Voici une démonstration.

Tweeter

Conclusion

En utilisant cette technique simple mais puissante, nous avons déplacé ce qui serait une quantité importante de balisage et de logique hors de la couche de vue et dans une fonction d'assistance qui peut être utilisée pour rendre les composants du carrousel n'importe où avec juste un appel carousel_for(some_images) . Cette aide générique peut être utilisée dans tous vos projets Rails chaque fois que vous utilisez Twitter Bootstrap. Plus important encore, vous disposez désormais d'un nouvel outil dans votre boîte à outils que vous pouvez également utiliser pour les composants spécifiques à un projet.

Ainsi, la prochaine fois que vous vous retrouverez à taper et à retaper le même type de balisage et à intégrer une logique conditionnelle dans vos vues, voyez si une fonction d'assistance n'attend que d'être écrite pour vous faciliter la vie.