top of page
Writer's pictureThe Tech Platform

Building responsive UI with Angular Material — BreakpointObserver



Building responsive web application is one of the unsaid requirement nowadays. There are different ways one can build responsive UI, ranging from plain old CSS media queries to various framework and libraries like Angular Material.


In this story we will see how to build react to changing viewport on the device as an Observable and update UI accordingly. We will use BreakpointObserver component from Angular material CDK library. We will render list of card components differently based on target device type.


The BreakpointObserver CDK component contains BreakPoints object contains various predefined screen types as below:

export declare const Breakpoints: {
    XSmall: string;
    Small: string;
    Medium: string;
    Large: string;
    XLarge: string;
    Handset: string;
    Tablet: string;
    Web: string;
    HandsetPortrait: string;
    TabletPortrait: string;
    WebPortrait: string;
    HandsetLandscape: string;
    TabletLandscape: string;
    WebLandscape: string;
};

Once the BreakpointObserver service is injected into the component, we can subscribe to desired screen type and update our UI as a response.

this.cardsLayout=merge(
    this.breakpointObserver.observe([
        Breakpoints.Handset,
        Breakpoints.XSmall,
        Breakpoints.Small]).pipe(
        map(({ matches })=>{
            if(matches){
                console.debug('👉🏽 handset layout activated',);
                return this.getHandsetLayout();
            }
             return this.getDefaultLayout();
         })),
   this.breakpointObserver.observe(Breakpoints.Tablet).pipe(
       map(({ matches })=>{
           if(matches){
               console.debug('👉🏽 tablet layout activated',         
                                                   this.cardsLayout);
               return this.getTabletLayout();
           }
           return this.getDefaultLayout();
        })),
   this.breakpointObserver.observe(Breakpoints.Web).pipe(
        map(({ matches })=>{
            if(matches){
                console.debug('👉🏽 web layout activated', 
                                                    this.cardsLayout);
                return this.getWebLayout();
             }
        return this.getDefaultLayout();
   }))
);

In this example, we are observing Handset, XSmall and Small screen type to display UI targeting mobile and small screen handheld devices. The Tablet and Web screen types are pretty explanatory.

getHandsetLayout(): Layout{
    return{
        name: 'Handset',
        gridColumns: 1,
        layoutItem: [
            {title: 'Card 1',cols: 1,rows: 1},
            {title: 'Card 2',cols: 1,rows: 1},
            {title: 'Card 3',cols: 1,rows: 1},
            {title: 'Card 4',cols: 1,rows: 1},
            {title: 'Card 5',cols: 1,rows: 1},
            {title: 'Card 6',cols: 1,rows: 1},
            {title: 'Card 7',cols: 1,rows: 1}
        ]
    };
}

getTabletLayout(): Layout{
    return{
        name: 'Tablet',
        gridColumns: 4,
        layoutItem: [
            {title: 'Card 1',cols: 2,rows: 1},
            {title: 'Card 2',cols: 2,rows: 1},
            {title: 'Card 3',cols: 2,rows: 1},
            {title: 'Card 4',cols: 2,rows: 1},
            {title: 'Card 5',cols: 1,rows: 1},
            {title: 'Card 6',cols: 1,rows: 1},
            {title: 'Card 7',cols: 1,rows: 1}
        ]
    };
}
            
getWebLayout(): Layout{
    return{
        name: 'Web',
        gridColumns: 6,
        layoutItem: [
            {title: 'Card 1',cols: 2,rows: 1},
            {title: 'Card 2',cols: 2,rows: 1},
            {title: 'Card 3',cols: 2,rows: 1},
            {title: 'Card 4',cols: 2,rows: 1},
            {title: 'Card 5',cols: 2,rows: 1},
            {title: 'Card 6',cols: 2,rows: 1},
            {title: 'Card 7',cols: 1,rows: 1}
        ]
    };
}

getDefaultLayout(){
    return {
        name: 'default',
        gridColumns: 1,
        layoutItem: [    
            {title: 'Card 1',cols: 1,rows: 1},
            {title: 'Card 2',cols: 1,rows: 1},
            {title: 'Card 3',cols: 1,rows: 1},
            {title: 'Card 4',cols: 1,rows: 1},
            {title: 'Card 5',cols: 1,rows: 1},
            {title: 'Card 6',cols: 1,rows: 1},
            {title: 'Card 7',cols: 1,rows: 1}
        ]
    };
}


The HTML template subscribe the cardLayout.layoutItem observable via async pipe and update the colspan and rowspan properties.

<div class = "grid-container">
    <h1 class = "mat-h1">Dashboard</h1>
    <h3>Current Layout: {{ (cardsLayout | async)?.name }}</h3>
    <mat-grid-list [cols]="(cardsLayout | async)?.gridColumns" 
    rowHeight="350px">
        <mat-grid-tile  *ngFor="let card of (cardsLayout | 
        async)?.layoutItem" [colspan]="card.cols" 
        [rowspan]="card.rows">
            <mat-card class="dashboard-card">
                <mat-card-header>
                    <mat-card-title>            
                        {{card.title}}            
                    </mat-card-title>
                </mat-card-header>
            <mat-card-contentclass="dashboard-card-content">
                <div>Card Content Here</div>
            </mat-card-content>
        </mat-card>
    </mat-grid-tile>
  </mat-grid-list>
</div>

The complete source code can be found in this GitHub repository.


Demo




Source: Medium - Balram Chavan


The Tech Platform


0 comments

Comments


bottom of page