MEAN Stack



MEAN Stack

0 0


hack-a-stack-slides


On Github Yonet / hack-a-stack-slides

MEAN Stack

Slides: bit.ly/hack-a-stack

Oh hey, these are some notes. They'll be hidden in your presentation, but you can see them if you open the speaker notes window (hit 's' on your keyboard).

Install

Introduction

  • Name
  • Experience
  • Idea

Aysegul Yonet @AysegulYonet

Sr Software Engineer - Fullstack @A360

Teach/Speak/Code/Repeat

CTO AnnieCannons - AnnieCannons.com

Reminders

JS Review Slides

bit.ly/JS102

What is MEAN

Fullstack JavaScript

  • MongoDB
  • ExpressJS
  • AngularJS
  • NodeJS

What is Node.js

JS that runs on your computer. V8 engine. Can access files, listen to http request, access database... Utilities on your machine.

Check node version


node -v
v6.1.0
npm -v
3.8.9
node

			    
node starts a process.

Global Object


node
var a = 1;
global.a
1

			    
Compare to console.

Node Exercise

npm install -g learnyounode
learnyounode
				

Start our project

mkdir mean-app
cd mean-app
npm init -y
				

Express Server

Step 0: bit.ly/mean-step-0

Express

					npm i -S express
				
sudo chown -R $(whoami) "$HOME/.npm" npm -g install npm Check package.json
mkdir server
cd server
touch server.js
				
var express =  require('express');
var app = express();
				
app.get('/', function (req, res) {
  res.send('Hello World!');
});

app.listen(4200, function () {
  console.log('Example app listening on port 4200!');
});
				
res.sendFile('index.html');
app.get('/', function (req, res) {
  res.sendFile('client/index.html', {'root': './'});
});

app.listen(4200, function () {
  console.log('Example app listening on port 4200!');
});
				

Express Exercises

npm install -g expressworks
$ expressworks
				

MongoDB

MongoDB.com
					npm install mongodb --save
				

Data folder

Creata a data/db folder on your root folder.

					npm install mongodb --save
					which mongo
					cd /usr/local/bin/
					mongod
				
MacHD or C drive. cd into that folder and run mongod if permission issues sudo chown -R 'id -u' /data/db https://docs.c9.io/v1.0/docs/setup-a-database https://community.c9.io/t/setting-up-mongodb/1717

c9.io

MongoDB setup
mkdir data
echo 'mongod --bind_ip=$IP --dbpath=data --nojournal --rest "$@"' > mongod
chmod a+x mongod
./mongod
mongo
				
mongo
db
use myDataBase
show databases

				
mongo to start the shell db to see our databases
db.myDataCollection.insert({b: 1})
db.myDataCollection.find()
db["3test"].find()
db.getCollection("3test").find()
				
mongo to start the shell db to see our databases
db["3test"].insert({c: 3})
db["3test"].find()
db.getCollection("3test").find()
				
mongo to start the shell db to see our databases Why?

Mongoose

npm install mongoose --save
cd server
mkdir db
touch db.js
				
mongo to start the shell db to see our databases
npm install mongoose --save
cd server
mkdir db
touch db.js
				

Step-1

AngularJS

Angular 2?

  • The next major version of the Angular framework
  • Rather a development platform than just a framework
  • Mobile First, future ready, powered by open source community
  • Faster, better tooling, more flexible, ...

Angular 2 Goals

  • Built with mobile in mind
  • Flexible development, developers decide which language to use
  • Make it run in different environments
  • Be even faster, take advantage of immutables and observables
  • Simple, expressive and easier to reason about
  • ...

  • ES5
  • ES2015 - ES6
  • Classes
  • Modules
  • Arrow functions
  • TypeScript
  • Type Annotations
  • Decorators
  • Interfaces
  • RxJS

Classes

Syntactic sugar for JavaScript prototypes introduced in ES2015.

class Car {

  manufacturer:string;

  constructor(manufacturer:string = 'BMW') {
    this.manufacturer = manufacturer;
  }

  drive(miles:number) {}
}

let bmw = new Car();

Classes

Syntactic sugar for JavaScript prototypes introduced in ES2015.

class Car {

  manufacturer:string;

  constructor(manufacturer:string = 'BMW') {
    this.manufacturer = manufacturer;
  }

  drive(miles:number) {}
}

