Spice up food ordering with AngularFire

Everyone needs food sometimes and when it comes to ordering food at bigger companies for many people you do not want to handle food orders from different people through E-Mail.

Think about an App where people can type in at which restaurant they want to order and other people can add their meal so you can bundle your orders for every restaurant.

So normally you would start by setting up your MEAN stack or whatever you like and set up your solution folder and configure everything. But that usually takes some time.

Firebase to the rescue!

Firebase is a Google service you can start with for free. It offers many features like a Realtime Database, Authentication services, Cloud Storage and hosting. And if you still need some server code Firebase offers a feature named Cloud Functions

In this Tutorial I will show you how to make an Angular App with all the hot stuff like

  • Firebase for storing our data
  • Angular Cli to help us developing our Angular app
  • Angular Material to make our app look good

Setting up Firebase

To set up our Firebase environment go to https://firebase.google.com and login with your Google account or register a new account.

Now you can create your new project. You only have to choose a name and a region.
That’s it!

This is everything you need to be ready to use Firebase.

Because we will not have any authentication in this part, we need to customize the rules a bit. I will cover authentication in the next tutorial.

Go to “Database”. This is where you can see your stored objects and click on the tab “Rules”

This is where you can manage the rules for your Database. Because we do not want to care about authentication in this tutorial we can disable it like this

{
“rules”: {
“.read”: “true”,
“.write”: “true”
}
}

Now we can start to develop our Angular App.

What we going to create

We are going to create a simple app where you can tell your colleagues where you want to order food. Everyone will be able to add their meal to the order and if you actually ordered the food at the restaurant, you can complete the order.

What you need

Install Angular cli. This is a tool developed by a Google team. It helps us to create a project with the recommended folder structure, a working webpack configuration and other things.

npm install -g @angular/cli

Now go into the folder where you want to create your app and type in your terminal

ng new PROJECT_NAME --routing

this creates a new angular project with routing files.

to run the app simply type

cd PROJECT_NAME
ng serve

Next we will need something to style our app. I have chosen Angular Material for this. https://material.angular.io/

npm install --save @angular/material

in order to make Angular Material work, add the stylesheet to the “angular-cli.json” file

"styles": [
"../node_modules/@angular/material/core/theming/prebuilt/indigo-pink.css",
"styles.css"
],

and if you want to use Material icons, add

<link href="https://fonts.googleapis.com/icon?family=Material+Icons" rel="stylesheet">

To index.html found in the folder “src”.

At last install AngularFire to help our App communicate with Firebase.

npm install firebase angularfire2 --save

Follow the instructions in part 4 of the documentation https://github.com/angular/angularfire2/blob/master/docs/1-install-and-setup.md

Now with everything set up, you are ready to go.

Building our app

Creating the structure

At first we will create our files by using angular-cli. So go to the root of the project at open your terminal.

First create our Order module with a component so we have our application in a logical structure. Maybe we want to create a User Module later to handle login and registration. Our order module needs routing too so give it the “routing” flag.

ng g module order --routing

cd into your order module and create a root component for your module

ng g component order

And create the rest of the components for the order module

ng g component /order/add-meal --module order/order.module
ng g component /order/add-order --module order/order.module
ng g component /order/order-detail --module order/order.module

Lets create a service to handle our Firebase objects

ng g service /order/services/order — module order/order.module

And we need Models for our objects in the database. Because our Models sometimes have the same properties, like “$key” the unique identifier of a Firebase object, we add a base-model which we can inherit from.

ng g class /common/baseModel
ng g class /order/models/order
ng g class /order/models/meal

Your folder structure should look like this now

Folder structure

Implementing the magic

The Models

At first, let’s the define the base-model. Because we need a $key property in our order object and maybe in other objects in the future we are going to define it here like this

export class base-model {
public $key: string;
}

next we want to create our meal model. A meal needs at first just a name and maybe an info for the meal. For example:

name: Ceasar Salad
info: some Dressing

export class Meal {
public name: string;
public info: string;
constructor () {
this.name = '';
this.info = '';
}
}

The last model we need for our first steps is the order model.

export class Order extends base-model {
public where: string;
public when: string;
public description: string;
public url: string;
public meals: Meal[];
public completed: boolean;
  constructor () {
super();
this.where = '';
this.when = '';
this.description = '';
this.url = '';
this.meals = [];
this.completed = false;
}
}

You will notice that our Order class extends from our base-model. This means the order class has every property the base-model class has.

Now we have a Model to enable our users store

where the user wants to order food
when the user wants to order food
a description of the restaurant
the url of the restaurant to enable other users pick food
the meals the users want to order
and a completed flag if the order is actually ordered at the restaurant.

Talking with Firebase

After creating our Models, we want to create the service which gives us the data we need in our application.

To enable our service to communicate with the configured Firebase Database, we need to import the the following modules

import { 
FirebaseListObservable,
AngularFire,
FirebaseObjectObservable
}
from ‘angularfire2’;

We are going to define our orders property inside the service class and inside the constructor, we assign a Firebase list to it.

public orders: FirebaseListObservable<Order[]>;
constructor(private af: AngularFire) {
this.orders = af.database.list('/orders');
}

You can see that AngularFire2 uses the new Observable objects from NgRx in Angular. NgRx is the module for reactive extensions in Angular.

Now we got our orders list, we can write our first method in the server.

getAll() {
return this.orders;
}

this method returns all orders. It is as simply as it looks.

Before you can get something of a list you want to add some objects into it. This method will do that.

add(order: Order) {
this.orders.push(order);
}

As soon as you push an object into the observable, the list in the database gets updated and because the orders object is a observable, the clients get updated too.

Of course you can get a single order too if you pass the $key.

