Search code examples
javascriptcssangulartypescriptangular-material

How to push mat-list-items to the bottom without using absolute position in Angular Material?


I have just started using Angular Material and I wanted to implement the sidenav to have items that are at the bottom of the page. For now I am able to accomplish it with css.

position: absolute;
left: 0;
bottom: 0; 

Is there any way to put spaces between items? My issue comes when I resize the page, the bottom items overlap with the items which are above.

I've included screenshots and the code below. Thanks.

Full screen window

Resized window

<mat-sidenav-container class="h-screen w-screen">
<mat-sidenav #sidenav class="shadow border-0" fixedInViewport mode="side" opened>  
    <mat-toolbar>
        <button mat-mini-fab color="warn" (click)="navigation.toggle()">
            <mat-icon inline=true fontSet="fa" fontIcon="fa-fire" aria-label="SLMC Logo"></mat-icon>
        </button>
    </mat-toolbar>
    <mat-nav-list>
        <a mat-list-item>
            <button mat-icon-button>
                <mat-icon inline=true fontSet="fa" fontIcon="fa-users-cog" aria-label="Administrator"></mat-icon>
            </button>
        </a>
        <a mat-list-item>
            <button mat-icon-button>
                <mat-icon inline=true fontSet="fa" fontIcon="fa-tachometer-alt" aria-label="Services"></mat-icon>
            </button>
        </a>
        <a mat-list-item>
            <button mat-icon-button>
                <mat-icon inline=true fontSet="fa" fontIcon="fa-chart-bar" aria-label="Analytics"></mat-icon>
            </button>
        </a>
        <a mat-list-item>
            <button mat-icon-button>
                <mat-icon inline=true fontSet="fa" fontIcon="fa-plus" aria-label="Compose"></mat-icon>
            </button>
        </a>

        <!-- This is where the bottom items reside.-->
        <div class="absolute bottom-0 left-0"> 
            <a mat-list-item>
                <button mat-icon-button>
                    <mat-icon inline=true fontSet="fa" fontIcon="fa-bell" aria-label="Notifications"></mat-icon>
                </button>
            </a>
            <a mat-list-item>
                <button mat-icon-button>
                    <mat-icon inline=true fontSet="fa" fontIcon="fa-user" aria-label="User Account"></mat-icon>
                </button>
            </a>
            <a mat-list-item>
                <button mat-icon-button>
                    <mat-icon inline=true fontSet="fa" fontIcon="fa-cog" aria-label="Settings"></mat-icon>
                </button>
            </a>
        </div>
    </mat-nav-list>
</mat-sidenav>
<mat-sidenav-content>
    <mat-sidenav-container>
        <mat-sidenav #navigation class="shadow border-0" fixedInViewport mode="side" opened>
            <app-navigation (toggleNavigation)="navigation.toggle()"></app-navigation>
        </mat-sidenav>
        <mat-sidenav-content class="page-wrap">
            <app-header></app-header>
            <main class="flex-1">
                <router-outlet></router-outlet>
            </main>
            <app-footer></app-footer>
        </mat-sidenav-content>
    </mat-sidenav-container>
</mat-sidenav-content>


Solution

  • Very interesting and logical question... getting the bottom 3 icons to stick to the bottom requires position:absolute, which causes the over-riding effect (which is exactly the effect which is expected with absolute positioning).

    To get our ideal scenario, we gotta toggle the position:relative between position:absolute based on the breakpoint where the over-riding starts to take effect. This is where we needed JavaScript/TypeScript as CSS can't do this alone...

    You can check complete working stackblitz here

    this is the relevant TS file:

    import {Component} from '@angular/core';
    import {HostListener} from '@angular/core';
    
    /** @title Sidenav open & close behavior */
    @Component({
      selector: 'sidenav-open-close-example',
      templateUrl: 'sidenav-open-close-example.html',
      styleUrls: ['sidenav-open-close-example.css'],
    })
    export class SidenavOpenCloseExample {
      events: string[] = [];
      opened: boolean;
      appropriateClass:string = '';
    
      @HostListener('window:resize', ['$event'])
      getScreenHeight(event?){
        //console.log(window.innerHeight);
        if(window.innerHeight<=412){
          this.appropriateClass = 'bottomRelative';
        }else{
          this.appropriateClass = 'bottomStick';
        }
      }
      constructor(){
        this.getScreenHeight();
      }
    
      shouldRun = [/(^|\.)plnkr\.co$/, /(^|\.)stackblitz\.io$/].some(h => h.test(window.location.host));
    }
    

    note the ngClass in the relevant HTML below:

      <mat-nav-list>
            <div class=""> 
              <a mat-list-item>
                  <button mat-icon-button>
                      <mat-icon inline=true fontSet="fa" fontIcon="fa-users-cog" aria-label="Administrator"></mat-icon>
                  </button>
              </a>
              <a mat-list-item>
                  <button mat-icon-button>
                      <mat-icon inline=true fontSet="fa" fontIcon="fa-tachometer-alt" aria-label="Services"></mat-icon>
                  </button>
              </a>
              <a mat-list-item>
                  <button mat-icon-button>
                      <mat-icon inline=true fontSet="fa" fontIcon="fa-chart-bar" aria-label="Analytics"></mat-icon>
                  </button>
              </a>
              <a mat-list-item>
                  <button mat-icon-button>
                      <mat-icon inline=true fontSet="fa" fontIcon="fa-plus" aria-label="Compose"></mat-icon>
                  </button>
              </a>
            </div>
            <!-- This is where the bottom items reside.-->
              <div [ngClass]='appropriateClass'> 
                  <a mat-list-item>
                      <button mat-icon-button>
                          <mat-icon inline=true fontSet="fa" fontIcon="fa-bell" aria-label="Notifications"></mat-icon>
                      </button>
                  </a>
                  <a mat-list-item>
                      <button mat-icon-button>
                          <mat-icon inline=true fontSet="fa" fontIcon="fa-user" aria-label="User Account"></mat-icon>
                      </button>
                  </a>
                  <a mat-list-item>
                      <button mat-icon-button>
                          <mat-icon inline=true fontSet="fa" fontIcon="fa-cog" aria-label="Settings"></mat-icon>
                      </button>
                  </a>
              </div>
    
        </mat-nav-list>
    

    relevant CSS:

    button {border:1px solid red}
    .bottomStick{/*border-top:1px solid green;border-bottom:1px solid green;*/position:absolute; bottom:0}
    .bottomRelative{position:relative;}