Plan Your Next Party with an Angular Invite App

Plan Your Next Party with an Angular Invite App
Welcome! Angular is Google's open source framework for crafting high-quality front-end web applications. r/Angular2 exists to help spread

So, you’re planning a party, and you want to write a small app to help you manage all the invites. Akita to the rescue!

What Is Akita?

Akita is a state management pattern, built on top of RxJS, which takes the idea of multiple data stores from Flux and the immutable updates from Redux, along with the concept of streaming data, to create the Observable Data Stores model.

Those are a lot of complex concepts, but at the end of the day Akita encourages simplicity. It saves you the hassle of creating boilerplate code and offers powerful tools with a moderate learning curve, suitable for both experienced and inexperienced developers alike.

Getting Started — The Model

The first thing we’ll need to do is think of what we want in the model of the entity we’ll create. It represents a person who’s invited to party, so we’ll need a name field, a unique ID field (generated automatically by Akita in the creator function we add), and a boolean indicating whether that person has RSVPed. We’ll also throw in a numerical field indicating how many additional guests that person will arrive with.

import { ID, guid } from '@datorama/akita';


export type Person = {
  id: ID;
  name: string;
  attending: boolean;
  guests: number;
};

export type ViewPeople = {
  invited: number;
  invitees: Person[],
  RSVPed: number;
  totalWithGuests: number;
}

export function createPerson(name: string): Person {
  return {
    id: guid(),
    name,
    guests: 0,
    attending: false
  };
}

004-01.ts
ViewPeople is another type we’ve added — it will be used in our query to describe the type of data we want to query from the store.

The Store

Next, we can use the Akita Schematics to create an entity store. The entities in this case will be the people we invite to the party.

import { Person } from './person.model';
import { EntityState, EntityStore, StoreConfig } from '@datorama/akita';
import { Injectable } from '@angular/core';

export interface PeopleState extends EntityState<Person> {
  ui: {
    filter: string;
  };
}

const initialState = {
  ui: {
    filter: 'SHOW_ALL'
  }
};

@StoreConfig({ name: 'people' })
export class PeopleStore extends EntityStore<PeopleState, Person> {
  constructor() {
    super(initialState);
  }

  updateFilter(filter: string) {
    this.updateRoot({ ui: { filter } });
  }
}

004-02.ts
As you can see, we don’t need to add much — all the boilerplate fuctions for handling the data are built-in as part of the Akita entity store. We’re only going to add a ui filter which will let us filter out invitees based on their status (all invitees/those who RSVPed/those who RSVPed and have additional guests arriving with them).

The People Service

Next, we’ll create a service in order to populate the store. It’ll call the built-in store method in order to add or remove people, and will also have method to alter their status once they’ve RSVPed or notified they’re bringing guests along.

import { ID, increment, decrement } from '@datorama/akita';
import { Injectable } from '@angular/core';
import { PeopleStore } from './people.store';
import { createPerson } from './person.model';

export class PeopleService {
  constructor(private peopleStore: PeopleStore) { }

  addPerson(name: string) {
    this.peopleStore.add(createPerson(name));
  }

  removePerson(id: ID) {
    this.peopleStore.remove(id);
  }

  addGuest(id: ID) {
    this.peopleStore.update(id, person => ({
      guests: increment(person.guests)
    }));
  }

  removeGuest(id: ID) {
    this.peopleStore.update(id, person => ({
      guests: decrement(person.guests)
    }));
  }

  updateAttending(id: ID) {
    this.peopleStore.update(id, person => ({ attending: !person.attending }));
  }

  updateFilter(filter: string) {
    this.peopleStore.updateFilter(filter);
  }
}

004-03.ts

The People Query

To get the stats on our party guests, we’ll create an entity query with a method that uses the rxjs method combineLatest() to give us all the stats we need — the number of people we invited, how many RSVPed, and how many people we should expect in total (attendees plus their guests).

