Adding Animated Illustrations to an Angular App with Lottie (ngx-lottie)
Tags: angular, javascript, webdev, lottie
Published: 2022-05-02 18:56:02

<small>
**Note:** The concepts herein helped me better (and fully) understand Angular component re-usability through concepts like: data binding, component-in-component placement, `@Input()` decorators, and `@Output()` decorators. Hopefully it can help you in this same way too!
</small>

---

### Problems
* Since angular animations are meant for transitions / interactions, it's hard to get actual stop-motion graphics into the web app
* Since the framework is css-driven (doesn't play nice with html5 in general, let alone html5's `<canvas>` element), you don't get to utilize cool new tools like hype4 (for the life of me the 'iframe' route never seems to work)
* I'm not an After Effects professional, so the alternative of more abstracted tools is far more appealing.

### Solution & Workflow Assumptions
Here's how I got animations from Lottie's free library into a re-usable angular component using the `ngx-lottie` package and angular's data / event binding features. Note:
* This article assumes you have basic knowledge of JavaScript/TypeScript/Angular (with associated dependencies like Node installed)
* For speed, the templates were created utilizing my `@riapacheco/yutes` package on npm (feel free to use it -- instructions for install is in npm docs)
* The code from this demo is on my github, so feel free to fork it: https://github.com/riapacheco/lottie-demo