let bmw = new Car();
class Car { ... }

class Convertible extends Car {

}

let cabrio = new Convertible();

Modules

ES2015 brings a module system to the table that enables us to write modular code.

// Car.js

export class Car { ... }

export class Convertible extends Car {
 ...
}

Modules

ES2015 brings a module system to the table that enables us to write modular code.

// App.js

import {Car, Convertible} from 'Car';

let bmw = new Car();
let cabrio = new Convertible();

Type Annotations

Type annotations provide optional static typing. Applied using : T syntax

var height:number = 6;
var isDone:boolean = true;
var name:string = 'a360';

var list:number[] = [1, 2, 3];
var list:Array<number> = [1, 2, 3];

function add(x: number, y: number): number {
  return x+y;
}






class ContactsApp {

}


import {Component} from 'angular2/core';


@Component({
  selector: 'contacts-app',
  template: 'Hello World!'
})
class ContactsApp {

}

            

How to instantiate that component?

import {Component} from 'angular2/core';


@Component({
  selector: 'contacts-app',
  template: 'Hello World!'
})
class ContactsApp {

}


import {Component} from 'angular2/core';
import {bootstrap} from 'angular2/platform/browser';

@Component({
  selector: 'contacts-app',
  template: 'Hello World!'
})
class ContactsApp {

}

bootstrap(ContactsApp);
import {Component} from 'angular2/core';
import {bootstrap} from 'angular2/platform/browser';

@Component({
  selector: 'contacts-app',
  template: 'Hello World!'
})
class ContactsApp {

}

bootstrap(ContactsApp);

In a real world app, we put bootstrap() in a separate file to make the root component easier to test.

<html lang="en">
  <head>
    <meta charset="utf-8">
    <title>My first Angular 2 App!</title>
  </head>
  <body>



    <script src="..."></script>
  </body>
</html>
<html lang="en">
  <head>
    <meta charset="utf-8">
    <title>My first Angular 2 App!</title>
  </head>
  <body>

    Loading...

    <script src="..."></script>
  </body>
</html>

Bootstrapping Tasks

Angular performs the following tasks to bootstrap an application (simplified):

Upgrades located DOM element into Angular component Creates injector for the application Creates (emulated) Shadow DOM on component's host element Instantiates specified component Performs change detection

Demo →

Using directives

Directive Dependencies

Directive dependencies are defined in the directives property.


@Component({
  selector: 'contacts-app',
  template: ''

})
class ContactsApp {}

Directive Dependencies

Directive dependencies are defined in the directives property.

import {ContactsHeaderComponent} from '...';

@Component({
  selector: 'contacts-app',
  template: '',
  directives: [ContactsHeaderComponent]
})
class ContactsApp {}

Directive Dependencies

Directive dependencies are defined in the directives property.

import {ContactsHeaderComponent} from '...';

@Component({
  selector: 'contacts-app',
  template: '<contacts-header></contacts-header>',
  directives: [ContactsHeaderComponent]
})
class ContactsApp {}

Header Component

import {Component} from 'angular2/core';

@Component({
  selector: 'contacts-header',
  template: `
    <div class="some class">
      <h1>Contacts App</h1>
    </div>
  `
})
export class ContactsHeaderComponent {

}

Demo →

Exercise 2: Using Directives

Displaying Data

We can bind data to elements in HTML templates and Angular automatically updates the UI as data changes.

@Component({
  selector: 'contacts-app',
  template: 'Hello {{name}}'
})
class ContactsApp {
  name:string = 'Angular 2';
}
@Component({
  selector: 'contacts-app',
  template: 'Hello {{name}}'
})
class ContactsApp {
  name:string = 'Angular 2';
}

Let's display our first contact!

Defining a Contact

To enable type safety during compilation, we can define interfaces using the interface key word

interface Contact {
  id: Number;
  name: string;
  email: string;
  phone: string;
  ...
}
import {Contact} from './models/contact';

@Component(...)
class ContactsApp {







}
import {Contact} from '...';

@Component(...)
class ContactsApp {
  contact: Contact = {
    id: 1,
    name: 'Aysegul',
    email: 'chris@burdorf.io',
    image: 'path/to/image',
    ...
  }
}
@Component({
  selector: 'contacts-app',
  template: `
    
{{contact.name}}
` }) class ContactsApp { ... }
@Component({
  selector: 'contacts-app',
  template: `
    <div>
      <img [src]="contact.image">
      <span>
        {{contact.name}}
      </span>
    </div>
  `
})
class ContactsApp {
  ...
}

