Search code examples
jquerycssbackbone.jsmarionette

Backbone Marionette Region + Layout + View hierarchy and responsiveness


I am writing my first Marionette app and would like to use the Marionette UI structure.

My understanding is that, roughly speaking,

  • A Region is 1:1 with an existing, single DOM node (like a div or a span) and may contain Views, including the special ones provided by Marionette
  • A Layout is a View as well as a container of Regions and specifies a template by which those regions will be arranged; as a View it may be rendered into a Region

So I think this means you must follow this sort of hierarchy:

- Root (region) [could be more than one]
-- Layout A
--- Inner Region A1
--- Inner Region A2
-- Layout B
--- Inner Region B1
--- Inner Region B2
-- View C
--- maybe some subviews?

If my assumptions are wrong, please correct.

In any case, my application has a navigation and content area in the UI. Now, when the script is loaded, we may be loading it into a page that already has div#region-navigation in place to customize the appearance, or we may be loading it into a page without the node in place. If the navigation node is in place, we don't need to render it, but we do need to be able to maintain a reference to it and do things to it ("Log In" => "Log Out" for instance). On the other hand, if it is not in place, we need to render and maintain it.

My question is: what is the "Marionette" way to handle this? I have thought of one way, but I would really like to avoid going down any unnecessarily painful paths.

My solution is to create an absolute RootRegion that is a single selector (default body) that definitely does not exist at the time of creation.

I would have two AppLayouts: an InjectedAppLayout, where the layout only has a Content region, and a ManagedAppLayout, where the layout replaces the contents of body.

Then based on the script tag data- params and/or what is on the page (using jQuery) I can choose which Layout to use.

In either case, I have a HeaderRegion and a ContentRegion. In the case of an InjectedAppLayout the HeaderRegion lives outside while the ManagedAppLayout contains both. Then, I would possibly need to make a separate ExternalHeaderRegion and InternalHeaderRegion or use a conditional because I would need to handle things very differently depending on whether it is being managed by me or not.

This seems very sub-optimal but I haven't found any example of how people manage this.

Finally, in the case of an InjectedAppLayout, I am afraid that the div containing the ContentLayout may be very small even if the screen width is big because I do not control it. My styles, using Bootstrap and media queries, all use max-width values to determine which styles to set. My question is: will the @media (max-width: XXXpx) queries still apply to the containing div in the case of the injected app layout?


Solution

  • I've used following structure

    -Marionette Application (root - have regions hash of existing node elements)
    -- LayoutView (breaks application region in sub regions if needed)
    
    ---CollectionView || CompositeView (render collections)
    ----ItemView
    ||
    ---LayoutView (create more sub-regions) 
    ---- (other sub views)
    ||
    ---ItemView (render model)
    

    +Router and controllers for application statuses maintain

    Lets separate responses between this stuff

    Application - starts first. In charge of keeping constants and global params, start and stop sub modules load default routers and controllers + provide Request/Response channel.

    1) Can receive some extra params on start

    var options = {
      something: "some value",
      another: "#some-selector"
    };
    
    MyApp.start(options);
    

    2) have regions has to work with existing nodes (navs, content, footer, sidebars and so on)

    MyApp.addRegions({
      someRegion: "#some-div",
      anotherRegion: "#another-div"
    });
    

    So you can provide some params to JS to render correct views + you can hook-up your application with node elements

    Router and Controller

    Helps you to render correct view according to application status. In charge of keeping application view correct according to the url and provide navigation You can use it with links + navigate manually with navigate method

    var MyRouter = new Marionette.AppRouter({
      controller: myController,
      appRoutes: {
        "foo": "doFoo",
        "bar/:id": "doBar"
      }
    });
    

    LayoutView In Charge of rendering and closing subviews. Separate parent node in some child

    Backbone.Marionette.LayoutView.extend({
      template: "#layout-view-template",
    
      regions: {
        menu: "#menu",
        content: "#content"
      }
    });
    

    Using this structure you can pass to application some params to let application know if user loged in or not. According to this you can add regions with addRegions and render some Layouts (like 'UserNav' or 'GuestNav') . Layout will render its child views like UserLinks, UserAvatar and so on. Then user click links Router and Controller handle this and tell Application what to render in some region.