import { Person } from './person.model';
import { QueryEntity } from '@datorama/akita';
import { PeopleStore, PeopleState } from './people.store';
import { Injectable } from '@angular/core';
import { map, filter } from 'rxjs/operators';
import { combineLatest } from 'rxjs';

export class PeopleQuery extends QueryEntity<PeopleState, Person> {
  selectFilter$ = this.select(state => state.ui.filter);

  constructor(protected store: PeopleStore) {
    super(store);
  }

  selectPeople$ = combineLatest(this.selectFilter$, this.selectAll()).pipe(
    map(([filter, people]) => ({
      invited: people.length,
      invitees: this.partyFilter(filter, people),
      RSVPed: this.partyFilter('SHOW_ATTENDING', people).length,
      totalWithGuests: this.partyFilter('SHOW_ATTENDING', people).reduce((acc, curr) => acc + curr.guests + 1, 0)
    }))
  );

  partyFilter(filter: string, people: Person[]) {
    switch (filter) {
      case 'SHOW_ATTENDING':
        return people.filter(person => person.attending);
      case 'SHOW_ALL':
        return people;
      case 'SHOW_WITH_GUESTS':
        return people.filter(person => person.attending && person.guests);
    }
  }
}

004-04.ts

GUI Components

Finally, it’s time to create some components to let us add or remove invitees and update their status.

import { Person } from './../state/person.model';
import { Component, OnInit } from '@angular/core';
import { PeopleQuery } from '../state/people.query';
import { Observable } from 'rxjs';
import { PeopleService } from '../state/people.service';
import { ID } from '@datorama/akita';
import { ViewPeople } from '../state/person.model';

@Component({
  selector: 'app-party-planner',
  templateUrl: './party-planner.component.html'
})
export class PartyPlannerComponent implements OnInit {
  selectPeople$: Observable<ViewPeople>;

  constructor(
    private peopleQuery: PeopleQuery,
    private peopleService: PeopleService
  ) { }

  ngOnInit() {
    this.selectPeople$ = this.peopleQuery.selectPeople$;
  }

  addPerson(name: string) {
    this.peopleService.addPerson(name);
  }

  removePerson(id: ID) {
    this.peopleService.removePerson(id);
  }

  addGuest(id: ID) {
    this.peopleService.addGuest(id);
  }

  removeGuest(id: ID) {
    this.peopleService.removeGuest(id);
  }

  updateAttending(id: ID) {
    this.peopleService.updateAttending(id);
  }

  updateFilter(filter: string) {
    this.peopleService.updateFilter(filter);
  }
}

004-05.ts

<ng-container *ngIf="(selectPeople$ | async) as people">

	<party-stats [invited]="people.invited" [rsvped]="people.RSVPed" [totalWithGuests]="people.totalWithGuests"></party-stats>

	<party-filter (updateFilter)="updateFilter($event)"></party-filter>

	<person-input (addPerson)="addPerson($event)"></person-input>

	<person-list [people]="people.invitees" (addGuest)="addGuest($event)" (removeGuest)="removeGuest($event)" (removePerson)="removePerson($event)"
	 (toggleAttending)="updateAttending($event)">
	</person-list>

</ng-container>

**004-06.html **

In summary: The Akita entity store is a great place to start when you need to manage multiple entities and their states. A lot of the functionality you’ll need is built-in to the entity store and the entity query.

Here are some more articles about

Angular 2 Demystified

Learn Angular 2 from Beginner to Advanced

Angular 2 Firebase - Build a Web App with Typescript

Master Angular 2 - The No Nonsense Course

Angular 2 - The Complete Guide

Suggest:

Angular Tutorial - Learn Angular from Scratch

Web Development Tutorial - JavaScript, HTML, CSS

Javascript Project Tutorial: Budget App

Test Driven Development with Angular

JavaScript Programming Tutorial Full Course for Beginners

E-Commerce JavaScript Tutorial - Shopping Cart from Scratch