Exercise 3: Display first contact

Property Binding Explained

Props and Attrs

Attributes are always strings and only set up the initial value

<input value="Aysegul">
<img src="path/to/img.png">

Props and Attrs

Properties can be any value and changed imperatively at run-time

var input = document.querySelector('input');
input.value; // 'Aysegul'

var img = document.querySelector('img');
img.src; // 'path/to/img.png'

Props and Attrs

Properties and attributes values aren't always reflected automatically

var input = document.querySelector('input');
input.value; // 'Aysegul'

input.value = 'Pascal';
input.getAttribute('value'); // 'Aysegul'

Props and Attrs

Properties and attributes values aren't always reflected automatically

var input = document.querySelector('input');
input.value; // 'Aysegul'

input.value = 'Pascal';
input.getAttribute('value'); // 'Aysegul'

Property Binding

Angular 2 binds to element properties instead of attributes in order to work with any element

[] - syntax is the declarative way of writing to a property

Creating a list

var CONTACTS: Contact[] = [
  { id: 1, firstname: 'Aysegul', ...},
  { id: 2, firstname: 'Pascal', ...},
  { id: 3, firstname: 'Julie', ...},
  { id: 4, firstname: 'Igor', ...},
  ...
];
import {CONTACTS} from '...';

@Component({
  selector: 'contacts-app',
  template: `
    
` }) class ContactsApp { contacts: Contact[] = CONTACTS; }
import {CONTACTS} from '...';

@Component({
  selector: 'contacts-app',
  template: `
    <ul>
      <li>
        <!-- each contact goes here -->
      </li>
    </ul>
  `
})
class ContactsApp {
  contacts: Contact[] = CONTACTS;
}

Lists with ngFor

The NgFor directive instantiates a template once per item from an iterable.

<li *ngFor="#item of items">...</li>
  • * - Indicates template directive
  • items - Expression evaluating to collection
  • #item - Local variable for each iterator
@Component({
  selector: 'contacts-app',
  template: `
    <ul>
      <li>

        <!-- each contact goes here -->


      </li>
    </ul>
  `
})
class ContactsApp {
  ...
}
@Component({
  selector: 'contacts-app',
  template: `
    <ul>
      <li *ngFor="#contact of contacts">
        <img [src]="contact.image">
        <span>
          {{contact.name}}
        </span>
      </li>
    </ul>
  `
})
class ContactsApp {
  ...
}

Demo →

Exercise 4: Display list of contacts

Services and DI

Services

We can create services in Angular using ES2015 classes.

import {Injectable} from 'angular2/core';

@Injectable()
export class ContactsService {

  private contacts: Contact[] = CONTACTS;

  getContacts() {
    return this.contacts;
  }
}

Configuring the Injector

A component's providers accepts a list of providers to configure the injector.

@Component({
  selector: 'contacts-app',
  providers: [ContactsService]
  ...
})
class ContactsApp {}

Injecting Dependencies

Dependencies can be injected using TypeScript type annotations.

@Component(...)
class ContactsApp {

  contacts: Contact[];

  constructor(contactsService: ContactsService) {
    this.contacts = contactsService.getContacts();
  }
}

Injecting Dependencies

Dependencies can be injected using TypeScript type annotations.

@Component(...)
class ContactsApp {

  contacts: Contact[];

  constructor(contactsService: ContactsService) {
    this.contacts = contactsService.getContacts();
  }
}

Exercise 5: Services and DI

Component Routing

Components we have

Our application consists of three components:

  • ContactsApp - The root component that is being bootstrapped
  • ContactsList - A component to list contacts by provided data
  • ContactDetail - A component to show a contact's details

Adding Router providers

ROUTER_PROVIDERS configure the injector for router dependencies.

import {ROUTER_PROVIDERS} from 'angular2/router';

@Component({
  ...
  providers: [
    ContactsService,
    ROUTER_PROVIDERS
  ]
})
class ContactsApp {}

Adding Router providers

ROUTER_PROVIDERS configure the injector for router dependencies.

import {ROUTER_PROVIDERS} from 'angular2/router';

