📜 ⬆️ ⬇️

Use Marionette.Region to create boot views

In client applications, it is often necessary to somehow visualize the process of downloading data from the server. In this article, I will describe a way to achieve this behavior by reusing the Marionette.Region area in MarionetteJS.

I’ll say right away that my approach is largely based on the screencast author’s approach on www.backbonerails.com . This is a very good and useful series of screencasts, not only (and not so much) from the point of view of what is discussed here, but also in general for studying MarionetteJS.

purpose


So our goal is to develop a reusable component for MarionetteJS, which deals with the visualization of the loading process. As will be shown later, in the case of Backbone / Marionette, the start and end loading events can be considered a special case of a change in the model state, therefore the purpose of this component can be formulated more abstractly as a visualization of the change in the model state.

Getting Started with BackboneJS


As everyone is well aware, MarionetteJS is a superstructure over BackboneJS. And I would like to start with how Backbone can help with this task. Suppose we have a model (or collection) and a representation that displays it. At the BackboneJS level, we could solve the task as follows:
')

Figure 1 - Model events when loading data

That is, a simple idea is that the view surveys the model and uses the request and sync events generated by Backbone.Model to go to the “boot” (for example, draws some kind of boot animation) or to the “synchronized” state (removes the boot animation ). For the sake of brevity, I will call the ability to respond to model state change events such as request, sync, error, etc. responsiveness . By state change, I mean any events that are not related to a change in model data.

In general, Backbone allows us to easily achieve the desired behavior, due to events generated by Backbone.Model (or Backbone.Collection), but there are problems with reusing code that needs to subscribe to model events and process them. Actually, the question of how to implement responsiveness does not arise; the main question is where this can be done so that the use of this implementation is the most unobtrusive and convenient. I will not dwell on further reasoning, as it would be possible to implement our component on the basis of Backbone, because in any case it will not work out better than on the basis of MarionetteJS.

We use representations MarionetteJS


When I first encountered the task of loading visualization, I did not think that there would be so few ready-made solutions on this issue. Googling found several discussions about the implementation of the Marionette.View.loadingView attribute in the same way as Marionette.CollectionView.emptyView. Later, a ready- made solution appeared . But to be honest, I believe that visualizing the loading of a model at the presentation level that directly reflects this model is not a good idea. In general, the way of visualization of loading depends not on the model representation, but on who displays it. Those. if we display different models in the same place in the document, then the loading visualization should look uniform for all of them. In short, this option does not suit us.

We use MarionetteJS controllers


Now it's time to talk about the approach on which my idea originated. It is described in this issue of the already mentioned series of screencasts. In a nutshell, this approach uses the model as a source of events and a basic abstract controller that determines how views are displayed. Here is an outline of this approach:
  1. Directly responsiveness is implemented by a pair of LoadingControler + LoadingView.
  2. To work with LoadingView, the base class class of the controller (show, region, options) is used. Derived controllers should use this method to display views. This method in turn creates an instance of LoadingController and passes it the original view and the area to display. Further, LoadingController immediately in its constructor (and not on the request event) substitutes LoadingView into the specified area.
  3. LoadingController reviews the model. And, conveniently, the model can not be specified explicitly, by default, RealView.collection and RealView.model will be viewed. More specifically, LoadingController does not view the model, but the xhr objects associated with requesting this model's data. Upon completion of these requests, instead of LoadingView, RealView is inserted in the same area.


Figure 2 - Sequence Diagram with LoadingView + LoadingController

Remarks
LoadingController works with LoadingView and RealView not directly, but through Marionette.Region, but this is not important for a general understanding of the work.

Here is the class diagram that participates in this interaction:


Figure 3 - Class diagram when using LoadingView + LoadingController

This is the first approach that has already been mentioned. But it did not suit me for the reason that it imposes some requirements on the overall architecture of the application:
  1. All controllers must inherit from the same base class and use only its method to display the views.
  2. To display the boot view, you must follow the sequence: start loading the model, create a new view, show it.

At the same time, there are very good solutions that I borrowed:
  1. The ability to determine the model you want to review, without explicit reference to it.
  2. Responsibility for the visualization of the load is beyond the scope of the model presentation.


