Controlling Scrolling with @Angular/flex-layout

Flex-layout library written by Angular team can be used to layout our web applications written in @Angular.  One of the issues that we had to persistently deal with is to control scrolling in various parts of the screen independently.  We can always let browser control the vertical scrolling.  However, this becomes a problem with more complex layouts, where for example, you want to have to columns, each one with independent scrolling.  Another example would be to let left column be fixed, and write to be scrollable.  If you let browser control scrolling, you will see that left side, albeit fixed, will show unusable space underneath.  Here is an example that looks Ok to begin with, but looks strange when you scroll the left side.

image

image

I wanted to create a clean prescriptive solution for independent vertical scrolling in multiple components.  To demonstrate I will re-create the layout above with a few components.

  • Root component that is created automatically when you scaffold new app with Angular CLI.
  • Left side component, called BlueComponent
  • Write side wrapper component, called YellowComponent
  • Small component that will be in the first row inside yellow, called RedComponent
  • Component that will host a list, called GreenComponent
  • Component which be a row inside green component, called ScrollableOrangeComponent

What I want is for yellow and blow occupy 50% of width of the visible screen without scrolling.  I also want the content of the green component to scroll independently.

Here is how it looks when all rows inside green are visible due to browser size.

image

Here is how it looks when green does not have enough height to accommodate all the instance of orange component

image

I created components with borders so that I can tell where it ends. In this case I achieved my goal.  How is this done?  You have to follow a few steps.

1. We have to constraint the browser window vertically to ensure there are no scrollbars in the browser, as you may end up with multiple vertical scrollbars in this case.

This can be done by setting a little bit of css applied to the body element in the shared root CSS file included in Angular template project.  This file is called styles.css. I am going to use vertical size units called vh.  These are vertical height percentage unit.  If we set that to 100, this would occupy all available vertical space.  We also want to reset margins to ensure that they are 0 and occupy no space.

body {
    height: 100vh;
    background-color: bisque;
    margin: 0;
}

I set background color to be able to see the body.  In my case I do not see any, which is the idea.

2.  Now we have to setup root component to also be constrained vertically.

In order to do this, we have to use row layout from flex-layout (or flexbox) library.  I want to have to 50% columns, one for blue, one for yellow.

<div fxFlexFill fxLayoutAlign=”start stretch” fxLayout=”row” class=”root”>

    <app-blue fxFlex=”50″ fxFlexFill></app-blue>

    <app-yellow fxFlex></app-yellow>

</div>

Let’s walk through this if you have not use flex-layout library before.  FxFlexFill stretches out the content to occupy all available space, aka 100% of width and height.  fxLayout set to row, making sure that our content will be arranged as a number of columns.  In our case we have two compoentns: blue and yellow.  Each one is set to 50% via fxFlex=”50”.  We are using fxFlexFill on blue as well.  We are using additional root class, but just to set the border so that we can see our root component.

Blue component is very simple, just a title.

<div class=”blue” fxLayout=”row” fxlayoutAlign=”start stretch” fxFlexFill>

    <div fxFlex>Blue</div>

</div>

Again we are making sure it stretches the content with fxFlexFill.  It’s layout is not relevant for us.

3.  Now we need to setup component on the right hand side with two components inside arranged in rows.

<div class=”yellow” fxLayout=”column” fxlayoutAlign=”start stretch” fxFlexFill>

    <div fxFlex=”none”>Yellow</div>

    <app-red fxFlex=”none”></app-red>

    <app-green fxFlex></app-green>

</div>

What we have here is fxLayout set to column in order to arrange the inner components in rows.  In the first row we have just the title.  We are setting it;s fxFlex to none in order to opt out of flexbox layout and let the component size itself.  Red component in the next row, also opting out of any resizing.  Finally, our green component is set to fxFlex without a value, which means occupy all available space, in this case vertical space of the last row,

4.  Now we need to setup our next component to accommodate scrollable content when necessary.

<div class=”green” fxLayout=”column” fxlayoutAlign=”start stretch”>

    <div fxFlex>Green</div>

    <app-scrollable-orange [title]=”item” *ngFor=”let item of items”></app-scrollable-orange>

</div>

We layout this component’s contents in rows, using fxLayout set to column.  We set layout align to start stretch, where start means in the direction of the layout, horizontally in this case.  Then we want to occupy all available space in perpendicular direction, vertically in this case.  Then we are creating some number of scrollable components, using ngFor.

import { Component, OnInit } from &#39;@angular/core&#39;;

@Component({
  selector: &#39;app-green&#39;,
  templateUrl: &#39;./green.component.html&#39;,
  styleUrls: [&#39;./green.component.css&#39;]
})
export class GreenComponent implements OnInit {
  items: number[] = [];
  constructor() {}

  ngOnInit() {
    for (let i = 1; i &lt;= 40; i++) {
      this.items.push(i);
    }
  }
}

We do that in the onInit method.  We also need to enable scrolling in this component in its css file.

.green {
  background: green;
  color: white;
  border: 5px bisque solid;
}
:host {
  overflow-y: auto;
}

We do two things here.  We set vertical overflow to automatic on the :host.  When angular creates components, it will create a component html tag, in our case aap-green, as this is what we have in our yellow component, then it will put the content of the green’s template inside of it.  We want to make sure the outer element (app-green) is scrollable.  Inside the css this is visible as :host.  The rest if css is not relevant, and just controls the colors.

Scrollable orange component is also not relevant for the layout, but for the sake of completeness, here is its source:

import { Component, OnInit, Input } from &#39;@angular/core&#39;;

@Component({
  selector: &#39;app-scrollable-orange&#39;,
  templateUrl: &#39;./scrollable-orange.component.html&#39;,
  styleUrls: [&#39;./scrollable-orange.component.css&#39;]
})
export class ScrollableOrangeComponent implements OnInit {

  @Input() title?: string = &quot;1&quot;;

  constructor() { }


  ngOnInit() {
  }

}

It’s template is also simple:

<div class=”orange”>Orange {{ title }}</div>

That is it.  So, to summarize, we have to constrain the body and root component vertically.  Then we need to allow our component with scrollable content to occupy all available vertical space inside its parent and show scrollbar when the content is too large.  You can do something similar for horizontal scrolling, but this is far less common.

You can download the sample project here.

Enjoy.

One Comment

Leave a Reply

Your email address will not be published. Required fields are marked *