@Component({
  ...
  providers: [
    ContactsService,
    ROUTER_PROVIDERS
  ]
})
class ContactsApp {}

Configuring Routes

We use the @RouteConfig() decorator to configure routes for our application.

import {RouteConfig} from 'angular2/router';

@Component()
@RouteConfig([
  // route definition goes here
])
class ContactsApp {
  ...
}

Configuring Routes

We use the @RouteConfig() decorator to configure routes for our application.

import {RouteConfig} from 'angular2/router';

@Component()
@RouteConfig([
  // route definition goes here
])
class ContactsApp {
  ...
}

Route Definition

A Route Definition has a path, a component, and an optional name.

@RouteConfig([
  {
    path: '/',
    component: ComponentClass,
    name: 'ComponentName'
  }
])

@Component()
@RouteConfig([

])
class ContactsApp {
  ...
}
import {ContactListComponent} from '...';

@Component()
@RouteConfig([
  {path: '/', component: ContactListComponent, name: 'ContactList'}
])
class ContactsApp {
  ...
}

Displaying Components

<router-outlet> directive specifies a viewport where components should be loaded into.


@Component({
  selector: 'contacts-app',
  template: '...',

})
@RouteConfig()
class ContactsApp {...}

Displaying Components

<router-outlet> directive specifies a viewport where components should be loaded into.

import {ROUTER_DIRECTIVES} from 'angular2/router';

@Component({
  selector: 'contacts-app',
  template: '',
  directives: [ROUTER_DIRECTIVES]
})
@RouteConfig()
class ContactsApp {...}

Displaying Components

<router-outlet> directive specifies a viewport where components should be loaded into.

import {ROUTER_DIRECTIVES} from 'angular2/router';

@Component({
  selector: 'contacts-app',
  template: '<router-outlet></router-outlet>',
  directives: [ROUTER_DIRECTIVES]
})
@RouteConfig()
class ContactsApp {...}

Exercise 6: Route to first component

Links with RouterLink

RouterLink lets link to a specific part of our app.

<li *ngFor="#contact of contacts">

    <img src="{{contact.image}}">
    <span>
      {{contact.firstname}} {{contact.lastname}}
    </span>

</li>

Links with RouterLink

RouterLink lets link to a specific part of our app.

<li *ngFor="#contact of contacts">
  <a [routerLink]="['/ContactDetail', {id: contact.id}]">
    <img src="{{contact.image}}">
    <span>
      {{contact.firstname}} {{contact.lastname}}
    </span>
  </a>
</li>

Links with RouterLink

RouterLink lets link to a specific part of our app.

<li *ngFor="#contact of contacts">
  <a [routerLink]="['/ContactDetail', {id: contact.id}]">
    <img src="{{contact.image}}">
    <span>
      {{contact.firstname}} {{contact.lastname}}
    </span>
  </a>
</li>

RouterLink DSL

<a [routerLink]="['/ContactDetail', {id: contact.id}]">
@Component()
@RouteConfig([
  ...
  {
    path: '/contact/:id',
    component: ContactDetailComponent,
    name: 'ContactDetail'
  }
])
class ContactsApp {...}

RouterLink DSL

<a [routerLink]="['/ContactDetail', {id: contact.id}]">
@Component()
@RouteConfig([
  ...
  {
    path: '/contact/:id',
    component: ContactDetailComponent,
    name: 'ContactDetail'
  }
])
class ContactsApp {...}

RouterLink DSL

<a [routerLink]="['/ContactDetail', {id: contact.id}]">
@Component()
@RouteConfig([
  ...
  {
    path: '/contact/:id',
    component: ContactDetailComponent,
    name: 'ContactDetail'
  }
])
class ContactsApp {...}

Retrieving RouteParams

RouteParams is an injectable immutable map of parameters for the given route.

@Component(...)
class ContactDetailComponent {

  constructor(routeParams: RouteParams) {
    // ask for routeParams.get('id')
  }
}

Retrieving RouteParams

RouteParams is an injectable immutable map of parameters for the given route.

@Component(...)
class ContactDetailComponent {

  constructor(routeParams: RouteParams) {
    // ask for routeParams.get('id')
  }
}

Exercise 7: Using RouteParams

Two-way Data Binding

NgModel Directive

NgModel implements two-way data binding by providing and combining property and event bindings.

