Search code examples
htmlcssformsangularsticky

Is it possible to have sticky div inside an angular form?


I'm working with angular and angular-material.

Inside a page, I have a form, and some buttons (grouped in a div) which are depending on this form.

But, I'd like to have these buttons (the div) sticking the bottom of the page, even if I scroll.

Here's some code :

<form (ngSubmit)="update()" #updateForm="ngForm">
  <div> some content with inputs and selects </div>
  <div class="button-container"> buttons like save, cancel, ... </div>
</form>

and :

.button-container {
  position: sticky !important;
  bottom: 0;
  float: right;
  z-index: 999;
}

If I put the buttons out of the form, they don't work anymore. Thing is, it'd be better if I don't change the buttons' methods, and only modify HTML and CSS. What I did doesn't work, any idea ?

I did this on plunker, with the same CSS properties as my project https://plnkr.co/edit/pw7zOruWwhV0o1Vya717?p=preview


Solution

  • I think I had the same problem. Normally I would use position fixed but this wouldn't work because material used transform: translate3d(0,0,0). This made fixed to behave like absolute. To solve this problem I used the below:

    //Place this in your form
    
    <app-fnls-displacer>
      <div style="position: fixed; right: 30px; bottom: 30px; padding-bottom: 2em; z-index: auto">
        <button mat-fab class="fab" type="submit" (click)="myfunction()">
          <mat-icon>add</mat-icon>
        </button>
      </div>
    </app-fnls-displacer>
    

    This is the component and directive used:

    import {AfterViewInit, Component, Directive, OnDestroy, TemplateRef, ViewChild, ViewContainerRef} from '@angular/core';
    
    import {Overlay, OverlayRef, OverlayConfig, TemplatePortal} from '@angular/material';
    
    @Directive({ selector: '[displacerPortal]' })
    export class DisplacerPortalDirective extends TemplatePortal<any> {
      constructor(templateRef: TemplateRef<any>, viewContainerRef: ViewContainerRef) {
        super(templateRef, viewContainerRef);
      }
    }
    
    @Component({
      selector: 'app-fnls-displacer',
      template: `
        <ng-template displacerPortal>
          <ng-content></ng-content>
        </ng-template>
      `
    })
    export class DisplacerComponent implements OnDestroy, AfterViewInit {
    
      private _config = new OverlayConfig();
    
      @ViewChild(DisplacerPortalDirective)
      private _portal: DisplacerPortalDirective;
    
      private _overlayRef: OverlayRef = undefined;
    
      constructor(private _overlay: Overlay) {}
    
      public ngOnDestroy() {
        this._overlayRef.detach();
      }
    
      public ngAfterViewInit() {
        this._overlayRef = this._overlay.create(this._config);
        this._overlayRef.attach(this._portal);
      }
    }
    

    I found it on a material GitHub page. It places the content inside it directly to the body, so that you can use position: fixed.