Angular vs other frameworks: Creating a simple app.

It is very important to choose the right framework for your project, because frameworks have pros and cons and some fit various project and teams differently. This post focuses on Angular framework, by showing you how to create a simple app, and compares the process with creating an app with 2 other frameworks: React and Vue.

There are basically 2 types of Angular framework: AngularJS (basically version 1, and patches, and it is the first Angular framework created) and Angular (basically version 2 and over, and it is a successor to AngularJS framework).

New Angular framework offers improvement over the AngularJS. It is still a complete, all things included solution for creating a modern web application. It drops some of the old stuff, notably $scope, improves performance in big applications, by removing dirty checking mechanism. It Uses TypeScript for writing logic of applications, and is based on components.

I am going to show you how to create a simple application and compare the process and concepts with other frameworks, I have experience with, notably React and Vue.

Every major framework these days that cares about developer experience, has some sort of scaffolding application, that creates a starting application structure. Angular is not an exception, and this tool is called Angular CLI.

While other frameworks have similar tools for scaffolding a project, angular CLI can do things beyond just starting a project. It can create classes, directives, components and more. This is especially useful for beginners as adding a component to angular may not as straightforward as in other frameworks, you need to add some code to a class, decorated by NgModule. If you use some powerful IDE, there should probably be an existing plugin that allows you to use the CLI with GUI.

The CLI takes care of a lot of stuff for you, and as you code you can learn how to integrate a component on your own. This feels like coding in some Java IDE, when IDE takes care of the boilerplate needed to create a class. You can install add-ons for your IDE/text editor to create components for other framework it is nice to have an official tool for that to go along with a framework.

To use it, you should install it globally with npm. The command looks like this:

npm install -g @angular/cli

It allows you to use either npm or yarn for fetching dependencies (I personally prefer yarn, which works way faster than npm). Here is how to switch package managers:

ng set --global packageManager=yarn

But be careful when using npm after you started a project with yarn, it can produce errors. Choose one package manager and stick to it for a particular project.

Once you have the CLI installed you can create an application with a command:

ng new angular-sale-hunter

Where angular-sale-hunter is the name of the application I chose (you should use whatever name is appropriate for your application). It is going to be a sales finder app that is going to feature categories, featured items, search, and notifications components. It will be easily extendable with other functionality, thanks to modularity of Angular.

For our app to be pretty without doing much styling manually, we have to install material design components for Angular:

npm install --save @angular/material @angular/cdk

Other major frameworks also have material design libraries, so no major difference in this field.

Angular makes an extensive use of decorators (an upcoming feature in javascript). They take configuration or mark properties/classes as as being a type that fits certain operations. React and Vue while can be supplied with decorators support via various libraties, do make use of decorators out of the box.

Lets create a module for routing, the command is:

ng generate module app-routing --flat —module=app

—flat means to put the module in src/app folder, —module=app means that routing module belongs to app module.

The minimal app router module configuration for our app is going to look like this:

import { NgModule } from '@angular/core';
import { RouterModule, Routes } from '@angular/router';
import { HomePageComponent } from './home-page/home-page.component'
import { ProductPageComponent } from './product-page/product-page.component'

const routes: Routes = [
  { path: '', component: HomePageComponent },
  { path: 'product/:id', component: ProductPageComponent },
];

@NgModule({
  imports: [
    RouterModule.forRoot(routes)
  ],
  exports: [
    RouterModule
  ],
})
export class AppRoutingModule { }

NgModule is a decorator that has some important properties for the module, you can do various stuff with it. For a beginner article though, you can get away with what CLI gives you with some of my explanations.

Here we define routes and you need to import the result of RouterModule.forRoot(routes) call and export RouterModule inside @NgModule decorator like above for routing to work.

You see all these imports and exports in NgModule decorators. They are separate from javascript module system. They tell angular which modules this module needs, and which components, directives(like component, but they don’t have a view), and pipes (they apply transformation to data) can be used by other modules.

The the other 2 major frameworks have a similar way of dealing with routes configuration, so no big difference here when switching frameworks regarding routes. React has one more way of handling routes thanks to having a JSX, an html-like syntax that is written directly in javascript (React uses a third party library for routing, which is called react-router).

The app’s main component will have the following template:

<h1>Sales Hunter</h1>
<app-notifications></app-notifications>
<router-outlet><router-outlet>

The router-outlet component is managed by routing module and will be replaced by the component that gets rendered in a response to the corresponding route. Every non-standard HTML tag you see is provided either by a component or a directive, either created by yourself or a third party or angular. The router outlet here is provided by Angular. The app notifications are going to be created by us.

Let’s generate a front page and a product page components:

ng generate component home-page
ng generate component product-page

Change the home page template to look like the following:

<app-search></app-search>
<app-categories></app-categories>
<app-featured-items></app-featured-items>

As you have seen, Angular uses templates for rendering, the same as Vue, this is different from React that uses JSX (You may say its HTML within javascript, without string quotes).

