Custom directive that combines other directives

Hi,

In my Ionic app,I have a common functionality that relies on the ion-content scroll value to change the size of an image in ion-header. At the top most position, the image will display in its full size. As the user scrolls down, the image will gradually shrink in size.

Here is a simplified template code:

<ion-header>
   <ion-img src="logo.png" style="width:{{imgWidth}}px;"></ion-img>
</ion-header>

<ion-content [scrollEvents]="true" (ionScroll)="onScroll($event)">
...other stuff
</ion-content>

In terms of achieving this feature, I don’t have any issues. However I have no idea how to abstract this piece of functionality into a reusable module because as you can tell, it needs to be implemented in multiple pages. Should this be a directive? A component?

Can anyone shed some light?

Thank you in advance.

As customEvent is not working with stackblitz, please test with real project; feel free to let me know if you face any issue in implementation

Directive:

import { Directive ,Input ,Output,EventEmitter } from '@angular/core';

@Directive({
  selector : '[appScroll]'
})

export class AppScrollDirective {
  @Input() set appScroll(data) {
    console.log(data)
  /*   if(data.detail.scrollTop > 500) {
        this.imgWidth.emit(50)
    } else {
      this.imgWidth.emit(100)
    } */
  }
  @Output() imgWidth = new EventEmitter<any>();
}

Html

<ion-header>
  <ion-toolbar>
    <ion-title>
      My super tabs app
    </ion-title>
  </ion-toolbar>
  <ion-toolbar>
    <img src="https://static.thenounproject.com/png/6725-200.png" [style.width.px]="imgWidth">
  </ion-toolbar>
</ion-header>

<ion-content  [scrollEvents]="true" (ionScroll)="scrollValue = $event" [appScroll]="scrollValue" (imgWidth)="imgWidth = $event">
  <div style="margin-top:800px">Scroll me</div>
</ion-content>

ts

export class Tab1Page {
  scrollValue;
  imgWidth = 100
  constructor() {}

}

Playground link: https://stackblitz.com/edit/ionic-v4-angular-tabs-bhztan?file=src%2Fapp%2Ftab1%2Ftab1.page.html

1 Like

@indraraj26 I would like to thank you for steering me into the right direction.

Let me share my final implementation for everyone’s benefit. It still requires repetitive copy-pasting of codes in component TS and HTML files but it’s very minimal so I can live with that.

Custom directive:

import { Directive, Output, EventEmitter, HostListener } from '@angular/core';

@Directive({
  selector: '[appVerticalScrollTracker]'
})
export class VerticalScrollTrackerDirective {

  @Output() imageWidth: EventEmitter<number> = new EventEmitter();

  maxImageWidth: number = 75;
  minImageWidth: number = 50;

  constructor() { }

  @HostListener('ionScroll', ['$event']) onScroll(event: CustomEvent) {
    this.calculateSize(event.detail.scrollTop);
  }

  calculateSize(scrollTop) {
    let newSize = this.maxImageWidth - (scrollTop / 20);
    if(newSize >= this.minImageWidth) {
      this.imageWidth.emit(newSize);
    } else {
      this.imageWidth.emit(this.minImageWidth);
    }
  }

}

Component TS file. Just define this single property:

imageWidth: number;

Component template file.

<ion-header>
   <ion-img src="logo.png" [style.width.px]="imageWidth"></ion-img>
</ion-header>

<ion-content [scrollEvents]="true" appVerticalScrollTracker (imageWidth)="imageWidth = $event">
   ...other stuff
</ion-content>
1 Like

Looks good, Can you explain why are you dividing with 20 (scrollTop / 20)

Oh that’s just for gradually altering the width value (px) relative to the scrollTop value. If I just take the raw scrollTop value, the calculated width values would be too great causing the ion-img to change size too rapidly.

1 Like