Use Marionette.Region


Marionette.Region personifies some area of ​​the screen in which views are placed, and allows you to control the lifetime of the view and separate from the view the way it appears on the screen. If you want to show a presentation in the field, you do not need to think about what will happen to the presentation that is already placed in the area - it will be deleted automatically. If you need to change the way a view appears on the screen, you can simply inherit from Marionette.Region and implement your logic. For example, you can change the way you appear by adding animation, or you can change the way you insert a view into a document, wrapping it with some of your own elements. For example, this release describes the implementation of a private area that wraps an arbitrary view into a dialog box.

In my implementation, the main work takes place in the abstract class ResponsiveRegion, which is the successor of Marionette.Region. Specific classes need only define handlers that change the appearance of the area depending on the model events and, possibly, list the events themselves. For example, you can change the transparency, the visibility of the elements of the region, insert some kind of overlay with animation, in general, do anything. I will not pay much attention to the decoration part, but focus on the abstract class. Here's how I implemented responsiveness at the domain level using the ResponsiveRegion:

  1. ResponsiveRegion initialization is not different from Marionette.Region. Suppose a Layout view (derived from LayoutView) that defines an area indicates (declaratively) that our implementation of the ResponsiveRegion area should be used, not the standard Marionette.Region.

    List.Layout = Marionette.LayoutView.extend({ ... regions : { someRegion : { selector : '#region', regionClass : Marionette.ResponsiveVisiblityRegion } } ... }); 

  2. To show the view in this area, you can use region.show (view) (as with the usual Marionette.Region), then the default parameters will be used, or you can use region.show (view, options) and specify the advanced responsiveness settings.
  3. The area puts the submitted view into the document. She can do it not directly, but through a wrapper.
    Example
      <div class=”js-model-state-sync”>     </div> <div class=”js-model-state-request”>     </div> <div class=”js-model-state-error”>      </div> 


  4. The region reviews the model of the view in it (the model is indicated either explicitly in region.show (view, options), or RealView.collection and RealView.model are implicitly used). Depending on the request / sync / error event, an area changes the appearance of its content. For example, toggles the visibility of wrapper elements.

The sequence diagram looks like this:


Figure 4 - Sequence Diagram with ResponsiveRegion

A classes like this:


Figure 5 - Class diagram when using ResponsiveRegion

Remarks
In reality, there are also dependencies between Layout and Controller on ResponsiveRegion. The first link is omitted due to the fact that, despite the fact that the Layout view is actually dependent on the ResponsiveRegion, it actually works with it as usual Marionette.Region. It is just more convenient to declare the type of the area in the layout declaration. The second link is omitted because it is optional and only occurs if the controller needs to specify responsiveness options.

What have we achieved by applying this approach?
  1. We fully complied with the structure of the objects of MarionetteJS, without creating a single entity, but only expanding Marionette.Region. ResponsiveRegion in most cases is also used as its ancestor, and does not make any additional assumptions about the components with which it is used. Compared to the previous approach based on an abstract controller, there is no basis for how your application is organized: if you use Marionette, you can use ResponsiveRegion.
  2. We review the model all the time while its presentation is in the ResponsiveRegion. ResponsiveRegion, as the successor to Marionette.Region, is naturally aware of the cessation of the existence of the view, which allows it to unsubscribe from the events of the model at the right moment and not to leave trash behind.
  3. We consider the event of the beginning and end of the model loading as a special case of a change in its state, which can occur any number of times during the model's lifetime. This allows you to freely react to other model events, such as a loading error or validation of model data.


Conclusion


All of the approaches considered can be successfully applied depending on the situation. But I believe that to visualize the change in the state of a model in general and to load its data in particular, the variant with Marionette.Region is best suited. Firstly, this is the component that in Marionette is responsible for displaying the views on the screen. And secondly, the necessary logic is simple enough so that it can be implemented within one component. That's all. The source code of what was said here and a small example are available here .

Links


  1. backbonerails.com - screencast series by MarionetteJS
  2. Documentation MarionetteJS
  3. BackboneJS Event List

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


All Articles