With angular and Vue it is possible to have template as a string (but personally I do not prefer that because you loose the benefits of syntax highlighting, easiness of editing and so on.).

Vue is the most flexible in regard to templating, you can have a template as a string, as an html element, as contents of  a script tag, as a contents of an html element with special attribute, and templates, contained in a special .vue file, together with styles and scripts, in fact the latter is the preferred way of creating component based apps with Vue.

When it comes to component file organization, Vue prefers to keep everything in one file: markup, scripts, styles. React (mostly create-react-app) prefers to keep markup(JSX) and scripts in one file, import css as modules (but nobody is stopping you from having a global css file or use js objects as containers from style properties and use style attributes). And angular prefers total separation of scripts styles and markup.

Lets go through creating the components here. While it can be somewhat difficult to integrate a component with your app as a beginner, angular cli got you covered:

ng generate component search
ng generate component categories
ng generate component featured-items
ng generate component notifications

When you generate a component with CLI you refer to it in a template as app- followed by the component name. (This behavior can be changed, by changing the selector property of the configuration object passed to the @Component decorator, then you can use the value of that property as a tag name).

I will walk you through the editing a featured-items and notifications components, the other components get integrated into the app the same way, even though they have different internal logic.

After the CLI has done its work we have a directory called featured-items in our app directory, with all the necessary files: ts, css, html.

Let’s create an item class definition for TypeScript in app/item.ts, which will look like this:

export default class {
  id: number;
  name: string;
  category: string;
}

Then we can use it in our mock_data.ts file which we will create in app/mock_data.ts file and it will look the following way:

import Item from './item';

const Items: Item[] = [
  {
    id: 1,
    name: 'Tender Coat',
    category: 'Coats & Jackets’,
    photo: ‘imgurl.jpg’
  },
… // more items here
]

export default Items

There are several objects in the array and every object has a reference to a photo file name.

Its a common pattern in Angular to handle incoming data with a service, and we are going to follow this convention, because decoupling( making things more independent ) data from a view is a good practice. We are going to use the CLI to generate that service.

ng generate service items —module=app

It is important to provide a —module=app option if you are a beginner, so that the CLI makes the service usable within the main module. As you progress with Angular you will be able to integrate it yourself, but event then it would be much simpler and faster to use that command. Edit the code for the newly generated items.service.ts to look like the following:

import { Injectable } from '@angular/core';
import Items from './mock_data';

@Injectable()
export class ItemsService {

  constructor() { }

  getItems() {
    return Items;
  }
}

The component gets decorated with Injectable decorator, this is needed for other components to use that service. getItems is the method we are going to use to get the items array. In a real application you are going to use some http capable solution inside this function to get the data from the server.

Let’s edit featured-items.component.ts generated for us by the CLI. First, let’s import the Item definition and the mock data.

import Item from '../item';
import { ItemsService } from ‘../items.service';

We should use dependency injection here for the service to be available to this component it is done this way:

constructor(
  private itemsService: ItemsService
) { }

Dependency injection is the defining feature of both AngularJS and Angular. You see here an itemsService of type ItemsService gets passed as a parameter to the component’s constructor. Later on during the lifetime of the component the itemsService parameter’s value will be accessible by the component as this.itemsService. This pattern appears a lot throughout Angular development.

Other frameworks do not use it so often. In react the closest thing is context. Most applications in React should not use the context, they should pass data as props from a parent component to a child. In recent versions of Vue there are provide/inject options but they are close to react’s connect mechanism.

Let’s create an items component property and edit the ngOnInit method (it runs once after the ngOnChanges method, and we put the initialization logic there) to use the freshly injected items service:

items: Item[]

ngOnInit() {
  const featuredItems: Item[]
    = this.itemsService.getItems()
    .sort( function() { return 0.5 - Math.random() } )
  this.items = featuredItems;
}

We use mock data implementation here, in a real app you would request the needed items via HTTP.

After we have initialized the items property we can use it in the component’s template

<ul class="featured-items">
  <li *ngFor="let item of items”>
    {{ item.name }}
  </li>
</ul>

Lets keep the best practice from React and use its key attribute pattern. In Angular it is represented by trackBy function

<li *ngFor="let item of items; trackBy: trackByItems" class="featuerd-item-container">

We need to create a trackBy function in our component for trackBy to work:

trackByItems(index: number, item: Item): number { return item.id; }

Right now featured items look pretty basic. Let’s modify them to be more user friendly. The code inside <ul> would look like this:

<li *ngFor="let item of items; trackBy: trackByItems" class="featuerd-item-container">
  <div class="featured-item">
    <mat-card class="featured-item-card">
      <div class="card-image-container">
        <img mat-card-image src="assets/{{ item.photo }}" alt="{{ item.name }}">
      </div>
      <mat-card-content class="item-card-content">
        <p>
          {{ item.name }} ( Category: {{ item.category }})
        </p>
      </mat-card-content>
    </mat-card>
  </div>
</li>

You are maybe wondering, where is the component for mat-card is. This is a component created by material library for angular, the one that we installed at the beginning of this post. To make those components work in our application, we need to add some configuration. First of all we need to import MatCardModule in app.module.ts:

