How to Build CRUD Application with Angular and NgRx

How to Build CRUD Application with Angular and NgRx

In this post, we discuss to build a mini-CRUD application with NgRx as the state management system.

Project Details

Throughout this article, we will be building a simple course management system. As shown below, you will be able to perform all the CRUD operations on the course entity via this simple web application.

Project structure

As the following figure illustrates, our application will consist of two primary modules, namely App and Course. The course module, in turn, will have two custom components, namely Course List and Create Course.

REST API

In general, an Angular application interacts with a REST API to perform CRUD operations on data.

Therefore, I implemented a simple REST API in Spring Boot that exposes the below endpoints. We will use this API to connect from the Angular application and carry out data operations.

// Retrieve all courses
GET     http://localhost:8080/api/courses

// Create a course
POST    http://localhost:8080/api/courses

// Delete a course
DELETE  http://localhost:8080/api/courses/{courseId}

// Update a course
PUT     http://localhost:8080/api/courses/{courseId}

Full source code

You can find the complete source code of this sample application on GitHub. Please note that I have also added the executable JAR file (course-1.0.0-SNAPSHOT.jar) of the Spring Boot application (REST API) to the same repository.

NgRx Entity at a Glance

You have already come across most of the NgRx terms that I will use in this article. For example, store, effects, actions, selectors, and reducers. In this article, I will introduce a new NgRx library called NgRx Entity (@ngrx/entity).

NgRx Entity helps us to manage various data entities in an application. For example, the Course is an entity in our application. It takes the following format.

export interface Course {

id: string;

name: string;

description: string;

}

The NgRx Entity library makes it very easy to perform different operations (add, update, remove, select) on course objects stored in the application state. Let’s see how…

EntityState interface

The entity library provides a set of tools to make our life easier with NgRx. The first and foremost is the EntityState interface. The shape of theEntityState looks like the below.

interface EntityState<V> {
  ids: string[];
  entities: { [id: string]: V };
}

We have to use EntityState to declare the interface for our courses state.

import { EntityState } from '@ngrx/entity';export interface CourseState extends EntityState<Course> { }

When EntityState is used, the courses state will take the following format.

As you can see, it maintains an array of course IDs and a dictionary of course objects. There are two primary reasons we maintain a list of IDs and a dictionary of entities as opposed to just maintaining an array of entities:

  1. We want to make looking up a specific entity fast. If you wanted to just select one course from the store, using the entities dictionary is much faster than searching through an array
  2. We also want to maintain the order of the list. This is especially important if you want to keep the list sorted!

Entity adapter

Entity adapter is another tool that goes hand-in-hand with EntityState. It provides a bunch of helper methods that make it very easy to perform various operations on data stored in EntityState.

These helper methods make reducers simple, expressive, and consistent. You can create an entity adapter in the following way.

import { createEntityAdapter } from '@ngrx/entity';
const courseAdapter = createEntityAdapter<Course>();

The following are some of the very useful methods exposed by the adapter to interact with the state.

  • addOne: Add one entity to the collection.
  • addMany: Add multiple entities to the collection.
  • addAll: Replace current collection with provided collection.
  • removeOne: Remove one entity from the collection.
  • removeMany: Remove multiple entities from the collection, by I or by the predicate.
  • removeAll: Clear entity collection.
  • updateOne: Update one entity in the collection.
  • updateMany: Update multiple entities in the collection.
  • upsertOne: Add or update one entity in the collection.
  • upsertMany: Add or update multiple entities in the collection.
  • map: Update multiple entities in the collection by defining a map function, similar to Array.map.
Setting Up the Project

Software versions used

  • Angular CLI: 8.0.1
  • Node: 11.6.0
  • Angular: 8.0.2
  • NgRx: 8.6.0
  • Bootstrap: 4.4.1

Project initialization

Step 1: Execute the below command and create a new project.

ng new angular-ngrx-example

Step 2: We will use Bootstrap to add styles to our application. You can install Bootstrap with the below command.

npm install bootstrap --save

Step 3: Import Bootstrap by updating the angular.json file as shown below.