getByKey(key: string) {
return this.af.database.object(`/orders/${key}`);
}

The complete service could look like this. I implemented a method to add a meal and to mark a order as ordered.

export class OrderService {
public orders: FirebaseListObservable<Order[]>;
constructor(private af: AngularFire) {
this.orders = af.database.list('/orders');
}
getAll() {
return this.orders;
}
add(order: Order) {
this.orders.push(order);
}
getByKey(key: string) {
return this.af.database.object(`/orders/${key}`);
}
addMeal(order: Order, meal: Meal) {
let orderToUpdate: FirebaseObjectObservable<Order> = this.getByKey(order.$key);
let meals = [];
orderToUpdate.subscribe(data => {
if (!data.meals) {
data.meals = [];
}
meals = data.meals;
});
meals.push(meal);
orderToUpdate.update( { meals: meals } );
}
completeOrder(order: Order) {
let orderToUpdate = this.getByKey(order.$key);
orderToUpdate.update( { completed: true } );
}
}

Take the magic to the user (Components)

At first we will define the first component the user is going to see at this moment. The order.component.

We are going to use the Material Dialogs for this. We create the the component with references to our service and not the dialog service of Angular Material and of course a orders object which gets filled by the orderService.

public orders: FirebaseListObservable<Order[]>;
constructor(public dialog: MdDialog, private orderService: OrderService) {
}

If you create a component with angular-cli it automatically implement a ngOnInit function. If you need to load some data if the component is initialized, this is the right place. Because we want our component to load all orders on start we call our service here

ngOnInit() {
this.orders = this.orderService.getAll();
}

we need three dialog functions in our component.

The first one is the detail view of one order. We need to inject our created OrderDetailComponent created with angular-cli and some data to the dialog.

openOrderDetail(order: Order) {
this.dialog.open(OrderDetailComponent, { data: order });
}

the other two dialogs are similar to this. The finished component could look like this to enable the basic functionality

@Component({
selector: 'app-order',
templateUrl: './order.component.html',
styleUrls: ['./order.component.css']
})
export class OrderComponent implements OnInit {
public orders: FirebaseListObservable<Order[]>;
constructor(public dialog: MdDialog, private orderService: OrderService) {
}
ngOnInit() {
this.orders = this.orderService.getAll();
}
openOrderDialog() {
this.dialog.open(AddOrderComponent);
}
openMealDialog(order: Order) {
this.dialog.open(AddMealComponent, { data: order });
}
openOrderDetail(order: Order) {
this.dialog.open(OrderDetailComponent, { data: order });
}
}

To present our orders to the user i will use the Google Material Design.

You can look at the different components here https://material.angular.io/

Lets take a look at this step by step

<md-card *ngFor="let order of orders | async"  fxLayout="column" fxFlex="20" class="orderCard">

we display each of our orders in a md-card material component. In Angular 1.x this was done by using ng-repeat. This is now replaced by this *ngFor but basically it does the same. You maybe noticed the “async” pipe. This is important for our Firebase objects. This pipe will subscribe to the observable for you and will change if the value changes. You can do this with custom observables too, not just Firebase objects.

The other two attributes are used by the Angular Flex Layout (https://github.com/angular/flex-layout) module to display our md-card correctly.

next part is very basic.

<md-card-header>
<md-card-title>{{order.where}} <small *ngIf="order.completed" class="green">ordered!</small></md-card-title>
<md-card-subtitle>{{order.when}}</md-card-subtitle>
</md-card-header>

You will notice another asterisk attribute the *ngIf. This is to just show out “ordered!” message if the order is completed. In Angular version 4 there is a possibility to use “else” too.

Another thing i want to bring out is how Angular attaches to events and attributes of a HTML Component. We look at our actions here.

<md-card-actions>
<button md-raised-button color="primary" [disabled]="order.completed" (click)="openMealDialog(order)">add meal</button>
<button md-button (click)="openOrderDetail(order)">view order</button>
</md-card-actions>

the “color” attribute from Angular Material but the other like [disabled] is the basic HTML attribute. The square brackets around that tells us that the property will change inside the view.
Whereby the (click) attribute is something that will “change” something in our component. So that means you got no standard two way binding anymore in Angular.

If you still want two way binding you can use it like this:

[(ngModel)]="order.where"

Our finished view of the order component will could look something like this:

<button md-fab id="add" (click)="openOrderDialog()"><md-icon>add</md-icon></button>
<div fxLayout="row" style="flex-wrap: wrap">
<md-card *ngFor="let order of orders | async" fxLayout="column" fxFlex="20" class="orderCard">
<md-card-header>
<md-card-title>{{order.where}} <small *ngIf="order.completed" class="green">ordered!</small></md-card-title>
<md-card-subtitle>{{order.when}}</md-card-subtitle>
</md-card-header>
<md-card-content class="cardContent">
<p>
{{order.description}}
</p>
<a md-button [href]="order.url" target="_blank">open shop</a>
</md-card-content>
<md-card-actions>
<button md-raised-button color="primary" [disabled]="order.completed" (click)="openMealDialog(order)">add meal</button>
<button md-button (click)="openOrderDetail(order)">view order</button>
</md-card-actions>
</md-card>
</div>

The best thing about that approach with Firebase and Angular is that the other components are not even worth to mention because they just repeat itself. 
You can build a basic functioning Web Application in about 4 hours and you don’t have to crack your head for Server maintaining or anything else.

If you are interested in the full code visit the github repo here

I will continue to develop it further and blog about it. The next thing i am going to write about is how you add authentication to the luncher app so stay tuned. And please leave a comment how you like my very first blog post. Every opinion is welcome!

Thanks for reading,

Florian