<input [(ngModel)]="name">

Hello, {{name}}!

Or, the canonical version:

<input bindon-ngModel="name">

Hello, {{name}}!

Two-way Binding can be implemented like this:

<input [value]="name"
  (input)="name=$event.target.value">

ngModel hides repetitive expressions:

<input [ngModel]="name"
  (ngModelChange)="name=$event">

Shorthand syntax:

<input [(ngModel)]="name">

Two-way Binding can be implemented like this:

<input [value]="name"
  (input)="name=$event.target.value">

ngModel hides repetitive expressions:

<input [ngModel]="name"
  (ngModelChange)="name=$event">

Shorthand syntax:

<input [(ngModel)]="name">

Simple Forms

Using ngModel we can already create simple forms.

<form>

  <label>Firstname:</label>
  <input [(ngModel)]="contact.firstname">

  <label>Lastname:</label>
  <input [(ngModel)]="contact.lastname">
  ...
</form>

Exercise 8: Two-way Data Binding with ngModel

Fetching Data

Http in Angular 2

Angular 2 introduces a completely redesigned Http layer based on Observables.

  • Event-driven stream of notifications
  • Enables functional programming structures
  • Allows to transform to promises if needed

Configuring the Injector

HTTP_PROVIDERS configure the injector for http dependencies

import {HTTP_PROVIDERS} from 'angular2/http';
import 'rxjs/add/operator/map';

@Component({
  ...
  providers: [HTTP_PROVIDERS]
})
export class ContactsApp {...}

Configuring the Injector

HTTP_PROVIDERS configure the injector for http dependencies

import {HTTP_PROVIDERS} from 'angular2/http';
import 'rxjs/add/operator/map';

@Component({
  ...
  providers: [HTTP_PROVIDERS]
})
export class ContactsApp {...}

Configuring the Injector

HTTP_PROVIDERS configure the injector for http dependencies

import {HTTP_PROVIDERS} from 'angular2/http';
import 'rxjs/add/operator/map';

@Component({
  ...
  providers: [HTTP_PROVIDERS]
})
export class ContactsApp {...}

Http Service

Http is an injectable class with methods to perform http requests.

import {Http} from 'angular2/http';
import {Injectable} from 'angular/core';

@Injectable()
class ContactsService {
  constructor(private http: Http) {

  }
}

Http Service

Http is an injectable class with methods to perform http requests.

import {Http} from 'angular2/http';
import {Injectable} from 'angular/core';

@Injectable()
class ContactsService {
  constructor(private http: Http) {

  }
}

Http Service

Http is an injectable class with methods to perform http requests.

import {Http} from 'angular2/http';
import {Injectable} from 'angular/core';

@Injectable()
class ContactsService {
  constructor(private http: Http) {

  }
}

Http and Observables

Http returns an Observable which will emit a single response it's received.

class ContactsService {
  ...
  getContacts() {
    return this.http.get('http://myapi.com/contacts')
      .map((res) => { return res.json(); })
      .map((data) => { return data.items; });
  }
}

Http and Observables

Http returns an Observable which will emit a single response it's received.

class ContactsService {
  ...
  getContacts() {
    return this.http.get('http://myapi.com/contacts')
      .map((res) => { return res.json(); })
      .map((data) => { return data.items; });
  }
}

Http and Observables

Http returns an Observable which will emit a single response it's received.

class ContactsService {
  ...
  getContacts() {
    return this.http.get('http://myapi.com/contacts')
      .map((res) => { return res.json(); })
      .map((data) => { return data.items; });
  }
}

Http and Observables

Http returns an Observable which will emit a single response it's received.

class ContactsService {
  ...
  getContacts() {
    return this.http.get('http://myapi.com/contacts')
      .map(res => res.json())
      .map(data => data.items);
  }
}

Subscribing to Http

We can subscribe to Observables using .subscribe()

@Component(...)
class ContactsListComponent {

  constructor(contactsService: ContactsService) {
    contactsService.getContacts()
      .subscribe(contacts => this.contacts = contacts);
  }

}

Subscribing to Http

We can subscribe to Observables using .subscribe()

@Component(...)
class ContactsListComponent {

  constructor(contactsService: ContactsService) {
    contactsService.getContacts()
      .subscribe(contacts => this.contacts = contacts);
  }

}

