Getting Started with ImageEngine and Angular
Tags: imageengine, angular, cdn, optimization
Published: 2021-08-19 12:01:01

###Intro

In this article we'll go through using [@imageengine/angular](https://www.npmjs.com/package/@imageengine/angular) package in a sample project to easily take advantage of [ImageEngine](https://imageengine.io)'s CDN and optimisation engine.

`ImageEngine` is a super fast CDN that exposes an accessible engine to optimise your app image assets. You can optimise your distributions (which we'll call `Engine`s) in different ways - through `ImageEngine`'s dashboard, defining settings for your `Engine` or specific paths in your `Engine`, or through [simple query parameters](https://support.imageengine.io/hc/en-us/articles/360058880672-directives) (called `directives` in `ImageEngine`), that allow you to dynamically, on-the-fly, execute optimizations to your assets. 

The part we'll be leveraging here, through this component, is the query parameters - although this is mostly hidden from you and you don't have to worry about it to use the component. 

To deliver actual real optimizations to your images you need an `ImageEngine` `Engine` , nonetheless this package can still be used by itself for enabling `lazy loading` and preventing `Cumulative Layout Shift` without one.

You can check the [small app we'll be building](https://micaelnussbaumer.com/ie-angular-sample/), and the [GitHub repo](https://github.com/mnussbaumer/ie-angular-sample/tree/master). It's a very basic sample app to show you how to use the component and visualise the different fit types in relation to image size and container size. After we write our app you can also try out all other properties of the component by changing the input parameters used.

The slashed border around the image represents the size you pick from the size select, the image inside it is the actual image, displayed according to the type of fit you chose.

###Summary:
[Intro](#intro)
[Setting up the Angular Project](#setting-up-the-angular-project)
[Defining our Layout and Content](#defining-our-layout-and-content)
[Deploy to Github Pages](#deploy-to-github-pages)
[Create ImageEngine Engine](#create-imageengine-engine)
[Conclusion](#conclusion)

###Setting up the Angular Project

To follow along you'll need to have [Node.js](https://nodejs.dev), `NPM` (that comes with `node.js`), and [Angular CLI](https://angular.io/cli) installed.

The versions used for this tutorial are `npm` `7.15.1`, `node.js` `v16.3.0` and `Angular CLI` `12.1.1`.
To deploy on GitHub pages you'll also need a Github account and `git` installed, the version used here was `2.25.1`.

With those installed, from the command line run, (answering `N` to the prompt for `Angular Router` and choosing `CSS` for the styles):

```bash
ng new ie-angular-sample
cd ie-angular-sample
npm install @imageengine/angular
```
Now we need to add the `@imageengine/angular` module and the `Forms` module to our app imports.

Open `src/app/app.module.ts` and make sure it looks like this:

```js
import { NgModule } from '@angular/core';
import { BrowserModule } from '@angular/platform-browser';
import { FormsModule } from '@angular/forms';

import { AppComponent } from './app.component';
import { NgxImageengineModule } from "@imageengine/angular";

@NgModule({
  declarations: [
    AppComponent
  ],
  imports: [
      BrowserModule,
      NgxImageengineModule,
      FormsModule
  ],
  providers: [],
  bootstrap: [AppComponent]
})
export class AppModule { }
```

`FormsModule` is included in `angular` - we need it for form bindings - and `NgxImageengineModule` is the module exported from the `@imageengine/angular` package.

Now we're ready to use the image component in our app.
To save some space we're not going to show here the CSS we'll be using, because it has also a style reset sheet included, but make sure to copy it over from [styles.css](https://github.com/mnussbaumer/ie-angular-sample/blob/master/src/styles.css) (replace the contents of `src/styles.css` with those of that file).

###Defining our Layout and Content

Now let's edit `src/app/app.component.ts` to:

```js
import { Component, ChangeDetectorRef, AfterViewInit } from '@angular/core';
import { IEDirectives, IEFit } from "@imageengine/imageengine-helpers";

const OPTIONS = [
    [1920, 920],
    [960, 460],
    [480, 230],
    [240, 115]
];

const FITS: IEFit[] = [
    "stretch",
    "box",
    "letterbox",
    "cropbox"
];

@Component({
  selector: 'app-root',
  templateUrl: './app.component.html',
  styleUrls: ['./app.component.css']
})

export class AppComponent implements AfterViewInit {
    image_path: string = "assets/h-lightbox-3.jpeg";
    sizes: number[][] = [...OPTIONS];
    size: number = 3;
    fits: IEFit[] = [...FITS];
    fit: IEFit = "cropbox";
    width: string = `${OPTIONS[3][0]}px`;
    height: string = `${OPTIONS[3][1]}px`;
    src_url: string | null = "";

    constructor(private change_detector: ChangeDetectorRef) { }

    ngAfterViewInit(): void {
       this.set_src_url();
    }
    
    change_size(new_val: number): void {
        this.size = new_val;
        this.width = `${this.sizes[new_val][0]}px`;
        this.height = `${this.sizes[new_val][1]}px`;
        this.change_detector.detectChanges();
        this.set_src_url();
    }

    change_fit(new_val: IEFit): void {
        this.fit = new_val;
        this.change_detector.detectChanges();
        this.set_src_url();
    }

    set_src_url() {
        let img = document.querySelector(".ngx-ie-image");
        if (img) {
            this.src_url = img.getAttribute("src");
            this.change_detector.detectChanges();
        }
    }
}
```

We import some type helpers from [@imageengine/imageengine-helpers](https://www.npmjs.com/package/@imageengine/imageengine-helpers), this is a package that is a dependency of `@imageengine/angular` and contains the type specifications that the component uses plus some helpers that that package utilises (although we're not going to need them here).

Our component is very simple, we have a few properties we'll use to drive our UI, some functions to be used as callbacks (`change_size` and `change_fit`) and a helper (`set_src_url`) to get the url that was computed and in actual use by the component to fetch the asset.

Let's also edit `src/app/app.component.html` to:

```html
<div id="main-container" >
  <div class="image-holder" [style.minWidth]="width">
    <div style="display: flex; flex-flow: row wrap; width: 100%; justify-content: center; align-items: center;">
      <select [ngModel]="size" name="select-size" (ngModelChange)="change_size($event)">
          <option [value]="index" *ngFor="let opt of sizes, index as index">{{opt[0]}}px x {{opt[1]}}px</option>
      </select>
      <select [ngModel]="fit" name="select-fit" (ngModelChange)="change_fit($event)">
          <option [value]="opt" *ngFor="let opt of fits">{{opt}}</option>
      </select>
    </div>
    <p style="margin: 20px auto; color: white;">Generated URL: {{src_url}}</p>
    <ngx-imageengine [wrapper_styles]="{width: width, height: height}" [derive_size]="true" [path]="image_path" [directives]="{format: 'jpg', fit: fit, compression: 10, sharpness: 15}"></ngx-imageengine> 
  </div>
</div>
```
Besides the normal html and the `select`'s bound to the component properties, we have our `ngx-imageengine` component and our focus will be on that.

We won't cover everything about the component here, but in case you're curious just read the [package readme](https://www.npmjs.com/package/@imageengine/angular#accepted-inputs) as it provides a much more in depth overview of both the properties and details to make the best use of it. Here we'll explain some of the most common and useful properties of the component. 

Our web-app displays an image inside a rectangle element and allows us to set the size of this container rectangle and the fit we wish for our image (the way the image is placed and sized relative to the rectangle/sizes we define). It also shows us the resulting url generated to fetch that image taking into account the [directives](https://support.imageengine.io/hc/en-us/articles/360058880672-directives) we set from `ImageEngine`'s CDN. It also allows you to visualise how the type of `fit` affects the display of the image.


For the component properties we set:
- `wrapper_styles`
	The component itself renders a wrapper `div`, with an `img` tag inside of it. We can pass specific styles to be inlined both on the wrapper and `img` tag (and classes as well). Here we're only defining the `width` and `height` according to the properties of our component (that reflect the chosen option). This will make the wrapper itself assume these dimensions.
- `derive_size`
	We set this property to `true` to indicate that we want our component to automatically derive the desired sizes for the image from the size of the wrapper. Notice that this would also work if the wrapper was styled through normal CSS - in which case we wouldn't need to pass the `wrapper_styles` object.
- `path`
	The relative path to the image asset.
- `directives`
	This is an object specifying what directives we want to apply to our image when requesting it from `ImageEngine`'s CDN. In this case we're specifically setting the image format to `jpg`,  the `fit` to whatever is selected from the fit select input, a `compression` of 10 (interpreted as percentage, goes from 0 to 100, defaults to 0 when not specified) and `sharpness` of 15, to add a bit of sharpness to the image. All of these could be dynamically set as is the case with `fit`.

To see all options you can check the [@imageengine/angular readme](https://www.npmjs.com/package/@imageengine/angular)

Right now because we didn't specify a `host` from where to fetch the image the component will fetch it from its relative location on your app. If you visit the sample app or the `Github Repository` you might have noticed that the path includes `"/ie-loader-images/"` instead of `/assets/` but if you're following this tutorial that is irrelevant (it's related to the `Engine` and `CDN` I'm using for this example).

To recap - we set a size for the wrapper, we tell our component to derive the image size from the wrapper, and we give it a few directives to optimise our image. Notice we didn't have to pass `width` and `height` directives since we're using `derive_size`.

Before seeing our app in action, let's add the remaining missing things.
Edit `src/app/app.component.css` to:

```css
#main-container {
    width: 100vw;
    min-height: 100vh;
    background-color: transparent;
    padding-top: 50px;
}

.image-holder {
    background-color: transparent;
    min-width: 100vw;
    text-align: center;
}

.image-holder select {
    font-size: 18px;
    height: 40px;
    padding: 20px;
    font-weight: lighter;
    background-color: black;
    color: white;
}
```
Which is just some basic styling for our elements.

Let's also add our image, you can copy it from the [GitHub repo](https://github.com/mnussbaumer/ie-angular-sample/tree/master/src/ie-loader-images). 
Put it in the `src/assets` folder.

Once that is done we should be able to start our app with, from the command line on the root of our project:
`npm run start` 

And then visiting `localhost:4200`.

If you now change the settings from the selects you should see how that affects the image and the url. If you change on the `component` itself the directives being passed you will also see them reflected on the url and on the image.

One thing to take into consideration is the fact that we're using `device_pixel_ratio` conversions, so if you're visualising the app in a monitor with higher resolution than 1:1 (like a retina display) the sizes you see being encoded in the url will respect that and get the correct dimensions for the intrinsic size you want to display.

You can turn that off and there's other [properties](https://www.npmjs.com/package/@imageengine/angular) as well to help you make the most out of `ImageEngine` easily. 

This also means that some images might be requested at higher dimensions than your original images have. When this is the case `ImageEngine` won't apply crop directives, nor upscale the image and instead return the image in its original size (with other non-size related directives applied).

The component will take care of displaying them according to the chosen `fit` without you needing to do anything. You can see this by choosing 1920x920 as the size and `cropbox` `fit`. The original image is smaller in size than that, so `ImageEngine` will return it in its original dimensions without the `cropbox` factor we specified, but our component still displays it as the `cropbox` would look - because the original size is smaller than the final intrinsic size you might notice the image loose some quality. 

You'll also see, if you change the size to a bigger one and then back to a smaller one, that the component changes from the smaller to the bigger one, but then keeps the bigger one as long as it's bigger than the new selection. This can be turned off by using the `@Input` `force_size_recalculation` with `true`. 

It defaults to `false` because in practice , it means that if you have `responsive` and/or `derive_size` on, if the sizes change to a smaller one, instead of asking for a new image in those smaller sizes (that would need to be transferred) it keeps the sizing that's bigger - meaning it uses the already fetched image. Nonetheless if you're changing ratios between breakpoints for instance, and using `cropbox` you might actually want to force a refetch, in those cases you can turn that flag on, but in almost every case it's better, more performant to just keep `force_size_recalculation` as false.

One last note regarding the `directives` the component supports. Setting the `letterbox` colouring isn't possible directly through the component, but is easily achievable by just giving a `background-color` to the wrapper element and using the `box` fit method.

###Deploy to Github Pages

Angular has support for deploying on [GitHub pages](https://pages.github.com) with the help of a package named [angular-cli-ghpages](https://www.npmjs.com/package/angular-cli-ghpages).

We won't cover how to create a GitHub repo, but once you have that, add your repo as a remote:

`git remote add origin git@github.com:<YOUR_USERNAME>/<THE_NAME_OF_YOUR_REPO>.git`

 
And substitute the values between `<...>` by your username and the name you gave the repo.
Now let's add the `angular-cli-ghpages` with:

`ng add angular-cli-ghpages`

Followed by:

`ng deploy --base-href=/<THE_NAME_OF_YOUR_REPO>/`

(if you have a custom domain associated with your GitHub pages, you need to add the flag `cname` to the command, like `--cname=your_domain.com`)

Now you should be able to check the website online from `Github Pages`, the address should be `https://YOUR_GITHUB_USERNAME.github.io/THE_NAME_OF_YOUR_REPO`. 

You can see that it works even without an `host` `Engine` - obviously it won't do any real optimization to your assets until we add one.

###Create ImageEngine Engine

Since now we have our resources online, we are able to create a CDN distribution for it with `ImageEngine`. You can sign up for a free trial of `ImageEngine` by following the instructions on the following video and using the address of your newly created website.

{% vimeo 552953993 %}
[imageengine.io](https://imageengine.io)

Once that is done you should have an `ImageEngine` delivery address. We'll now add that to our component and re-deploy with the delivery address set. To do that change your component to have the property `host` set to whatever is the address you got previously.

```html
<ngx-imageengine [wrapper_styles]="{width: width, height: height}"
                 [derive_size]="true"
                 [path]="image_path"
                 [directives]="{format: 'jpg', fit: fit, compression: 10, sharpness: 15}"
                 host="http://YOUR-ADDRESS-IN-IE.cdn.imgeng.in/">
</ngx-imageengine>
```

(if you have a domain and are running `Github Pages` with HTTPS then you need to enable it on your `Image Engine` settings as well)

Notice we put a forward slash at the end of the `host address` we just added. This is because the paths to work in `Github Pages` (at least with `cname` and `base-href`) need to be absent of leading slashes to be relative but we need it when prepending the domain. In this case it's simpler to add it to the host address. 

And run again:
`ng deploy --base-href=/<THE_NAME_OF_YOUR_REPO>/`

###Conclusion

And that's it, now you can see that the generated URL includes the domain. Because it works without an `Engine` as well (without optimising the images obviously) you can run and test locally your app, assert on the generated urls, and confirm it's working as expected, besides actually seeing how the images will look like. You can do the same in a `staging` environment without setting up multiple `Engine`'s to deal with different origins.

To make it more production ready you would probably add an environment variable to control the `host` value, so that when building for production that is set and the `ImageEngine` CDN used, and when running locally it's not.

As you can see it's quite easy to add advanced image optimisations to your website. Exploring the potential of `ImageEngine`'s CDN and `Optimization Engine` allow you to drastically reduce your image's payloads, leading to a much leaner and snappier experience for your end-users.

With the aid of this component you can easily create fully maintainable layouts and designs, that only require editing of your CSS/styles to be updated when they change, provide automatically the exact best fit and size according to that styling, prevent `Cumulative Layout Shift`, have responsiveness to screen orientation/size changes, lazy-loading and are aware of your end-user screen pixel ratio.

Hope you find it useful!