tjoskar / ng-lazyload-image

🖼 A small library for lazy loading images for Angular apps with zero dependencies
https://naughty-bose-ec1cfc.netlify.com
MIT License
762 stars 142 forks source link

bug(hook): hook won't trigger on lazy-loaded routes #554

Open DwieDima opened 2 years ago

DwieDima commented 2 years ago

I use the latest version of ionic/angular.

Here I want to add a FadeIn animation when an image is initially loaded. After loading the image, the FadeIn animation should not be executed again on RouteChange.

Currently the hook does not trigger at all (no animation visible, no console logs).

Expected: Hook should trigger

import { Injectable, NgModule } from '@angular/core';
import { BrowserModule } from '@angular/platform-browser';
import { RouteReuseStrategy } from '@angular/router';

import { IonicModule, IonicRouteStrategy } from '@ionic/angular';
import {
  Attributes,
  IntersectionObserverHooks,
  LAZYLOAD_IMAGE_HOOKS,
} from 'ng-lazyload-image';

import { AppRoutingModule } from './app-routing.module';
import { AppComponent } from './app.component';

@Injectable()
class LazyLoadImageHooks extends IntersectionObserverHooks {
  public constructor() {
    super();
    console.log('SUPER');
  }

  public setLoadedImage(imagePath: string, attributes: Attributes): void {
    console.log('setLoadedImage');
    attributes.element.classList.add('animated-img');
    super.setLoadedImage(imagePath, attributes);
  }

  public finally(attributes: Attributes): void {
    console.log('finally');
    super.finally(attributes);
    setTimeout(() => {
    // timeout must match animation duration defined in global.scss
    // remove class when image loaded
    // otherwise fadeIn animation will trigger on every route change
    attributes.element.classList.remove('animated-img');
    }, 3000);
  }
}

@NgModule({
  declarations: [AppComponent],
  imports: [
    BrowserModule,
    IonicModule.forRoot({ mode: 'ios' }),
    AppRoutingModule,
  ],
  providers: [
    { provide: RouteReuseStrategy, useClass: IonicRouteStrategy },
    { provide: LAZYLOAD_IMAGE_HOOKS, useClass: LazyLoadImageHooks },
  ],
  bootstrap: [AppComponent],
})
export class AppModule {}

global.scss

.animated-img {
  animation: fadeIn 3s;
}

@keyframes fadeIn {
  0% {
    opacity: 0;
  }
  100% {
    opacity: 1;
  }
}

Reproduction:

https://stackblitz.com/edit/github-i3f4hm?file=src/app/app.module.ts

i suspect the issue has to do something with the routing of ionic ion-tabs, if you put the images outside of ion-tabs, everything works as expected. Unfortunately I could not find a solution yet.

DwieDima commented 2 years ago

@tjoskar After some investigation this should cause the problem:

https://github.com/tjoskar/ng-lazyload-image/blob/dd170a5c33d80449da8bc340d2e268fcf84806d7/src/lazyload-image.module.ts#L9

https://github.com/tjoskar/ng-lazyload-image/blob/dd170a5c33d80449da8bc340d2e268fcf84806d7/src/lazyload-image.directive.ts#L34

Since your examples are not lazy-loaded, providing custom hook works, because they are initialized in app.module.ts using the injection token LAZYLOAD_IMAGE_HOOKS.

Lazy-loaded routes have their own module, where they get the default initialization and provider in app.module.ts is ignored. In this case these lines above.

As a workaround you have to apply the provider to each lazy-loaded module, which i would not recommend.

It should be possible to apply these settings globally. I already tried in your lib to remove providers in lazyload-image.module.ts and optionally initialize the hook in lazyload-image.directive.ts if undefined. But this seems to throw an NullinjectionError :(

lazyload-image.directive.ts

this.hooks = hooks || new IntersectionObserverHooks();

lazyload.image.module.ts

@NgModule({
  declarations: [LazyLoadImageDirective],
  exports: [LazyLoadImageDirective],
})
tjoskar commented 1 year ago

Hi, Sorry for not responding to this earlier! It has been a couple of crazy weeks for me.

I will take a look, but PRs are most welcome.

DwieDima commented 1 year ago

Hi @tjoskar, have you had time to look at the issue? It looks like the custom hooks are not initialized as singleton. This is also the reason why every module in the providers must be assigned the custom hooks class. This should be solvable with forRoot, unfortunately I haven't come to a good solution yet :(

tjoskar commented 1 year ago

@DwieDima, unfortunately not. Ever since I got a kid a few months back, I have had almost zero time for my open-source projects. I believe I have to find new maintainers for this project.

DwieDima commented 1 year ago

@tjoskar ohh congratulations! Yes that is of course understandable. Would it be ok for you to merge my pull request then?

This contains the separate control of image fadeIn by configuration in directive: #560

KirstenStake commented 1 year ago

@DwieDima did you ever find a solution? trying to use the debounce hook but no hooks are being triggered in my ionic project.

@tjoskar any way of making the hooks work on a lazy loaded module app?

DwieDima commented 1 year ago

@KirstenStake yes, as a workaround i have to apply the provider in each lazy-module

...
  providers: [
    // TODO: apply only in app.module when issue is resolved
    // https://github.com/tjoskar/ng-lazyload-image/issues/554
    { provide: LAZYLOAD_IMAGE_HOOKS, useClass: LazyLoadImageService },
  ],
...