Safe Navigation Operator

Angular throws a null reference error when null objects are accessed:

The null contact's name is {{nullContact.firstname}}

Results in:

TypeError: Cannot read property 'firstname' of null in [null]

Safe Navigation Operator

The "safe navigation" operator (?.) is a convenient way to guard against nulls in property paths

The null contact's name is {{nullContact?.firstname}}

Safe Navigation Operator

The "save navigation" operator (?.) is a convenient way to guard against nulls in property paths

The null contact's name is {{nullContact?.firstname}}

Exercise 9: Fetching data using Http

Posting Data

Put Requests

Http.put() performs a request with the put method

class ContactsService {
  ...
  updateContact(contact: Contact) {
    let url = `myapi.com/contacts/{$contact.id}`;
    let data = JSON.stringify(contact);
    let headers = new Headers({...});

    return this.http.put(url, data, { headers });
  }
}

Put Requests

Http.put() performs a request with the put method

class ContactsService {
  ...
  updateContact(contact: Contact) {
    let url = `myapi.com/contacts/{$contact.id}`;
    let data = JSON.stringify(contact);
    let headers = new Headers({...});

    return this.http.put(url, data, { headers });
  }
}

Put Requests

Http.put() performs a request with the put method

class ContactsService {
  ...
  updateContact(contact: Contact) {
    let url = `myapi.com/contacts/{$contact.id}`;
    let data = JSON.stringify(contact);
    let headers = new Headers({...});

    return this.http.put(url, data, { headers });
  }
}

Put Requests

Http.put() performs a request with the put method

class ContactsService {
  ...
  updateContact(contact: Contact) {
    let url = `myapi.com/contacts/{$contact.id}`;
    let data = JSON.stringify(contact);
    let headers = new Headers({...});

    return this.http.put(url, data, { headers });
  }
}

Put Requests

Http.put() performs a request with the put method

class ContactsService {
  ...
  updateContact(contact: Contact) {
    let url = `myapi.com/contacts/{$contact.id}`;
    let data = JSON.stringify(contact);
    let headers = new Headers({...});

    return this.http.put(url, data, { headers });
  }
}

Put Requests

Http.put() performs a request with the put method

class ContactsService {
  ...
  updateContact(contact: Contact) {
    let url = `myapi.com/contacts/{$contact.id}`;
    let data = JSON.stringify(contact);
    let headers = new Headers({...});

    return this.http.put(url, data, { headers });
  }
}

Exercise 10: Sending data using Http

Using Async Pipe

Pipes

A pipe takes in data as input and transforms it to a desired output.

@Component({
  selector: 'contact-detail',
  template: `
    ...
    Birthday:
    {{contact?.dateOfBirth | date}}
  `
})
class ContactDetailComponent { }

Pipes

A pipe takes in data as input and transforms it to a desired output.

@Component({
  selector: 'contact-detail',
  template: `
    ...
    <span>Birthday:</span>
    <span>{{contact?.dateOfBirth | date}}</span>
  `
})
class ContactDetailComponent { }

AsyncPipe

The Async pipe can receive a Promise or Observable as input and subscribe to the input automatically.

import {Observable} from 'rxjs/Observable';

@Component(...)
class ContactsListComponent {

  contacts: Observable<Array<Contact>>;

  constructor(contactsService: ContactsService) {
    this.contacts = contactsService.getContacts();
  }
}

AsyncPipe

The Async pipe can receive a Promise or Observable as input and subscribe to the input automatically.

import {Observable} from 'rxjs/Observable';

@Component(...)
class ContactsListComponent {

  contacts: Observable<Array<Contact>>;

  constructor(contactsService: ContactsService) {
    this.contacts = contactsService.getContacts();
  }
}

AsyncPipe

The Async pipe can receive a Promise or Observable as input and subscribe to the input automatically.

@Component({
  template: `
    <ul>
      <li *ngFor="#contact of contacts | async">
        ...
      </li>
    </ul>
  `
})
class ContactsListComponent {...}

AsyncPipe

The Async pipe can receive a Promise or Observable as input and subscribe to the input automatically.

@Component({
  template: `
    <ul>
      <li *ngFor="#contact of contacts | async">
        ...
      </li>
    </ul>
  `
})
class ContactsListComponent {...}

THE END

Thank you for your attention!