import { MatCardModule } from '@angular/material/card';

Then add MatCardModule to the imports array of the app.module.ts, after BrowserModule

imports: [
    BrowserModule,
    AppRoutingModule,
    MatCardModule,
  ],

Right now it looks like a list of pictures, but we want to be able to click on an image and navigate to the featured-item detail page. This is where the router module comes in handy. It has a routerLink directive that allows creating anchors with a link to one of the routes. In our case we will create a link to /product/:id, that we defined earlier.

We generated the the product-page component earlier so the only thing left is add some markup and fanciness to the component. Let’s modify the generated product-page component, by first adding some imports:

import { ActivatedRoute } from ‘@angular/router';
import { Location } from '@angular/common'
import Item from ‘../Item';
import { ItemsService } from '../items.service';

Then we need to inject an instance of the ActivatedRoute into the constructor to be able to use it in the component’s methods:

constructor(private route: ActivatedRoute) { }

We also need to define an item property that will hold our featured item, so that the component’s template has an access to this property.

And we going to use the route right away in ngOnInit method like so:

ngOnInit() {
  const id = +this.route.snapshot.paramMap.get('id');
  const product: Item
    = this.itemsService.getItems().find((item: Item) => item.id === id);
  this.product = product;
}

A route’s parameters get accessed via route.snapshot.paramMap method (kind of lengthy way but it does its job). We use an ItemsService’s getItems method to decouple data retrieving logic from the presentational component like app-product-page).

Lets now work on a notification component. It will show us a notification, whenever a sale is available. In our demo app it will appear at random times, as opposed to an actual sale being available. For simplicity it will only have 10 items maximum, but in a real application you can have some sort of a button that is going to show more notifications.

We will use a notification service to get notifications, so let’s create it first, you are already familiar with the process:

ng generate service notifications --module=app

We are going to add 2 imports to notification.service.ts:

import { Observable } from 'rxjs/Observable';
import Items from ‘./mock_data’;

And it is going to have a getNotifications method:

getNotifications() {
  return Observable.create(observer => {
    const getRandomTime = () => {
      const min = 1000;
      const max = 60000;
      return Math.floor(Math.random() * (max - min)) + min;
    }

    const randomTimeout = () => {
      const index = Math.floor(Math.random() * 19);
      observer.next(Items[index].name)
      id = setTimeout(randomTimeout, getRandomTime());
    }

    let id = setTimeout(randomTimeout, getRandomTime());
  });
}

We are using Observable from RxJS, which allows us to create and receive events as a stream. We create an observable and trigger events in a callback passed to the create method. In this method we just randomly select time intervals at which a random entry from Items array will be send to a component, that subscribes to our observable. And that component is going to be notifications component, that we have already created.

In notifications/notification.component.ts we are going to import the notification service:

import { NotificationsService } from ‘../notifications.service';

Inject it into the component through the constructor:

constructor(
  private notifications: NotificationsService,
) { }

It is going to have the following methods and properties:

messages: string[] = []
messagesShown: boolean = false
trackByMessages(index: number, message: string): number { return index; }

messages will hold an array of messages to show to a user. messagesShown will control whether or not a dropdown with messages should be shown and trackByMessages is a function used for the trackBy in *ngFor, as we have already seen before.

We are also going to have the following message related methods:

showMessages() {
  if (!this.messages.length) return
  this.messagesShown = true;
}

hideMessages() {
  this.messagesShown = false;
  this.messages = []
}

ngOnInit() {
  this.notifications.getNotifications().subscribe(arg => {
    if (this.messages.length > 9) return;
    this.messages.push(arg);
  });
}

showMessages will show the messages dropdown if we have any messages. hideMessages will hide the dropdown and clear the messages array. And in ngOnInit we are going to subscribe to an Observable via notification service’s getNotifications method, and in the callback we will push the messages to the messages array.

And finally this is the markup of our component

<div class="inbox" (click)="showMessages()">
  new sales
  <div [ngClass]="{ 'count': true, 'check': messages.length }">{{ messages.length }}</div>
  <div
    class="messages-dropdown" *ngIf="messagesShown"
  >
    <ul>
      <li *ngFor="let message of messages">{{ message }}</li>
    </ul>
    <div class="close" (click)="hideMessages()">Close</div>
  </div>
</div>
<div class="notification"></div>

We use *ngIf to conditionally show the dropdown if the messagesShown property is true. [ngClass] attribute here allows us to conditionally add classes to the element if the corresponding values of the object are true. Right now a user will be able to click on a new sales button to see all the new sales available and close the dropdown afterwards.

So, here is my take on creating an application with Angular. Overall it is very smooth in my opinion. Typescript checking saves you time, and it is not hard to pick up. CLI component creation is very convenient. The framework is definitely worth using. You can find the source code for this post on Github: https://github.com/jsmegatools/creating-a-simple-app-with-Angular.js

Leave a Reply

Your email address will not be published. Required fields are marked *