<small>
To learn more about Lottie, [read more from their official site](https://lottiefiles.com/)!
</small>

---

### Skip ahead
[Step 1: Add the File from Lottie](#step-1-add-the-file-from-lottie)
[Step 2: Create the View Structure and General Style](#step-2-create-the-view-structure-and-general-style)
[Step 3: Install Lottie Packages](#step-3-install-lottie-packages)
[Step 4: Add the Lottie Animation to Splash Screen Component](#step-4-add-the-lottie-animation-to-splash-screen-component)

[Make it Reusable](#make-it-reusable)
[Reusing in Another Component](#reusing-in-another-component)

---

# Step 1: Add the File from Lottie
Create a new angular app by running the following command (--skip-tests added so that we don't generate tests files with our components):
```bash
$ ng new lottie-demo --skip-tests
```

Head to LottieFiles.com and navigate to their Free Animations section.

Then, choose an animation and download it as the lottie JSON file ( saved this file as 'piggy-bank.json):
![Image description](https://ik.imagekit.io/fuc9k9ckt2b/Blog_Post_Images/Dev_to/downloadoflottie_TuQUr-057.png?ik-sdk-version=javascript-1.4.3&updatedAt=1651514966435)


Create a folder called 'lottie' inside your 'src/assets' folder and add the file to it:

![Image description](https://ik.imagekit.io/fuc9k9ckt2b/Blog_Post_Images/Dev_to/lottie-as-json_54Vx8Ivw6.png?ik-sdk-version=javascript-1.4.3&updatedAt=1651514966550)

---

# Step 2: Create the View Structure and General Style
Create a new component (`$ ng g c shared/splash-screen`) and replace all the content that's in the app.component.html file with the new components selector:
```html
<!-- app.component.html -->
<app-splash-screen></app-splash-screen>
```

Add the following structural scss to the `splash-screen.component.SCSS` file:
```scss
:host {  
  height: 100%;
  overflow: hidden;
  display: flex;
  flex-flow: column nowrap;
  align-items: center;
  justify-content: center;
  position: relative;
}
```

In the component's template (HTML file), add the following code:
```html
<div class="lottie-animation mt-10">  
  This is where the animation will go.
</div>

<div class="container-sm" style="text-align: center">  
  <div class="splash-text">
    <h1>
      Step 1
    </h1>

    Lorem ipsum dolor sit amet consectetur adipisicing elit. 
    Impedit eligendi obcaecati quisquam molestias animi dolores 
    vitae veniam ut provident repudiandae doloremque quae quo 
    vel, magnam labore eveniet natus fugiat. Aspernatur.  
  </div>

  <div class="button-div mt-3 mb-10">    
    <a class="btn btn-md accent light" style="margin: 1rem;">      
      Previous    
    </a>

    <a class="btn btn-md primary" style="margin: 1rem;">      
      Next    
    </a>  
  </div>
</div>
```

<small>
Note: all the styling classes here are simply classes used from my [@riapacheco/yutes package](https://www.npmjs.com/package/@riapacheco/yutes) so I wouldn't worry about them
</small>

This is how the view will render when you run `ng serve`:
![Where the animation will go](https://ik.imagekit.io/fuc9k9ckt2b/Blog_Post_Images/Dev_to/lottie-area-preview_j1TO-Y5Km.png?ik-sdk-version=javascript-1.4.3&updatedAt=1651514966543)

---

# Step 3: Install Lottie Packages
Install lottie with the following command (more details about this package can be found in their npm docs:
```bash
$ npm install lottie-web ngx-lottie
```

Add the lottie module to your app.module.ts file with an exported function. (note: don't forget to add the CommonModule from @angular/common, so that you can use customized directives)
```typescript
import { AppComponent } from './app.component';
import { AppRoutingModule } from './app-routing.module';
import { BrowserModule } from '@angular/platform-browser';
import { CommonModule } from '@angular/common';
import { NgModule } from '@angular/core';
import { SplashScreenComponent } from './shared/splash-screen/splash-screen.component';

// Add these two
import { LottieModule } from 'ngx-lottie';
import player from 'lottie-web';

// Export this function
export function playerFactory(): any {  
  return import('lottie-web');
}

@NgModule({  
  declarations: [    
    AppComponent,    
    SplashScreenComponent,    
    StepTwoComponent  
  ],  
  imports: [    
    BrowserModule,    
    AppRoutingModule,    
    CommonModule,
    // Add the module like so:    
    LottieModule.forRoot({ player: playerFactory }),  
  ],  
  providers: [],  
  bootstrap: [AppComponent]
})

export class AppModule { }
```

---

# Step 4: Add the Lottie Animation to Splash Screen Component
In the splash-screen.component.TS file, you have to add a variable that binds lottie's AnimationOptions (to reference the file path of your JSON file), and a new function that binds to the event (we'll see later in the template HTML file):
```typescript
export class SplashScreenComponent implements OnInit {

  // This is the option that uses the package's AnimationOption interface  
  options: AnimationOptions = {    
    path: '/assets/lottie/piggy-bank.json'  
  };  

  constructor() { }  

  ngOnInit(): void {  }

  // This is the component function that binds to the animationCreated event from the package  
  onAnimate(animationItem: AnimationItem): void {    
    console.log(animationItem);  
  }
}
```

Now in your template HTML file, you can replace the dummy text with the `<ng-lottie>` element and bind the options variable and the onAnimate event:
```html
<!--splash-screen.component.html-->
<div class="lottie-animation mt-10">
  <ng-lottie
    [options]="options" 
    (animationCreated)="onAnimate($event)">
  </ng-lottie>
</div>

<!--...more code...-->
```

Now when we run ng serve in our terminal, we'll see the animation appear in the browser:
![Rendered in Browser](https://ik.imagekit.io/fuc9k9ckt2b/Blog_Post_Images/Dev_to/area-after-placement_R4PglSbFFH.png?ik-sdk-version=javascript-1.4.3&updatedAt=1651514966574)

---

# Make it Reusable
You can make this component re-usable with Angular's @Input() and @Outputs() so that parent components in the future can feed data or recognize events.

Prepare the component by adding @Input() to the options variable (making it available to other component) and by adding new variables to represent the text rendered in the component. If we want to communicate that a click event happened to the parent, we add @Output() to a variable that emits the event like so:
```typescript
// splash-screen.component.ts
export class SplashScreenComponent implements OnInit {  

  @Input() options: AnimationOptions = {
    path: '/assets/lottie/piggy-bank.json'  
  };  
  @Input() titleText = 'Step 1';  
  @Input() stepParagraph = 'Lorem ipsum blah blah blah';  
  @Input() secondaryButtonText = 'Previous';  
  @Input() primaryButtonText = 'Next';  
  @Output() animationCreated = new EventEmitter();
  @Output() secondaryClick = new EventEmitter();  
  @Output() primaryClick = new EventEmitter();  

  constructor() { }  

  ngOnInit(): void {  }  

  onAnimate(animationItem: AnimationItem): void {   
    console.log(animationItem);    
    this.animationCreated.emit(animationItem);  
  }  

  onSecondaryClick(clickedSecondaryEvent): void {    
    this.secondaryClick.emit(clickedSecondaryEvent);
  }

  onPrimaryClick(clickedPrimaryEvent): void {    
    this.primaryClick.emit(clickedPrimaryEvent);  
  }
} 
```

Then we just bind the data from the component to the template with either square brackets (on an element) or curly braces for actual string interpolation; or we bind events using parenthesis on elements:
```html
<!--splash-screen.component.html-->
<div class="lottie-animation mt-10">
  <ng-lottie
    [options]="options" 
    (animationCreated)="onAnimate($event)">
  </ng-lottie>
</div>

<div
  class="container-sm"
  style="text-align: center">
  <div class="splash-text">
    <h1>      
      {{ titleText }}    
    </h1>    
    {{ stepParagraph }}  
  </div>
  <div
    class="button-div mt-3 mb-10">
    <a 
      class="btn btn-md accent light" 
      style="margin: 1rem;" 
      (click)="onSecondaryClick($event)">      
        {{ secondaryButtonText }}    
    </a>

    <a 
      class="btn btn-md primary" 
      style="margin: 1rem;" 
      (click)="onPrimaryClick($event)">      
        {{ primaryButtonText }}    
    </a>
  </div>
</div>
```

This then renders like this (notice the new words from the component template):
![Reusable component](https://ik.imagekit.io/fuc9k9ckt2b/Blog_Post_Images/Dev_to/lottie-render_Se13DMiIXw.png?ik-sdk-version=javascript-1.4.3&updatedAt=1651514966821)

# Reusing in Another Component
To show how we re-use this component, add another free animation to the assets/lottie folder we created earlier. I've added one and named it `chat.json`.
![chat.json in file](https://ik.imagekit.io/fuc9k9ckt2b/Blog_Post_Images/Dev_to/chat.json_cka2CbsC-.png?ik-sdk-version=javascript-1.4.3&updatedAt=1651516382143)

Create a new component called `step-two` by running the command $ ng g c views/step-two in your terminal

<small>
Note: For the sake of this demo we're pretending that we want to re-use the component by adding it to a newly generated component for every step of an onboarding process. This isn't advised (adds more work and lots more files than needed). Usually, you'd want to programmatically make it so variables change when items are clicked but stay within the same component. If you need help on how to do that just email me.
</small>

So that we can see the component in the browser, remove the contents of the app.component.html file (which is the splash-screen selector element) and replace it with the newly created step-two component's element:
```html
<!--app.component.html-->
<app-step-two></app-step-two>
```

Now you should see "step two works!" in your screen if you run ng serve which is the default text that comes with a component. Replace this text with the splash-screen selector element so that you instead see the original component we created:
```html
<!--step-two.component.html-->
<app-splash-screen></app-splash-screen>
```

To get access to the splash-screen component's inputs (without coming up errors at first) we'll create the variables inside the parent step-two component. The new 'stepTwoOptions' variable is now referencing the new 'chat.json' animation instead of the original. Notice that there aren't any input decorators or anything like that, since parent components shouldn't really know that a child component exists:
```typescript
import { Component, OnInit } from '@angular/core';
import { AnimationOptions } from 'ngx-lottie';

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

export class StepTwoComponent implements OnInit { 
  stepTwoOptions: AnimationOptions = {    
    path: '/assets/lottie/chat.json'  
  };  
  stepTwoTitle = 'Step 2';  
  stepTwoParagraph = 'This is the second step of your stuff!';  

  constructor() { }  
  ngOnInit(): void {  }  
  
  clickNext(clickEvent): void {    
    console.log(clickEvent);  
  }
}
```

So now, to link the variables together, we just need to bind the variables (like a ratio) to the splash-screen selector. In Angular, all square brackets represent data binding, and any parenthesis represent event binding:
```html
<!-- step-two.component.html -->
<app-splash-screen
  [options]="stepTwoOptions"  
  [titleText]="stepTwoTitle"  
  [stepParagraph]="stepTwoParagraph"  
  [secondaryButtonText]="'Cancel'"  
  [primaryButtonText]="'Save'"  >
</app-splash-screen>
```

Notice that the button texts don't actual bind to anything and I just added a string directly inside the template.

Together, this component renders like this:
![Step 2 Render](https://ik.imagekit.io/fuc9k9ckt2b/Blog_Post_Images/Dev_to/step2render_AoBQBVbyF.png?ik-sdk-version=javascript-1.4.3&updatedAt=1651516850542)




Ria