Integrating Image Cropper In Angular Using ngx-image-cropper

Introduction

In this post, we will see how to use an ngx-image-cropper plugin to crop images in an Angular application. We need cropped images to give more flexibility to the user for uploading images of better quality and visibility. Adding this plugin in our application ensures a great extension to our existing functionality. Let's start how to do this. We will see several steps of installation and usage of this plugin as follows.

Installing ngx-image-cropper In Angular Application

We will write the below command to install and save the plugin in our package.json file. This command will install the latest version of the plugin into our application.

npm install ngx-image-cropper --save

Using In Our Application

Now we will add this to our Angular application by adding it in the imports array of ngModule. 

import { NgModule } from '@angular/core';
import { ImageCropperModule } from 'ngx-image-cropper';

@NgModule({
    imports: [
        ...
        ImageCropperModule // Make an entry of this module here
    ],
    declarations: [
        ...
    ],
    exports: [
        ...
    ],
    providers: [
        ...
    ]
})
export class AppModule {
}

Now we will design a component called cropper component. This component will have all the configuration of the ngx-image-cropper. We can use this component in our application where we need to open the cropper. Here I have used MatDialog of Angular material to design my cropper component. We just have to open this dialog by calling the open method. We will see the code in this post very soon for the same.

Example Of ngx-image-cropper

We will design a mat-dialog for the cropper component. We can use this component at various places across the project. I have created a component cropper. Now we will go to cropper.component.html file and write the below code:

Cropper.component.html

<h2 mat-dialog-title>Image Cropper</h2>
<mat-dialog-content style="height:540px">
  <image-cropper [maintainAspectRatio]="true" [imageChangedEvent]="imageChangedEvent"
    [aspectRatio]="aspectRatio" [resizeToWidth]="resizeToWidth" format="format" (imageCropped)="imageCropped($event)"
    (imageLoaded)="imageLoaded()" (loadImageFailed)="loadImageFailed()">
  </image-cropper>
</mat-dialog-content>
<mat-dialog-actions align="end">
  <button mat-button mat-dialog-close>Cancel</button>
  <button mat-button (click)="onDone()" cdkFocusInitial>CROP</button>
</mat-dialog-actions>

Cropper.component.ts file

import { Component, OnInit, Inject } from '@angular/core';
import { MAT_DIALOG_DATA, MatDialogRef } from '@angular/material';
import { ImageCropperModule } from 'ngx-image-cropper';

@Component({
  selector: 'app-cropper',
  templateUrl: './cropper.component.html',
  styleUrls: ['./cropper.component.scss']
})
export class CropperComponent implements OnInit {
  public freeCrop;
  format: any = "jpg"
  imageQuality: any = 92;
  width: any;
  resizeToWidth: any = 720;
  croppedImage: any;
  aspectRatio: any = 1;
  maintainAspectRatio: any = true;
  imageChangedEvent: any;
  imageCroppedEvent: any;
  file: any;
  result: any = {};
  constructor(@Inject(MAT_DIALOG_DATA) public data: any, public dialogRef: MatDialogRef) {    
  }

  ngOnInit() {
  }

  imageCropped(event: any) {
    this.imageChangedEvent = event;
    this.croppedImage = event.base64;
  }

  imageLoaded() {
    // show cropper
  }

  loadImageFailed() {
    // show message
  }

  cancel() {
    this.dialogRef.close();
  }

  onDone() {
    this.dialogRef.close(this.croppedImage);
  }

Now we will use this cropper component in our other component where the image is present and call this cropper component as follows.
Suppose we have one image-create component. Just use the below method to open and getting the cropper image from the cropper component.

Usage Component for cropper - image-create.component.ts file

import { Component, OnInit, ViewChild, NgZone, AfterViewInit } from '@angular/core';
import { MatDialog, MatDialogRef, MAT_DIALOG_DATA } from '@angular/material';
import { CropperComponent } from '../components/cropper/cropper.component';

@Component({
  selector: 'app-image-create',
  templateUrl: './image-create.component.html',
  styleUrls: ['./image-create.component.scss'],
})
export class imageComponent implements OnInit, AfterViewInit {
  constructor(private dialog: MatDialog)
    
    openCropImageDialog(event) {
    return new Promise((resolve, reject) => {
      const dialogRef = this.dialog.open(CropperComponent, {
        width: '90%',
        maxWidth: '680px',
        data: { event: event, aspectRatio: 16 / 9, maintainAspectRatio: true, quality: 92, resizeToWidth: 520 }
      })

      dialogRef.afterClosed().subscribe((res: any) => {
        if (res) {
          resolve(res);
        }
      })
    })

  }

We will call the above function in our usage component where we need to open the cropper dialog. In this function, we are opening the dialog using dialog.open() method. We will pass data from this method to our cropper component. In the example, we have passed event, aspectRatio, maintainAspectRatio, quality, and resizeToWidth properties for our cropper image. These values can differ from one image to others based on the user's requirement. 

The final cropper.component.ts file will look as follows:

import { Component, OnInit, Inject } from '@angular/core';
import { MAT_DIALOG_DATA, MatDialogRef } from '@angular/material';
import { ImageCropperModule } from 'ngx-image-cropper';

@Component({
  selector: 'app-cropper',
  templateUrl: './cropper.component.html',
  styleUrls: ['./cropper.component.scss']
})
export class CropperComponent implements OnInit {
  public freeCrop;
  format: any = "jpg"
  imageQuality: any = 92;
  width: any;
  resizeToWidth: any = 720;
  croppedImage: any;
  aspectRatio: any = 1;
  maintainAspectRatio: any = true;
  imageChangedEvent: any;
  imageCroppedEvent: any;
  file: any;
  result: any = {};
  constructor(@Inject(MAT_DIALOG_DATA) public data: any, public dialogRef: MatDialogRef) {
    this.imageChangedEvent = data.event; 
    if (data.event) {
      this.file = data.event.target.files[0];
    }

    if (data.aspectRatio) {
      this.aspectRatio = data.aspectRatio;
    }
    if (data.maintainAspectRatio) {
      this.maintainAspectRatio = !data.maintainAspectRatio;
    }
    if (data.width) {
      this.width = data.width;
    }

    if (data.height) {
      this.width = data.height;
    }

    if (data.quality) {
      this.imageQuality = data.quality;
    }

    if (data.format) {
      this.format = data.format;
    }

    if (data.resizeToWidth) {
      this.resizeToWidth = data.resizeToWidth;
    }
  }


  ngOnInit() {
  }

  async imageCropped(event: any) {
    this.croppedImage = event.base64;
  }

  imageLoaded() {

    // show cropper
  }

  loadImageFailed() {
    // show message
  }

  cancel() {
    this.dialogRef.close();
  }

  onDone() {
    this.dialogRef.close(this.croppedImage); // Result to return to the dialog opener
  }

Now after passing data, we set the passed data to our cropper component. Go to the cropper.component.ts file and add the below code. We are setting our data to the cropper properties. In the below code, whenever the user will open the cropper component from the image-create component then we will trigger the event(here referred as data.event) and passed the event to our image-cropper through imageChangedEvent which will open image in our cropper. Whenever we release the mouse, the image cropped() method will be called. We will get the base64 image URL as a result. We are returning the cropper image from the cropper component. We have used the below code in our OpenImageCropDialog() to resolve this promise. We will set this response URL to our image later.

    dialogRef.afterClosed().subscribe((res: any) => {
        if (res) {
          resolve(res);
        }
      });

Comments