/*
  ---------------------------- Abbreviated Code Snippet: Start ----------------------------
*/

  "projects": {
    "angular-ngrx-example": {
      "root": "",
      "sourceRoot": "src",
      "projectType": "application",
      "prefix": "app",
      "schematics": {},
      "architect": {
        "build": {
          "builder": "@angular-devkit/build-angular:browser",
          "options": {
            "outputPath": "dist/angular-ngrx-example",
            "index": "src/index.html",
            "main": "src/main.ts",
            "polyfills": "src/polyfills.ts",
            "tsConfig": "src/tsconfig.app.json",
            "assets": [
              "src/favicon.ico",
              "src/assets"
            ],
            "styles": [
              "node_modules/bootstrap/dist/css/bootstrap.min.css",
              "src/styles.css"
            ],

/*
  ---------------------------- Abbreviated Code Snippet: End ----------------------------
*/

Step 4: Install NgRx dependencies.

npm install @ngrx/{store,effects,entity,store-devtools,schematics} --save
Adding NgRx Support to Root Module

Execute the below schematics command to generate the initial state management and register it within the app.module.ts.

ng generate @ngrx/schematics:store State --root --statePath store/reducers --module app.module.ts

After the above command, your project folder structure should look like the below.

Following is the content of the index.ts file. Please note that I made a couple of minor changes to the auto-generated file. For example, I changed the State interface to AppState for the sake of clarity.

import {
  ActionReducer,
  ActionReducerMap,
  createFeatureSelector,
  createSelector,
  MetaReducer
} from '@ngrx/store';
import { environment } from '../../../environments/environment';


export interface AppState {

}

export const reducers: ActionReducerMap<AppState> = {

};


export const metaReducers: MetaReducer<AppState>[] = !environment.production ? [] : [];

The NgRx schematics command will also update the app.module.ts file. Following is the updated content of this file.

import { BrowserModule } from '@angular/platform-browser';
import { NgModule } from '@angular/core';

import { AppComponent } from './app.component';
import { StoreModule } from '@ngrx/store';
import { reducers, metaReducers } from './store/reducers';
import { StoreDevtoolsModule } from '@ngrx/store-devtools';
import { environment } from '../environments/environment';

@NgModule({
  declarations: [
    AppComponent
  ],
  imports: [
    BrowserModule,
    StoreModule.forRoot(reducers, {
      metaReducers,
      runtimeChecks: {
        strictStateImmutability: true,
        strictActionImmutability: true,
      }
    }),
    !environment.production ? StoreDevtoolsModule.instrument() : []
  ],
  providers: [],
  bootstrap: [AppComponent]
})
export class AppModule { }
Creating and Setting Up the “Course” Feature Module

Generating the “Course” module

As stated earlier, our application consists of two major modules, namely App and Course. Now is the time to create the Course module with the below command.

ng generate module course

The aforementioned command will create a sub-folder named course directly under the app folder. In addition, a new file named course.module.ts will be created and placed under the app/course folder.

Following is the initial version of course.module.ts file. Note that this file will be altered downstream to add NgRx support, declare components, and declare service providers.

import { NgModule } from '@angular/core';
import { CommonModule } from '@angular/common';

@NgModule({
  declarations: [],
  imports: [
    CommonModule
  ]
})
export class CourseModule { }

Defining the “Course” model

As the next step, you have to define the model interface that represents the Course entity. Create a file called course.model.ts and place it under the app/course/model folder. The content of this file should be as follows.

export interface Course {
  id: string;
  name: string;
  description: string;
}

Defining the Service class

Service is used to interact with the REST API and perform data operations. In order to define the service class, create a file named course.service.ts and place it under the app/course/services folder.

The content of this file should be as follows.

import { Course } from './../model/course.model';
import { Injectable } from '@angular/core';
import { HttpClient, HttpResponse } from '@angular/common/http';
import { Observable } from 'rxjs';
import { map } from 'rxjs/operators';


@Injectable()
export class CourseService {

  http: HttpClient;

  constructor(http: HttpClient) {
    this.http = http;
  }

  getAllCourses(): Observable<Course[]> {
    return this.http.get<Course[]>('/api/courses');
  }

  createCourse(course: Course): Observable<Course> {
    return this.http.post<Course>('/api/courses', course);
  }

  deleteCourse(courseId: string): Observable<any> {
    return this.http.delete('/api/courses/' + courseId);
  }

  updateCourse(courseId: string | number, changes: Partial<Course>): Observable<any> {
    return this.http.put('/api/courses/' + courseId, changes);
  }
}

As you can see, it has methods to retrieve, create, update, and delete Course entities via the REST API. Once the service class is defined, you have to register it in the course.module.ts file as shown below.

import { CourseService } from './services/course.service';
import { NgModule } from '@angular/core';
import { CommonModule } from '@angular/common';

@NgModule({
  declarations: [],
  imports: [
    CommonModule
  ],
  providers: [CourseService]
})
export class CourseModule { }

The following figure illustrates the folder structure of our application at this point.

Adding NgRx Artifacts to “Course” Module

As the next step, you have to define actions, reducers, effects, and selectors and attach to the course module. These artifacts will be created inside a directory called store which in turn is located under the app/course directory.

Defining NgRx actions (course.actions.ts)

import { Course } from './../model/course.model';
import { createAction, props } from '@ngrx/store';
import {Update} from '@ngrx/entity';


export const loadCourses = createAction(
  '[Courses List] Load Courses via Service',
);

export const coursesLoaded = createAction(
  '[Courses Effect] Courses Loaded Successfully',
  props<{courses: Course[]}>()
);

export const createCourse = createAction(
  '[Create Course Component] Create Course',
  props<{course: Course}>()
);

export const deleteCourse = createAction(
  '[Courses List Operations] Delete Course',
  props<{courseId: string}>()
);

export const updateCourse = createAction(
  '[Courses List Operations] Update Course',
  props<{update: Update<Course>}>()
);

export const courseActionTypes = {
  loadCourses,
  coursesLoaded,
  createCourse,
  deleteCourse,
  updateCourse
};

Special notes:

  • loadCourses, createCourse, deleteCourse, and updateCourse are self-explanatory actions which are dispatched by the components. However, coursesLoaded is a special action that will be dispatched by the effect in order to inform the store that the courses were loaded successfully.
  • The updateCourse action accepts a payload of type {update: Update}. Update is an auxiliary type provided by NgRx Entity to help model partial entity updates. This type has a property id that identifies the updated entity, and another property called changes that specifies which modifications are being made to the entity.

Defining NgRx reducers (course.reducers.ts)

import { Course } from './../model/course.model';
import { EntityState, EntityAdapter, createEntityAdapter } from '@ngrx/entity';
import { createReducer, on } from '@ngrx/store';
import { courseActionTypes, coursesLoaded } from './course.actions';

export interface CourseState extends EntityState<Course> {
  coursesLoaded: boolean;
}

export const adapter: EntityAdapter<Course> = createEntityAdapter<Course>();

export const initialState = adapter.getInitialState({
  coursesLoaded: false
});

export const courseReducer = createReducer(
  initialState,

  on(courseActionTypes.coursesLoaded, (state, action) => {
    return adapter.addAll(
      action.courses,
      {...state, coursesLoaded: true}
    );
  }),

  on(courseActionTypes.createCourse, (state, action) => {
    return adapter.addOne(action.course, state);
  }),

  on(courseActionTypes.deleteCourse, (state, action) => {
    return adapter.removeOne(action.courseId, state);
  }),

  on(courseActionTypes.updateCourse, (state, action) => {
    return adapter.updateOne(action.update, state);
  })
);

export const { selectAll, selectIds } = adapter.getSelectors();

Special notes:

  • The below code snippet defines the Course state by extending the EntityState. As we discussed before, EntityState maintains a list of IDs and a dictionary of entities. In addition to those two properties, we are here defining a custom property called coursesLoaded. This property is primarily used to indicate whether the courses have been already loaded into the state.
export interface CourseState extends EntityState<Course> {  coursesLoaded: boolean;}
  • The below code snippet creates an Entity Adapter that provides the helper functions.
export const adapter: EntityAdapter<Course> = createEntityAdapter<Course>();
  • The initial state is defined as follows. Entity adapter provides a helper function to obtain the initial state. As you can see, we are setting the coursesLoaded property to false initially.
export const initialState = adapter.getInitialState({  coursesLoaded: false});
  • The following code line exports a couple of predefined selectors provided to us by the adapter. These selectors will be used by our custom selectors. (We will look into this when we define our selectors.)
export const { selectAll, selectIds } = adapter.getSelectors();

Defining NgRx selectors (course.selectors.ts)

import { CourseState } from './course.reducers';
import { Course } from './../model/course.model';
import { createSelector, createFeatureSelector } from '@ngrx/store';
import { selectAll, selectIds } from './course.reducers';

export const courseFeatureSelector = createFeatureSelector<CourseState>('courses');

export const getAllCourses = createSelector(
  courseFeatureSelector,
  selectAll
);

export const areCoursesLoaded = createSelector(
  courseFeatureSelector,
  state => state.coursesLoaded
);

Special notes:

  • Here, we are using the selectAll predefined selector to retrieve all the course entities as an array.
  • The areCoursesLoaded selector is used to check whether the courses have been already loaded into the state. This selector uses the coursesLoaded custom property we defined under CourseState.

Defining NgRx effects (course.effects.ts)

import { courseActionTypes, coursesLoaded, updateCourse } from './course.actions';
import { CourseService } from './../services/course.service';
import { createEffect, Actions, ofType } from '@ngrx/effects';
import { concatMap, map, tap } from 'rxjs/operators';
import { Injectable } from '@angular/core';
import { Router } from '@angular/router';

@Injectable()
export class CourseEffects {

  loadCourses$ = createEffect(() =>
    this.actions$.pipe(
      ofType(courseActionTypes.loadCourses),
      concatMap(() => this.courseService.getAllCourses()),
      map(courses => courseActionTypes.coursesLoaded({courses}))
    )
  );

  createCourse$ = createEffect(() =>
    this.actions$.pipe(
      ofType(courseActionTypes.createCourse),
      concatMap((action) => this.courseService.createCourse(action.course)),
      tap(() => this.router.navigateByUrl('/courses'))
    ),
    {dispatch: false}
  );

  deleteCourse$ = createEffect(() =>
    this.actions$.pipe(
      ofType(courseActionTypes.deleteCourse),
      concatMap((action) => this.courseService.deleteCourse(action.courseId))
    ),
    {dispatch: false}
  );

  updateCOurse$ = createEffect(() =>
    this.actions$.pipe(
      ofType(courseActionTypes.updateCourse),
      concatMap((action) => this.courseService.updateCourse(action.update.id, action.update.changes))
    ),
    {dispatch: false}
  );

  constructor(private courseService: CourseService, private actions$: Actions, private router: Router) {}
}

Special notes:

createCourse$, deleteCourse$, and updateCourse$ effects are self-explanatory. They simply invoke the corresponding REST endpoint and perform the operation.

These effects do not map the incoming action to a new action type, which is why {dispatch: false} config is used.

However, loadCourses$ has a special behavior. It accepts the actions of type loadCourses and once the courses are retrieved via the REST API, it maps the response to a new action type called coursesLoaded.

The retrieved list of courses is passed into the coursesLoaded action.

Registering the NgRx artifacts in the Course module

After the NgRx artifacts are defined, update the course.module.ts file as shown below to add the State support.

import { CourseEffects } from './store/course.effects';
import { CourseService } from './services/course.service';
import { CommonModule } from '@angular/common';
import { FormsModule } from '@angular/forms';
import { NgModule } from '@angular/core';
import { StoreModule } from '@ngrx/store';
import { EffectsModule } from '@ngrx/effects';
import { courseReducer } from './store/course.reducers';

@NgModule({
  declarations: [],
  imports: [
    CommonModule,
    FormsModule,
    StoreModule.forFeature('courses', courseReducer),
    EffectsModule.forFeature([CourseEffects])
  ],
  providers: [CourseService],
  bootstrap: []
})
export class CourseModule { }

Special notes:

  • The below code line creates a dedicated slice (courses) in the application state for the course module and attaches the reducers to it.
StoreModule.forFeature('courses', courseReducer),

  • The following code line registers the effects in the course module state.
EffectsModule.forFeature([CourseEffects])

Now that that’s out of the way, your project folder structure should look like the below at this stage.

Creating Components and Defining Routes

As we discussed earlier, our application is made up of two main modules, namely App and Course. The course module consists of two components, namely courses-list and create-course.

Our next step is to create these two components and define the corresponding routes. Note that the courses-list and create-course directories will be created under the app/course/component directory.

Defining “courses-list” component

Template: courses-list.component.html.

<div>
  <table class="table table-dark">
    <thead>
      <tr>
        <th scope="col">Name</th>
        <th scope="col">Description</th>
        <th scope="col">Operations</th>
      </tr>
    </thead>
    <tbody>
      <tr *ngFor="let course of courses$ | async">
        <td>{{ course.name }}</td>
        <td>{{ course.description }}</td>
        <td>
          <button (click)="showUpdateForm(course)" class="btn btn-primary" style="margin: 5px">Update</button>
          <button (click)="deleteCourse(course.id)" class="btn btn-danger" style="margin: 5px">Delete</button>
        </td>
      </tr>
    </tbody>
  </table>

  <div *ngIf="isUpdateActivated" style="margin-top: 50px; margin-left: 50px;">
    <h4>Update Course</h4>
    <form (ngSubmit)="updateCourse(updateForm)" #updateForm="ngForm">
      <div class="form-group">
        <label for="name">Course Name</label>
        <input
          type="text"
          id="name"
          name="name"
          class="form-control"
          required
          [(ngModel)]="courseToBeUpdated.name"
          style="width: 400px"/>
      </div>
      <div class="form-group">
        <label for="side">Description</label>
        <input
          type="text"
          id="description"
          name="description"
          class="form-control"
          required
          [(ngModel)]="courseToBeUpdated.description"
          style="width: 400px"/>
      </div>
      <button [disabled]="updateForm.invalid" class="btn btn-primary" type="submit">Update</button>
    </form>
  </div>
</div>

Component: courses-list.component.ts.

import { getAllCourses } from './../../store/course.selectors';
import { courseActionTypes } from './../../store/course.actions';
import { AppState } from './../../../store/reducers/index';
import { Store } from '@ngrx/store';
import { Observable } from 'rxjs';
import { Course } from './../../model/course.model';
import { CourseService } from './../../services/course.service';
import { Component, OnInit } from '@angular/core';
import { FormBuilder, FormGroup, Validators, NgForm } from '@angular/forms';
import { Update } from '@ngrx/entity';

@Component({
  selector: 'app-courses-list',
  templateUrl: './courses-list.component.html'
})
export class CoursesListComponent implements OnInit {

  courses$: Observable<Course[]>;

  courseToBeUpdated: Course;

  isUpdateActivated = false;

  constructor(private courseService: CourseService, private store: Store<AppState>) { }

  ngOnInit() {
    this.courses$ = this.store.select(getAllCourses);
  }

  deleteCourse(courseId: string) {
    this.store.dispatch(courseActionTypes.deleteCourse({courseId}));
  }

  showUpdateForm(course: Course) {
    this.courseToBeUpdated = {...course};
    this.isUpdateActivated = true;
  }

  updateCourse(updateForm) {
    const update: Update<Course> = {
      id: this.courseToBeUpdated.id,
      changes: {
        ...this.courseToBeUpdated,
        ...updateForm.value
      }
    };

    this.store.dispatch(courseActionTypes.updateCourse({update}));

    this.isUpdateActivated = false;
    this.courseToBeUpdated = null;
  }
}

Special notes:

  • This component is responsible for facilitating the list, updating, and deleting operations.

Defining “create-course” component

Template: create-course.component.html.

<form (ngSubmit)="onSubmit(createForm)" #createForm="ngForm">
  <div class="form-group">
    <label for="name">Course Name</label>
    <input
      type="text"
      id="name"
      name="name"
      class="form-control"
      ngModel
      required
      style="width: 400px"/>
  </div>
  <div class="form-group">
    <label for="side">Description</label>
    <input
      type="text"
      id="description"
      name="description"
      class="form-control"
      ngModel
      required
      style="width: 400px"/>
  </div>
  <button [disabled]="createForm.invalid" class="btn btn-primary" type="submit">Create</button>
</form>

Component: create-course.component.ts.

import { Course } from './../../model/course.model';
import { createCourse } from './../../store/course.actions';
import { AppState } from './../../../store/reducers/index';
import { Store } from '@ngrx/store';
import { Component, OnInit } from '@angular/core';
import * as uuid from 'uuid';

@Component({
  selector: 'app-create-course',
  templateUrl: './create-course.component.html'
})
export class CreateCourseComponent implements OnInit {

  constructor(private store: Store<AppState>) { }

  ngOnInit() {
  }

  onSubmit(submittedForm) {
    console.log(submittedForm.value);

    if (submittedForm.invalid) {
      return;
    }


    const course: Course = {id: uuid.v4(), name: submittedForm.value.name, description: submittedForm.value.description};
    this.store.dispatch(createCourse({course}));

  }
}

Declaring the components in the Course module

You have to declare the above components in the course.module.ts file.

import { CourseEffects } from './store/course.effects';
import { CourseService } from './services/course.service';
import { CommonModule } from '@angular/common';
import { FormsModule } from '@angular/forms';
import { NgModule } from '@angular/core';

import { CoursesListComponent } from './component/courses-list/courses-list.component';
import { StoreModule } from '@ngrx/store';
import { EffectsModule } from '@ngrx/effects';
import { courseReducer } from './store/course.reducers';
import { CreateCourseComponent } from './component/create-course/create-course.component';

@NgModule({
  declarations: [
    CoursesListComponent,
    CreateCourseComponent
  ],
  imports: [
    CommonModule,
    FormsModule,
    StoreModule.forFeature('courses', courseReducer),
    EffectsModule.forFeature([CourseEffects])
  ],
  providers: [CourseService],
  bootstrap: [],
  exports: [CoursesListComponent, CreateCourseComponent]
})
export class CourseModule { }

Configuring routes

Now is the time to define the routes and associate corresponding components with those routes. This has to be done in the app.module.ts as shown below.

import { CreateCourseComponent } from './course/component/create-course/create-course.component';
import { CourseResolver } from './course/course.resolver';
import { CoursesListComponent } from './course/component/courses-list/courses-list.component';
import { EffectsModule } from '@ngrx/effects';
import { CourseModule } from './course/course.module';
import { BrowserModule } from '@angular/platform-browser';
import { NgModule } from '@angular/core';
import { StoreModule } from '@ngrx/store';
import { RouterModule } from '@angular/router';
import { HttpClientModule } from '@angular/common/http';

import { AppComponent } from './app.component';
import { reducers, metaReducers } from './store/reducers';
import { StoreDevtoolsModule } from '@ngrx/store-devtools';

const routes = [
  {
    path: 'courses',
    component: CoursesListComponent,
    resolve: {
      courses: CourseResolver
    }
  },
  {path: 'create-course', component: CreateCourseComponent},
  {path: '**', redirectTo: 'courses'}
];

@NgModule({
  declarations: [
    AppComponent,
  ],
  imports: [
    BrowserModule,
    CourseModule,
    HttpClientModule,
    RouterModule.forRoot(routes),
    EffectsModule.forRoot([]),
    StoreModule.forRoot(reducers, {
      metaReducers
    }),
    StoreDevtoolsModule.instrument({maxAge: 25}),
  ],
  providers: [CourseResolver],
  bootstrap: [AppComponent]
})
export class AppModule { }

Special notes:

  • CoursesListComponent uses a resolver to fetch data. A route resolver ensures that the data is available to use by the component before navigating to a particular route. In this instance, the resolver is responsible for retrieving the courses list prior to completing the navigation to /courses.

Implementing the route resolver (course.resolver.ts)

import { areCoursesLoaded } from './store/course.selectors';
import { loadCourses, coursesLoaded } from './store/course.actions';
import { AppState } from './../store/reducers/index';
import { Course } from './model/course.model';
import { Injectable } from '@angular/core';
import {ActivatedRouteSnapshot, Resolve, RouterStateSnapshot} from '@angular/router';
import { Observable } from 'rxjs';
import {select, Store} from '@ngrx/store';
import {filter, finalize, first, tap} from 'rxjs/operators';

@Injectable()
export class CourseResolver implements Resolve<Observable<any>> {

  constructor(private store: Store<AppState>) {}

  resolve(route: ActivatedRouteSnapshot, state: RouterStateSnapshot): Observable<any> {
    return this.store
    .pipe(
        select(areCoursesLoaded),
        tap((coursesLoaded) => {
          if (!coursesLoaded) {
            this.store.dispatch(loadCourses());
          }

        }),
        filter(coursesLoaded => coursesLoaded),
        first()
    );
  }
}

Special notes:

  • The areCoursesLoaded custom selector is used to check whether the data has already been loaded into the state.
  • The loadCourses action is dispatched only if the data is not already available in the state.
  • The operator chain will not let a value pass through to the subscriber until the coursesLoaded flag is set to true. As a result, the application will not be navigated to the /courses route until the courses are successfully loaded.

Defining the router outlet

As the final step, you have to define the router outlet in the app.component.html.

<div>
  <div>
      <ul class="nav nav-pills">
        <li class="nav-item">
          <a class="nav-link" routerLink="courses" routerLinkActive="active">List Courses</a>
        </li>
        <li class="nav-item">
            <a class="nav-link" routerLink="create-course" routerLinkActive="active">Create Course</a>
          </li>
      </ul>
  </div>

  <div style="margin-top: 20px;">
      <router-outlet></router-outlet>
  </div>

</div>

At this stage, your folder structure should look like the below.

Configuring a Proxy to Access the REST API

As mentioned at the start of this article, we are using a simple REST API written in Spring Boot to connect from the Angular application.

The Spring Boot application runs on localhost:8080 whereas the Angular application runs on localhost:4200. This mismatch will cause a Cross Origin Resource Sharing (CORS) error when the Angular application tries to access the REST API. To overcome this issue we have to create a proxy.

Creating a proxy file

Create a file called proxy.conf.json inside the project’s root folder (same level where package.json file exists), and add the below content to it.

{
  "/api": {
    "target": "http://localhost:8080",
    "secure": false
  }
}

Registering the proxy file

In the CLI configuration file, angular.json, add the proxyConfig option to the serve target:

  /*
  ---------------------------- Abbreviated Code Snippet: Start ----------------------------
*/

"serve": {
  "builder": "@angular-devkit/build-angular:dev-server",
  "options": {
    "proxyConfig": "proxy.conf.json",
    "browserTarget": "angular-ngrx-example:build"
  },


/*
  ---------------------------- Abbreviated Code Snippet: End ----------------------------
*/
Running the Application

The application should be started as a two-step process. You have to first start the Spring Boot application (REST API) and then the Angular application.

Running the Spring Boot application (REST API)

The Spring Boot application is packaged into an executable JAR file with the name course-1.0.0-SNAPSHOT.jar and placed here (GitHub).

Note that you have to have Java 8 installed on your system to execute this JAR file. If Java 8 is installed, you can execute the below command and start the application.

java -jar {path_to_the_jar_file}/course-1.0.0-SNAPSHOT.jar

You should see the below log if the application started successfully.

$ java -jar course-1.0.0-SNAPSHOT.jar

  .   ____          _            __ _ _
 /\\ / ___'_ __ _ _(_)_ __  __ _ \ \ \ \
( ( )\___ | '_ | '_| | '_ \/ _` | \ \ \ \
 \\/  ___)| |_)| | | | | || (_| |  ) ) ) )
  '  |____| .__|_| |_|_| |_\__, | / / / /
 =========|_|==============|___/=/_/_/_/
 :: Spring Boot ::        (v2.2.2.RELEASE)

2020-01-19 11:16:36.714  INFO 9340 --- [           main] c.medium.example.app.CourseApplication   : Starting CourseApplication v1.0.0-SNAPSHOT on DESKTOP-BKE1M8T with PID 9340 (G:\Angular\Repositories\angular-ngrx-example\angular-ngrx-example\course-1.0.0-SNAPSHOT.jar started by Sarindu in G:\Angular\Repositories\angular-ngrx-example\angular-ngrx-example)
2020-01-19 11:16:36.718  INFO 9340 --- [           main] c.medium.example.app.CourseApplication   : No active profile set, falling back to default profiles: default
2020-01-19 11:16:38.997  INFO 9340 --- [           main] o.s.b.w.embedded.tomcat.TomcatWebServer  : Tomcat initialized with port(s): 8080 (http)
2020-01-19 11:16:39.012  INFO 9340 --- [           main] o.apache.catalina.core.StandardService   : Starting service [Tomcat]
2020-01-19 11:16:39.013  INFO 9340 --- [           main] org.apache.catalina.core.StandardEngine  : Starting Servlet engine: [Apache Tomcat/9.0.29]
2020-01-19 11:16:39.111  INFO 9340 --- [           main] o.a.c.c.C.[Tomcat].[localhost].[/]       : Initializing Spring embedded WebApplicationContext
2020-01-19 11:16:39.111  INFO 9340 --- [           main] o.s.web.context.ContextLoader            : Root WebApplicationContext: initialization completed in 2298 ms

Running the Angular application

The Angular application can be started by executing the below command.

ng serve

When the application is started successfully, navigate to http://localhost:4200/courses from your browser and you should see the below screen.

Understanding the Execution Flow

Retrieve courses

Create course

Special note:The key thing to note is that the reducer updates the state with the new data (in turn, the UI will be updated), even before the effect invokes the API and actually creates a record in the server.

Update course

Special note:Again, the reducer updates the state with the updated course data (in turn, the UI will be updated), even before the effect invokes the API and actually updates the relevant record in the server.

Delete course

Special note: Similar to creating a course and updating a course, the reducer removes the relevant course information from the state (in turn, the UI will be updated), even before the effect invokes the API and remove the relevant record in the server.

Optimistic UI

Optimistic UI is a pattern that you can use to simulate the results of a state mutation and update the UI even before receiving a response from the server.

In this particular application, we are following the same pattern. As explained above, when creating, updating, and deleting a course, the state and the UI are updated even before receiving a response from the REST API.

Conclusion

The prime objective of this story was to provide a step-by-step by guide to build an NgRx-based Angular application.

As explained in the previous section, we have used the optimistic UI pattern to implement this mini system.

Thank you for reading!

Angular 9 Tutorial: Learn to Build a CRUD Angular App Quickly

What's new in Bootstrap 5 and when Bootstrap 5 release date?

What’s new in HTML6

How to Build Progressive Web Apps (PWA) using Angular 9

What is new features in Javascript ES2020 ECMAScript 2020

Node vs Angular : Comparing Two Strong JavaScript Technologies

Just from being a simple client-side scripting language, JavaScript has evolved over the years to turn out to be a powerful programming language. Here Node.js is a cross-platform runtime environment while AngularJS is one of the top JavaScript framework. Angular helps the developers to build web applications which are dynamic in nature using HTML template language and following the MVC design pattern.

15 Common Mistakes Developers Made While writing JavaScript Apps

15 Common Mistakes Developers Made While writing JavaScript Apps

Every developer make mistake some time when writing apps. This is a normal part of the development process. In this post, we'll learn 15 Common Mistakes Developers Made While Writing JavaScript Apps hope this post will help you

Common Mistakes Developers Made While Writing JavaScript Apps

Every developer make mistake some time when writing apps. This is a normal part of the development process. There are some mistakes that are made more often than others. It is good to be aware of them so that they can be corrected before it’s made. Once we can do that then we can develop apps a lot easier and become a proficient JavaScript developer since we can develop apps that makes less bugs. We will go through some of the more common bugs that are made so we can avoid them here

1. Confusing the Equals

In JavaScript, single equal, double equal, and tripe equal all have their own meanings. Single equal sign is the assignment operator which assigns the value on the left to the variable to the right. Double equal sign is the equality check operator which checks an object’s content for equality without checking its type. The triple equals operator checks an object’s content and the type for equality.

We use the single equal assignment operator like the following:

let a = 1

This is used for setting variable values, so it’s we use single equal in a place that shouldn’t be used like in:

if (a = 1){ ... }

then we would be assigning 1 to a which is always true . This means that the code inside the if statement always runs. That’s definitely not what we want.

Double equal sign shouldn’t be too too much since it doesn’t check the type of the object when checking for equality, so something like:

1 == '1'

if true . This creates problems because if we are only checking for number 1 and we get string with 1 in it, it’s still true. We want to convert them to the same type and then compare them with === to check for equality to avoid ambiguity. So if we want to compare if all forms of 1 are equal to each other we convert them to number first by using:

Number(1) === Number('1')

1 wouldn’t be equal to '1' is we use === since they are different types. This would make things a lot more clear. If we want to make sure something aren’t equal to each other then we can use !== to check if they don’t have the same content or the same type, so:

1 !== '1'

would be true .

2. Mismatching Brackets

Nested statements and functions in each other means that there’re multiple levels of brackets in each file. Usually, apps are pretty complex so the levels can add up. This means that mismatching brackets is easy if you use a text editor that doesn’t support syntax highlight or check for mismatched brackets. This can easily be avoided with modern text editors such as Visual Studio Code, Atom, and Sublime. If we want to use simpler text editors, then use linters and code formatting tools like ESLint and Prettier to detect these issues. They also let us format code automatically and detect common style issues that may occur like quote style inconsistencies, number of characters in a line, functions that can be shortened, etc.

3. Mismatching Quotes

JavaScript lets us use single quotes, double quotes, and backticks for strings. They are equivalent. However, we should open and close with the same character. So if we open a string with single quote then use single quote to close a string, and if we started with double quotes or backticks, then use those to close the string respectively. Also, special characters like quotation marks have to escape to be included in a string in some cases. If you open a string with single quotes, then if you use single quotes in a string, then you have to escape that to include it in the string. This also applies to double quotes and backticks. If you use double quotes in a double quoted string then you have to escape it. And if you use a backtick in a template string, then you have to escape the backtick character.

4. Parentheses

In if statements, parentheses always have to wrap around the whole condition. For example, something like:

if (x > y) && (y < 10) {...}

won’t work. The correct way to write that statement is to write is:

if ((x > y) && (y < 10)) {...}

if we want to check that both conditions are true.

5. Semicolon

Semicolons are optional at the end of a JavaScript line. However, to make things clear for other people, we should include it so that people know where a line ends. We can use a code linter or formatter like ESLint or Prettier to do this automatically. Lots of text editors also have add-ons to fix it automatically for you.

6. Capitalization

JavaScript code is case-sensitive, so things that have different capitalization will be considered different even if they have the same spelling. Anything that references them also have to be capitalized correctly. For example, getElementsByTagName should always be spelled this way with exact same capitalization. The general convention in JavaScript is that we spell variables and function names with camel case, so we should stick with that for consistency if we define our own variables.

7. Referencing Code That’s not Yet Loaded

It’s important that we reference code that have been defined and loaded before hand in order to use the code. If we are using script tag then before we reference the code, we have to load the code first. So the script tag that references the script with the variable we want to use should come before the script tag that references the script that has the code that we want to reference.

Fortunately, JavaScript modules are much more common nowadays, so that we just have to remember to export the code that you want to use in another module and the import the exported variable in the module that you want to use the variable before referencing it. JavaScript modules solves a lot problems with script tags. Global variables do not have to used much anymore to let different scripts reference the same variable, and also we can make variables private without the use of closures since we only need to export the ones that are needed elsewhere.

8. Variable Names

In JavaScript, there are many reserved words that cannot be used as variable names. They shouldn’t be used as variable names since they collide the name of the reserved words, creating confusion with the JavaScript interpreter. To avoid this we should use names that are descriptive like firstName , lastName , and things like that.

9. Scope

We shouldn’t use global variables whenever possible. This means that all variables we declare should use the let or const keywords for variables and constants respectively. This avoids issues with global variable scope and overwriting global variable data. It also let us avoid errors from assigning to entities that should be constant or assigning things accidentally that are global.

10. Missing or Unexpected Parameters

When functions are called with expected parameters missing, then unexpected results may be returned because you passed in unexpected input to a function.We have to make sure that we pass in the right parameters to a function so that we get the right results. It also a good idea to check for unexpected parameters if the function is used in lots of places like functions that are in libraries so that we can deal with unexpected parameters gracefully like ending the execution of the function when parameters passed in are of an invalid type or null or undefined .

11. Index Errors

JavaScript arrays starts with index 0, so the last index of an array will always be 1 less than the length of the array. When you pass in an index that is beyond the length or it’s invalid like a negative number, we get undefined , so we will get ReferenceErrors if we try to get the invalid entry and do stuff with it.

12. Null and Undefined

null and undefined are things that are often the values of variables. This means that we have to check for these when we are getting something by traversing the nested properties of objects or looping through an array. When we have null or undefined we want to handle them gracefully. This means that we should always check for them. So we if have an object called foo and we want to access deeply nested properties within it, then we should check for null or undefined in each level, like so:

if (foo && foo.bar && foo.bar.baz){ ... }

This way we know that all the properties are defined so that we can get access the baz property. For arrays if we want to access index i , we should check if it’s defined by writing:

if (arr[i]){...}

so that we won’t get unexpected undefined errors.

The optional chaining operator (?. ) is being worked on, so hopefully it will be finalized soon and we can use it to access deeply nested properties in an object and we don’t have to deal with properties being undefined as much anymore. Also, Lodash has a get function, which can try to access deeply properties of arrays and objects and get the value if it exists or return null if there’s anything undefined in the middle of the object’s hierarchy. This is much better than a long chain of undefined or null checks.

13. Confusing Undefined and Null

JavaScript has both undefined and null for non-values. However, there are quite a few differences between the two. undefined means that the variable may have been declared, but nothing is set to it. A variable can also be explicitly set as undefined . The type of an undefined variable, when checking the type with the typeof operator, will get us the type 'undefined'. Functions that don’t return anything returns undefined. On the other hand, null values have to be explicitly set by functions that return null or just set directly to a variable. When we check an object that has the null value set, we’ll find that the type of it is'object' if a variable has the null value.

For this reason, it’s probably easier to stick to undefined whenever we can when we’re setting variable values to non-values. It reduces confusion and we only have to check that the type of a variable is 'undefined' to see whether it’s undefined. That’s less painful than having two checks, for both null and undefined.

To write functions that return undefined, we don’t have to do anything like the following example:

const f = () => {}

To set a variable that was assigned some other value to undefined, we can write:

x = undefined;

To check if a property value is undefine, we can write:

typeof obj.prop === 'undefined'

or

obj.prop === undefined

To check if a variable is undefined, we can write the following code:

typeof x === 'undefined'

A declared variable that hasn’t been assigned anything automatically has the value undefined.

If we have to check for null, then we can write:

obj.prop === null

or

x === null

for variables. We can’t use the typeof operator for checking null because the data type of null is 'object'.

14. Confusing Addition and Concatenation

In JavaScript, the + operator is used for both adding two numbers and concatenating strings together. Because JavaScript is a dynamic language, the operands are all automatically converted to the same type before the operation is applied. For example, if we have:

let x = 1 + 1;

then we get two because they’re both numbers. The + operation was used for addition like we expected. However, if we have the following expression:

let x = 1 + '1';

then we get '11' because the first operand was coerced into a string before the + operation is applied. The + operation was used for concatenation instead of addition. When we use the + operator on multiple variables, this makes knowing the type even harder. For example, if we have:

let x = 1;
let y = 2;
let z = x + y;

as we expect, we get three because x and y are both numbers. On the other hand, if we have:

let x = 1;
let y = '2';
let z = x + y;

then we get '12' because y is a string, so the + operator was used for concatenation instead. To solve this issue, we should convert all the operands to numbers first before using them with the + operator. For example, we should rewrite the above example into the following:

let x = 1;
let y = '2';
let z = Number(x) + Number(y);

The code above will get us 3 as the value of z since we converted them both to numbers with the Number factory function first. The Number function takes in any object and returns a number if it can be parsed into a number, or NaN otherwise. An alternative way to do this is to use the new Number(...).valueOf() function, as we do in the following code:

let x = 1;
let y = '2';
let z = new Number(x).valueOf() + new Number(y).valueOf();

Since new Number(...) is a constructor that creates an object type, we want to use the valueOf function to convert it back to a primitive type to make sure that what we get is a number type. A shorter way to do this is to write:

let x = 1;
let y = '2';
let z = +x + +y;

The + sign in front of a single operand will try to convert the single operand into a number or toNaN if it can’t be converted into a number. It does the same thing as the Number function. We can also convert a variable to a particular type of number. The Number object has a parseInt function to convert an object into an integer and a parseFloat function to convert an object into a floating-point number. parseInt takes the object you want to convert to a number as the first argument. It also takes a radix as an optional second argument, which is the base of the mathematical numeral systems. If the string starts with 0x, then the radix will be set to 16. If the string starts with anything else, then the radix will be set to 10.

We can use them as in the following examples:

let x = 1;
let y = '2';
let z = Number.parseInt(x) + Number.parseInt(y)

Also, we can use the parseFloat function as in the following code:

let x = 1;
let y = '2';
let z = Number.parseFloat(x) + Number.parseFloat(y)

We will get 3 in both of the examples above.

15. Breaking Return Statements Into Multiple Lines

JavaScript closes a statement at the end, so one line code is considered distinct from the other. For example, if we have:

const add = (a, b) => {
  return
  a + b;
}

we get undefined if we run console.log(add(1, 2)); since we ran the return statement, which ended the function execution, before a + b is run. Therefore, a + b will never be run in this function. To fix this, we either have to put the return statement all in one line or use parentheses to surround what we want to return. For example, we can write:

const add = (a, b) => {
  return a + b;
}

This will log 3 if we run console.log(add(1, 2)); since we are actually returning the computed result in the function. We can also write:

const add = (a, b) => {
  return (
    a + b
  );
}

This is handy for returning expressions that might be longer than one line. This will also log 3 if we run console.log(add(1, 2));. For arrow functions, we can also write:

const add = (a, b) => a + b

for single-line functions since the return statement is implicit for single-line arrow functions. We can also write:

const add = (a, b) => (a + b)

to get the same thing. This also works for single-line arrow functions.

In JavaScript, if a statement is incomplete, like the first line of:

const power = (a) => {
  const
    power = 10;
  return a ** 10;
}

inside the function then the JavaScript interpreter will run the first line together with the second line to get the full statement. So:

const
  power = 10;

is the same as:

const power = 10;

However, for complete statements like return statements, the JavaScript interpreter will treat them as separate lines. So:

return 
  a ** 10;

is not the same as:

return a ** 10;

Even though JavaScript is a friendly language, it’s still very easy to make mistakes when writing JavaScript code. It’s easy to confuse undefined and null when we aren’t familiar with JavaScript. Because of the dynamic typing nature of JavaScript, operators like the + operator that can do multiple things can easily be converted to a type we don’t expect and produce the wrong result. Also, if statements can be complete on their own, then they shouldn’t be written in their own lines if we want both lines to be together.

Yes, that is all. I hope this tutorial will surely help and you if you liked this tutorial, please consider sharing it with others. Thank you !

An Angular Roadmap — The Past, Present, and Future of Angular

An Angular Roadmap — The Past, Present, and Future of Angular

✅Interested in being an Angular developer in 2019? ... blog post it's most likely that you've written some code in javaScript in the past.

Paleolithic JavaScript — SproutCore

In the beginning, there was SproutCore. It was the first comprehensive JavaScript framework aimed at making it easy to build desktop-quality single-page web apps. It’s not that this wasn’t possible before. When Google released Gmail, it showed the world that web apps really could replace complex desktop applications. Google even open-sourced the Closure toolkit — a set of libraries and an optimizing compiler that it used to build Gmail.

The problem was that Google’s Closure tools weren’t very developer-friendly. They relied heavily on Java, which alienated web developers who were used to working with JavaScript, PHP, Ruby, and Python. Gmail was a great demonstration of what was possible, but developing similar applications still felt out of reach for many.

Some courageous developers managed to string together amazing single page apps using a combination of jQuery, duct tape, and hope. While these apps looked amazing to end-users, for the developers working on them, the apps quickly turned into hulking piles of technical debt that made the dev team dread heading to work in the morning.

As a result, a few enterprising developers began to work on frameworks that would bring Gmail-like apps within easy reach of web developers everywhere. SproutCore was the first of these frameworks to take off. It came with a complete set of widgets that made it possible to build complex web applications without even touching HTML or CSS.

This ended up being great for former desktop developers who had been dragged kicking and screaming onto the web. Several more frameworks popped up with similar goals; GWT and Cappuccino were the most prominent. These frameworks even avoided JavaScript by transpiling other languages into JS. Again, this was great for desktop developers. But it left passionate web developers out in the cold and made them feel as though their hard-won HTML, CSS, and JavaScript skills weren’t valuable.

This left an opening for a framework that truly embraced the web, instead of trying to plaster over it and pretend it was something else. A couple of early frameworks (Backbone and Knockout) appeared, and achieved a moderate amount of success. Ember also showed up around this time. It took SproutCore, stripped it down to its bones, and tried to rebuild it into something truly web-friendly. Ember wanted to be the Six Million Dollar Man of the JavaScript world: rebuilt better, stronger, and faster.

None of these frameworks rocketed to popularity. The world was waiting for something better. In 2010, that something better appeared — it was named Angular.

The Golden Age of Angular

Even before Angular version 1.0 had been released, Angular took the front-end development world by storm. Finally, we had an easy-to-use JavaScript framework that treated HTML as a first-class citizen. Developers and designers could now work together to build amazing single-page applications. This came as a relief to designers, who had been annoyed and offended because older frameworks had treated HTML and CSS as tools for barbarians, tools that no civilized developer should have to touch.

The first thing that seemed magical to developers trying Angular for the first time was two-way data binding. Prior to this, most developers had only seen this kind of data binding in desktop frameworks like WPF and Windows Forms. It was great to be able to bind forms and other UI elements to JavaScript model objects. While two-way data binding could cause performance problems when overused, teams that used it judiciously found that Angular enabled them to create complex front-end applications much more quickly than ever before.

Angular proved to be popular for more than just easy binding of data to HTML elements. Angular directives provided an easy way to create reusable HTML + CSS components. Although other JavaScript frameworks provided this before Angular, Angular was the first one that became extremely popular. Reusable components had long been in-use in server-side frameworks. ASP.NET user controls and partial templates in Rails and Django are but a few examples.

Finally, Angular made dependency injection popular in the front-end development world. Dependency injection had long been popular in enterprise applications, which is perhaps why it hadn’t caught on in the JavaScript world. Front-end developers have long been averse to what they see as needlessly complex enterprise software design patterns. This concern isn’t without merit. Have you ever, in the course of writing an application, said to yourself “What I really need here is a “SimpleBeanFactoryAwareAspectInstanceFactory?”

Dependency injection, though, has proven its worth. And Angular made dependency injection easy to use for an audience that hadn’t used it much in the past. Need an HTTP client? Or perhaps you’d like to do some animation? No problem. Angular had built-in services for those. Just ask for them, and Angular would inject them into your components. No need to instantiate anything yourself.

Or perhaps you wanted to use the browser’s window or location objects without making it impossible to unit test your components outside of a browser? Angular had you covered there too, with its built-in $window and $location services. At runtime, you’d get the browser objects you were expecting. And when running unit tests server-side in Node.js, you could pass mock services into your components to ensure they behaved as expected in various scenarios.

If all of this wasn’t enough, Angular also made it simple to register and inject your own services. For developers who were used to binding all their data to the DOM and hoping for the best, this was awesome. If you were writing a new front-end app that called for APIs that would cost your company a lot of money if overused, you’d probably prefer to be able to write tests ahead of time to ensure that your application doesn’t try to do something like calling the Twilio API 800 million times.

So you’d create a Twilio service that gets injected at runtime. At testing time, you’d create a mock service that records the cost of every API call your application is trying to make. You’d write tests that cover common usage scenarios and ensure that these scenarios don’t result in your company receiving a 7-figure bill. Overall, most developers found that Angular directives combined with dependency injection made it possible to write modular, testable front-end applications using tried-and-true software engineering techniques. Many development teams decided that this resulted in a happy state of affairs, and decided to go all-in on Angular.

The Angular Decline? The Rise of React

While things were mostly great in the world of Angular, it wasn’t all sunshine and lollipops. Developers were starting to run into severe performance problems when they tried to bind too many model objects to too many DOM elements. Some applications slowed to a crawl. Direct calls to $digest and other black-magic sorcery started becoming necessary to achieve acceptable performance. Around the same time, a new challenger appeared: React. At first, React didn’t seem to pose too large a danger to Angular. After all, these React weirdos had gone to the trouble of inventing JSX, which looked a lot like a way to mix markup into your code. Hadn’t we gone to a lot of trouble to invent templating languages for the explicit reason of avoiding mixing markup and code?

As it turned out, React’s approach was pretty popular in the front-end development community. It didn’t rocket to popularity, however. Angular was still dominant, and it looked like it would remain that way. Until that is, Angular’s popularity was given a good kick in the teeth from an unexpected source: the Angular team itself.

The Introduction of Angular 2

Angular 2 was first announced at the ng-europe conference in 2014. The Angular team’s plans came as a shock to the Angular community, to say the least. Reaction from Angular developers was swift and negative… and not without reason. Angular 2 would be getting rid of many familiar concepts from Angular 1, introducing a new, incompatible templating language (and oh, by the way) would also be programmed using an entirely new language.

AngularJS

Although both Angular 1 and Angular 2 were called ‘Angular,’ in reality, they were very different frameworks with a few things in common. To help prevent confusion, the Angular team started referring to the old version of Angular as ‘AngularJS’, and the new version as simply ‘Angular.’ This makes intuitive sense since AngularJS was written in JavaScript, and Angular was not. To keep the distinction between the frameworks clear, I’ll be referring to Angular 1 as AngularJS from this point forward.

As a result of all of this, AngularJS developers lost faith in the framework’s future. They threatened to move to a new framework on future projects, and that is precisely what many of them did. React was the biggest beneficiary of the mass exodus from AngularJS.

Although React didn’t do as much as AngularJS, in a way that was positive. If you’re using a view library that doesn’t try to include everything plus the kitchen sink, it’s a lot more difficult for the developers of that library to pull the rug out from under you in the future. In the beginning, using React was a bit of a pain compared to AngularJS. You had to cobble together a patchwork of libraries just to cover the functionality the AngularJS provided out of the box.

Many teams saw this as a good way to reduce risk, because it was unlikely that the developers of all of those libraries would decide to make backward incompatible breaking changes at the same time, which is essentially what Angular had done.

The Emergence of Vue

To compound AngularJS’ woes, another framework named Vue showed up at about the same time the drama over Angular 2 was occurring. Vue was inspired by AngularJS but aimed to simplify it and get rid of what Vue’s creator saw as unnecessary complexity (so Vue felt very familiar to existing AngularJS developers). Vue provided a safe haven for many AngularJS developers who didn’t want to move over to React.

This doesn’t mean that AngularJS developers were not waiting patiently for Angular 2 to appear. But it’s clear that there was a mass exodus from AngularJS to React and Vue due to the uncertainty caused by the plans for Angular 2.

Rising From the Ashes with Angular 2

Eventually, Angular 2 was released. As expected, it did away with many familiar concepts from AngularJS but kept a few of the best pieces like services and dependency injection. Fortunately for the sanity of developers, Angular uses plain TypeScript and not a fork as originally planned.

To make things more confusing, the Angular team maintained a fork of the new framework that used the Dart programming language instead of TypeScript. Initially, the TypeScript and Dart versions were developed in sync, generated from a single code base. Eventually, the TS and Dart versions of Angular decided to go their separate ways, and Angular Dart now exists on its own.

Even with this confusion, Angular’s popularity began to increase again after the Angular 2 release. It happened slowly. As often occurs in software development, trends shifted. Developers realized that a big, batteries-included framework might actually be useful. After all, when your application grows large enough, you end up actually needing all of those batteries.

Enterprise developers, in particular, began moving back to Angular. This makes sense. Usually, when you start an enterprise web app, you know it is going to be complex. There’s no point in starting with a tiny MVP when you know from the beginning all 87 things your application is going to be expected to do.

Where’s Angular 3?

Although Angular 2 wasn’t perfect, many developers of complex web applications began to realize that the new-and-improved Angular was a good fit for their needs. Fortunately for them, Angular had some exciting improvements in store. More importantly, the Angular team demonstrated that it could consistently publish new versions of the framework with few breaking changes between versions

In a move that seemed odd at the time, the Angular team decided to skip version 3 entirely and move to version 4. This was done for good reason: the team working on Angular’s router package had already pushed ahead and released version 3, while the remainder of Angular was still at version 2.3. They decided to keep all Angular package versions in sync moving forward, and bumping everything up to version 4 for the next release was the easiest way to achieve this.

Angular 4

Angular 4 had some significant changes, including added ahead of time compilation, which resulted in small production JavaScript bundles and shorter application load time. Support for server-side rendering was added, which was a boost for teams that wanted to render their app ahead of time to improve initial load performance. Many other improvements were added throughout the framework, but upgrading apps from Angular 2 to 4 was quick and painless in most cases.

Angular 4.3 and Angular 5

Angular 4.3 added a new HTTP client that was easier to use than the old HTTP service. In Angular 5, the old HTTP service was deprecated and would be dropped in the next release. In spite of this inconvenience, there was relatively little grumbling because the upgrade in most cases was straightforward. Angular 5 also added better internationalization support and further build optimizations.

Angular 6 and 7

Angular 6 and 7 were disappointing to some developers. There were no large changes, but there were many small quality of life improvements, especially to the Angular CLI tooling. The decreasing number of visible changes isn’t an indication that the Angular team has stopped innovating. Instead, it shows that the framework is mature, so the development team is now free to do more work behind the scenes, fixing bugs and improving performance.

The stability of the framework since the release of Angular 2 has drawn some old-school AngularJS developers back into the Angular world. It has also attracted the attention of enterprise development teams. When you’re building enterprise apps that may live for decades it’s ideal to use a framework that gets new releases on a predictable schedule but doesn’t change too rapidly. A developer who had only used Angular 2 could be up and running and contributing to an Angular 7 app within minutes.

The Future of Angular

Angular 8 and Angular Ivy

And that brings us to today. As we’ve seen, Angular has come a long way. It has gone from loved by web developers to being reviled to being admired, although it isn’t yet loved again like it was in its early days.

On the horizon, we have Angular 8. A ton of work has been done in Angular 8 to make it easy to use with the Bazel build system, which is absolutely amazing news for all 3 developers who are using it outside of Google. More excitingly, though, the Angular team is hard at work on a new rendered called Angular Ivy. It’s intended to be a drop-in replacement for the current rendered. For the most part, current apps won’t need to make any changes to use Angular Ivy.

If Angular Ivy is a drop-in replacement, why should developers care? Two important reasons: speed, and bundle size — two very important concerns. For a few years, it seemed like web developers had gone a bit crazy. Teams were shipping JavaScript bundles that were 5MB, 10MB, or even larger, and thinking that there was no problem with this. After all, the applications worked perfectly on the developers’ i7-powered MacBook Pros so they should work fine for everyone, right?

Unfortunately, in the real world, not everyone is running the latest and greatest hardware. Hundreds of millions of people access the internet solely on older Android phones with slightly more processing power than a potato, through internet connections only a little faster than dial-up. For these users, a huge JavaScript bundle takes forever to load, and even longer for their device to parse and run. Even in less extreme cases, there are countless users around the world who aren’t using the latest and greatest hardware. For them, massive JavaScript apps are usable (but painful).

Angular Ivy Expectations

The Angular Ivy renderer will help in several ways:

  1. It is being written with an eye on efficiency, so it will accomplish the same tasks while executing far fewer CPU instructions. This will improve both the battery life and the sanity of users with less-than-powerful devices.
  2. The renderer will be written in a much more modular fashion that the current renderer. This will make it much more amenable to tree-shaking and dead code elimination. As a result, your production JavaScript bundle will include only the code that is needed to run your application, instead of bundling together everything plus the kitchen sink as often happens with the current rendered.
  3. In addition to the bundle-size reduction and improved rendering speed, Angular Ivy has another few quality-of-life enhancements for Angular developers. Rebuild times are significantly faster. So if you’re running your app in development mode and waiting for your changes to appear, you’re now going to be spending a lot less time waiting.
  4. Template-type checking is improved, which means you’ll catch more errors at compile time instead of at runtime. Runtime template bugs are annoying, because they either bite you during testing, or they bite your users when they’re trying to use your app.
  5. The Angular Ivy template compiler will generate code that is human readable, which the current View Engine compiler doesn’t do. This will come in handy when trying to track down tough template bugs.

The net result: smaller apps, faster apps, happier developers, and happier users.

Angular’s Past, Present, and Future

If you’ve been using Angular from its early days all the way until now, then congratulations! While there have been plenty of rough patches, we’ve ended up with a fast, modern framework that is fun to use.

If you were an AngularJS developer but moved on to React, Vue, or something else, I encourage you to give Angular another look. It’s worth your time, even if you decide to stick with what you’re using now.

And if you’ve never used Angular at all, why not give it a shot?

We’ve just been on a whirlwind tour through Angular’s past, present, and future. Without a doubt, it has been quite a ride. Regardless of your Angular background, I hope you’ve enjoyed the tour!

30s ad

Angular 2 Firebase - Build a Web App with Typescript

Angular 2 Demystified

Master Angular 2 - The No Nonsense Course

Complete Angular 7 - Ultimate Guide - with Real World App

A Quick Guide to Angular 7 in 4 Hours