Skip to content

Instantly share code, notes, and snippets.

Show Gist options
  • Save eneajaho/28c4ef1d75bf1d8733cec23e54068c0a to your computer and use it in GitHub Desktop.
Save eneajaho/28c4ef1d75bf1d8733cec23e54068c0a to your computer and use it in GitHub Desktop.
Provide function with InjectionToken in Angular
import {
Component,
Inject,
inject,
Injectable,
InjectionToken,
NgModule
} from '@angular/core';
import { BrowserModule } from '@angular/platform-browser';
@Injectable({ providedIn: 'root' })
export class LoggerService {
success(message: string): void {
console.log('Success >>>> ', message);
}
}
// export const loggerFn = (message: string): void => console.log(message);
export type SuccessLogger = (message: string) => void;
export const Logger = new InjectionToken<SuccessLogger>(
'Logger Success function',
{
factory: () => inject(LoggerService).success
// factory: () => loggerFn
}
);
@Component({
selector: 'my-app',
template: `
<button (click)="log('hahahaha')">
Log to console
</button>
`
})
export class AppComponent {
constructor(@Inject(Logger) public log: SuccessLogger) {}
}
@NgModule({
imports: [BrowserModule],
declarations: [AppComponent],
bootstrap: [AppComponent]
})
export class AppModule {}
@eneajaho
Copy link
Author

eneajaho commented Sep 24, 2021

Scroll top injection token

import { inject, InjectionToken } from '@angular/core';
import { WINDOW } from '@ng-web-apis/common';

export type ScrollTopFn = () => void;

export const ScrollTop = new InjectionToken<ScrollTopFn>(
  'Scroll Top function',
  {
    // this factory returns an arrow function that when gets fired the window will scroll to top
    factory: () => {
      // we need to inject the WINDOW before we return the arrow function
      // because this way the injection happens at compile time and not at runtime
      const window = inject(WINDOW);

      return () =>
        window.scrollTo({
          top: 0,
          left: 0,
          behavior: 'smooth',
        });
    },

    // The code below doesn't work, because this way the inject will be called at runtime and the context will get lost
    // factory: () => () => inject(WINDOW).scrollTo(0, 0)
  }
);

Usage

import { Component, Inject } from '@angular/core';
import { ScrollTop, ScrollTopFn } from './scroll-top.token';

@Component({
  selector: 'my-app',
  template: `
    <div>Hello world!</div>

    <div style="height: 150vh"></div>

    <button (click)="scrollTop()">Scroll top</button>
  `,
})
export class AppComponent {

  constructor(
    @Inject(ScrollTop) public scrollTop: ScrollTopFn
  ) {}

}

Stackblitz link

@eneajaho
Copy link
Author

import { inject, Injectable, InjectionToken } from '@angular/core';
import { FormGroup } from '@angular/forms';
import { ToastrService } from 'ngx-toastr';
import { HttpErrorResponse } from '@angular/common/http';



export function handleServerSideValidation(error: HttpErrorResponse,  form: FormGroup): undefined | string {
  // if the error isn't with status 422 (Unprocessable Entity) don't do anything
  if (error.status !== 422) {
    return undefined;
  }

  const unhandledErrors: any[] = [];
  const validationError = error.error?.errors;

  Object.keys(validationError || {}).forEach(element => {
    const formControl = form.get(element);

    if (formControl) {
      formControl.setErrors({ serverSideError: validationError[element].join('') });
    } else {
      // Field is not defined in form but there is a validation error for it, set it globally
      unhandledErrors.push(validationError[element].join(''));
    }
  });

  if (unhandledErrors.length) {
    return unhandledErrors.join();
  }

  return undefined;
}


export type HandleHttpErrorFn = (error: Error, form: FormGroup) => void;

export const HandleHttpError = new InjectionToken<HandleHttpErrorFn>(
  'Handle Http Error token', {
    factory: () => {
      const handleErrorService = inject(HandleServerSideValidationService);
      return handleErrorService.handleServerSideValidationError;
    }
  }
);

@Injectable({ providedIn: 'root' })
export class HandleServerSideValidationService {

  constructor(private toast: ToastrService) {}

  handleServerSideValidationError(error: Error, form: FormGroup): void {
    if (error instanceof HttpErrorResponse) {
      const unhandledErrors = handleServerSideValidation(error, form);
      if (unhandledErrors) {
        this.toast.error(error.statusText, unhandledErrors);
      }
    }
  }

}

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment