Une application Web est vite amenée à présenter des vues complexes.
Des éléments asyncrones peuvent être ajoutés à la page.
Des éléments peuvent être ajoutés ou supprimés dynamiquement.
Il faut respecter la cohérence de l'UI.
Cela peut amener à des vues complexes et difficiles à maintenir.
Même avec l'utilisation de partials et helpers, on a de la logique dans les vues.
-
inspiré par les avantages de React
-
développé puis open sourcé par GitHub
-
prototype présenté à la RailsConf 2019
L'objectif principal est de simplifier la manière dont les développeurs construisent des vues complexes pour les applications Rails.
Un ViewComponent est objet Ruby et un template.
# app/components/message_component.rb
class MessageComponent < ViewComponent::Base
def initialize(name:)
@name = name
end
end
<%# app/components/message_component.html.erb %>
<h1>Hello, <%= @name %>!</h1>
Une instance qui est passé au #render
de Rails.
<%# app/views/demo/index.html.erb %>
<%= render(MessageComponent.new(name: "World")) %>
Qui génère le HTML suivant :
<h1>Hello, World!</h1>
Principe de responsabilité unique
Garder la logique dans la vue déroge au SRP et tend à rendre le code complexe.
Ne vous répétez pas
En utilisant des composants réutilisables, on facilite la cohérence de l'UI.
-
Pour remplacer les partials qui sont réutilisées ou que l'on veut tester facilement.
-
Pour remplacer les templates qui comporte beaucoup de Ruby en ViewComponents.
Opinion construit sur la base de l'expérience des équipes de GitHub, des articles que j'ai pu lire et de l'expérience chez Kuartz.
Composants commun pour l'UI
<%= render(ButtonComponent.new) { "Default" } %>
<%= render(ButtonComponent.new(scheme: :primary)) { "Primary" } %>
<%= render(ButtonComponent.new(scheme: :danger)) { "Danger" } %>
<%= render(ButtonComponent.new(scheme: :invisible)) { "Invisible" } %>
Pour transformer un objet métier (souvent un modèle ActiveRecord) en un ensemble de composants génériques.
<%= render(User::ContributorComponent.new(user: @user)) %>
"Good frameworks are extracted, not invented." DHH
-
Composant spécifique à un cas d'utilisation implémenté dans l'application.
-
Composant adapté pour une utilisation générale dans plusieurs endroits de l'application.
-
Composant extrait dans une Gem et documenté dans Lookbook.
-
La plupart des méthodes d'instance peuvent être privées
-
Préférer les ViewComponents aux partials
-
Préférer les ViewComponents aux helpers générant du
-
Éviter le Global state
-
Éviter les requêtes à la base de données
-
Passer un objet plutôt que 3+ attributs d'objet
class MyComponent < ViewComponent::Base
# bad
def initialize(repository_name:, repository_owner:, repository_created_at:)
#...
end
# good
def initialize(repository:)
#...
end
end