πŸ“œ ⬆️ ⬇️

Reusable UI components in Ruby on Rails applications

In this article, I would like to talk about an alternative way to organize a frontend for applications on Ruby on Rails. I mainly work on the backend, but from time to time there are tasks on the front end, and what one often sees there does not inspire any optimism for further work.


Each developer at their own discretion organizes the file structure for storing CSS and JavaScript code, Rails has some conventions for HTML views. Perhaps someone had to deal with applications in which CSS and JavaScript are limited to almost the only application files, then the contents of these files can be even not β€œfootchains”, but whole β€œsheets” of code. With views, everything is also not simple, especially with partials , when they are used to create separate UI components: modal windows, menus, basements, etc. Sometimes it is necessary to trick a lot to use such UI components in different representations with different parameters and / or logic.


The result is that CSS, JavaScript, HTML views and UI components (based on partials ) are in different directories and each technology has its own file structure. With this approach, the front-end in medium and large applications is difficult to maintain, all of these technologies produce a very explosive mixture that can simply jerk at any inopportune moment.


Ideally, I would like the front end to consist of separate UI components and have a clear file structure. To solve such problems, the BEM methodology has long existed, so it was used as a basis for creating the Bemer gem .


Bemer allows you to:



More information can be found here .


The file structure for the UI component is different from the one used in the BEM methodology . Each UI component has its own directory, but the technology files are called index (index files):


Sample file structure when using Sprockets .


 app/ β”œβ”€β”€ assets/ | β”œβ”€β”€ javascripts/ | | └── application.coffee | └── stylesheets/ | └── application.scss β”œβ”€β”€ bemer_components/ | β”œβ”€β”€ modal/ | | β”œβ”€β”€ index.html.slim | | β”œβ”€β”€ index.bemhtml.slim | | β”œβ”€β”€ index.coffee | | └── index.scss | └── ... └── ... 

Example file structure when using Webpacker .


 app/ β”œβ”€β”€ javascript/ | β”œβ”€β”€ bemer_components/ | | β”œβ”€β”€ modal/ | | | β”œβ”€β”€ index.html.slim | | | β”œβ”€β”€ index.bemhtml.slim | | | β”œβ”€β”€ index.coffee | | | └── index.scss | | └── ... | β”œβ”€β”€ packs/ | | β”œβ”€β”€ application.js | | └── ... | └── ... └── ... 

With Bemer-a, you can create different types of UI components . In this example, we will consider the way in which BEMHTML templates can be used. The basis is the Modal component of Bootstrap , with the following HTML structure:


 <div class="modal fade" tabindex="-1" role="dialog"> <div class="modal-dialog" role="document"> <div class="modal-content"> <div class="modal-header"> <button type="button" class="close" data-dismiss="modal"> <span aria-hidden="true">&times;</span> </button> <h4 class="modal-title">Modal title</h4> </div> <div class="modal-body"> <p>Modal body</p> </div> <div class="modal-footer"> <button type="button" class="btn btn-default" data-dismiss="modal">Close</button> <button type="button" class="btn btn-primary">Save changes</button> </div> </div> </div> </div> 

At the beginning, you need to create a structure (tree) of a component using the define_component helper (only for this kind of component UI can BEMHTML templates be used). Possible variant of the structure (tree) of the Modal component:


 /   app/bemer_components/modal/index.html.slim = define_component bem_cascade: false, tag: :div do |component| = component.block :modal, cls: 'modal fade', role: :dialog, tabindex: -1 do |modal| = modal.elem :dialog, role: :document, cls: 'modal-dialog' = modal.elem :content, cls: 'modal-content' = modal.elem :header, cls: 'modal-header' = component.block :close, type: :button, tag: :button, cls: 'close', 'data-dismiss': :modal span aria-hidden='true' &times; = modal.elem :title, tag: :h4, cls: 'modal-title' = modal.elem :body, cls: 'modal-body' = modal.elem :footer, cls: 'modal-footer' 

The structure (tree) of a component consists of blocks and elements (nodes), they act as tags, using which, you can change the component using BEMHTML templates, and also get css classes from the BEM methodology. In this case, the generation of data from the BEM methodology is not needed, so you can disable it using the bem_cascade: false parameter bem_cascade: false .


Now, in any view, you can call the Modal component and apply the necessary BEMHTML templates to it:


 /      button.btn.btn-primary.btn-lg data-target="#my-modal" data-toggle="modal" type="button" | Launch demo modal /   Modal   BEMHTML  = render_component :modal do |template| = template.block :modal do |modal| = modal.add_attrs id: 'my-modal' = modal.specify(elem: :dialog).add_cls 'modal-lg' = modal.specify(elem: :title).content 'Modal title' = modal.specify(elem: :body).content p Modal body = modal.specify(elem: :footer).content button.btn.btn-default data-dismiss="modal" type="button" Close button.btn.btn-primary type="button" Save changes /   = render_component :modal do |template| = template.block(:modal).add_attrs id: 'my-modal' = template.elem(:dialog).add_cls 'modal-lg' = template.elem(:title).content 'Modal title' = template.elem(:body).content p Modal body = template.elem(:footer).content button.btn.btn-default data-dismiss="modal" type="button" Close button.btn.btn-primary type="button" Save changes 

Conclusion


Before using in your projects, carefully read the documentation and source code to understand all the pros and cons of this approach.


Maybe someone Bemer will help:



In conclusion, I want to say thank you to the whole BEM team for the fast feedback and in particular tadatuta and miripiruni for the detailed answers to questions on the BEM methodology and the template bem-xjst .


Links


  1. Source
  2. Documentation
  3. bemer-bootstrap - Reusable UI components Bootstrap
  4. Sample application
  5. Organizing large Rails projects with namespaces

')

Source: https://habr.com/ru/post/352938/


All Articles