Angular MVC - A Primer

Angular MVC - A Primer

In this tutorial, I want to talk about the major two approaches, the Model-View-Controller (MVC) pattern and the Model-View-ViewModel (MVVM) pattern. In the MVVM pattern, the controller is replaced by a ViewModel. The main differences between these two components are the direction of dependency between the View on one side, and the Controller or ViewModel on the other side.

When designing software with a user interface, it is important to structure the code in a way that makes it easy to extend and maintain. Over time, there have been a few approaches in separating out responsibilities of the different components of an application. Although there is plenty of literature on these design patterns around, it can be very confusing for a beginner to understand the features of limitations of the different patterns and the differences between them.

I will be developing the ideas and explaining the patterns by example using a browser application written in TypeScript and Angular. TypeScript is an extension of JavaScript that adds type information to the code. The application will mimic the popular Notes application on MacOS/iOS. Angular enforces the MVVM pattern. Let’s dive in and see the main differences between the MVC and the MVVM patterns.


Set Up Your Application with Angular CLI

To start off you will need to install Angular CLI. Make sure you have Node and npm installed first. If you haven’t done so, visit node.js.org and follow the instructions to download and install Node. Then, open a terminal on your computer and run the npm command to install Angular CLI.

npm install -g @angular/[email protected]

Depending on your system configuration, you may have to run this command as the system administrator using sudo. This will install the ng command globally on your system. ng is used to create, manipulate, test, and build Angular applications. You can create a new Angular application by running ng new in a directory of your choice.

ng new AngularNotes

This will start a wizard that takes you through a couple of questions about the new application and then creates the directory layout and some files with skeleton code. The first question regards the inclusion of the routing module. Routing lets you navigate to different components in the application by changing the browser path. You will need to answer yes to this question. The second question lets you choose the CSS technology which you want to use. Because I will only include some very simple style sheets, the plain CSS format will be sufficient. When you have answered the questions, the wizard will start downloading and installing all the necessary components.

You can use Material Design and its components to make the application look nice. These can be installed by using the npm command inside the application directory. The ng new command should have created a directory called AngularNotes. Navigate into that and run the following command.

npm install --save @angular/[email protected] @angular/[email protected] @angular/[email protected] @angular/[email protected]

The src directory contains the application source code. Here, src/index.html is the main entry point for the browser. Open this file in a text editor of your choice and paste the following line into the <head> section. This will load the font needed for the Material Icons.

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

The src/style.css style sheet contains global styles. Open this file and paste the following styles into it.

@import "[email protected]/material/prebuilt-themes/deeppurple-amber.css";

body {
margin: 0;
font-family: sans-serif;
}

h1, h2 {
text-align: center;
}

Next, open src/app/app.module.ts. This file contains the imports for all the modules that you want to be globally available. Replace to contents of this file with the following code.

import { BrowserModule } from '@angular/platform-browser';
import { NgModule } from '@angular/core';
import { FormsModule, ReactiveFormsModule } from '@angular/forms';
import { BrowserAnimationsModule } from '@angular/platform-browser/animations';
import { FlexLayoutModule } from "@angular/flex-layout";

import { MatToolbarModule,
MatMenuModule,
MatIconModule,
MatInputModule,
MatFormFieldModule,
MatButtonModule,
MatListModule,
MatDividerModule } from '@angular/material';

import { AppRoutingModule } from './app-routing.module';
import { AppComponent } from './app.component';

@NgModule({
declarations: [
AppComponent,
],
imports: [
BrowserModule,
BrowserAnimationsModule,
FlexLayoutModule,
FormsModule,
ReactiveFormsModule,
MatToolbarModule,
MatMenuModule,
MatIconModule,
MatInputModule,
MatFormFieldModule,
MatButtonModule,
MatListModule,
MatDividerModule,
AppRoutingModule,
],
bootstrap: [AppComponent]
})
export class AppModule { }

At this point, I could start showing you how to create the application layout in the file src/app/app.component.html. But this would already have me dive into the discussion of the application architecture. Instead, in the next section, I want to first guide you through the implementation of the Model. I will be discussing the View and its relation to the ViewModel in the following section.

The Model

The model contains the business end of your application. For simple CRUD (Create Read Update Delete) applications, the model is usually a simple data model. For more complex applications, the model will naturally reflect that increase in complexity. In the application you see here, the model will hold a simple array of text notes. Each note has an ID, a title, and a text. In Angular, the model is coded up in so-called services. The ng command lets you create a new service.

ng generate service Notes

This will create two new files, src/app/notes.service.ts and src/app/notes.service.spec.ts. You can ignore the second of these files in this tutorial, just as the other .spec.ts files. These files are used for unit testing the code. In an application that you want to release for production, you would write your tests there. Open src/app/notes.service.ts and replace its contents with the following code.

import { Injectable } from '@angular/core';
import { BehaviorSubject, Observer } from 'rxjs';

export class NoteInfo {
id: number;
title: string;
}

export class Note {
id: number;
title: string;
text: string;
}

@Injectable({
providedIn: 'root'
})
export class NotesService {
private notes: Note[];
private nextId = 0;
private notesSubject = new BehaviorSubject<NoteInfo[]>([]);

constructor() {
this.notes = JSON.parse(localStorage.getItem('notes')) || [];
for (const note of this.notes) {
if (note.id >= this.nextId) this.nextId = note.id+1;
}
this.update();
}

subscribe(observer: Observer<NoteInfo[]>) {
this.notesSubject.subscribe(observer);
}

addNote(title: string, text: string): Note {
const note = {id: this.nextId++, title, text};
this.notes.push(note);
this.update();
return note;
}

getNote(id: number): Note {
const index = this.findIndex(id);
return this.notes[index];
}

updateNote(id: number, title: string, text: string) {
const index = this.findIndex(id);
this.notes[index] = {id, title, text};
this.update();
}

deleteNote(id: number) {
const index = this.findIndex(id);
this.notes.splice(index, 1);
this.update();
}

private update() {
localStorage.setItem('notes', JSON.stringify(this.notes));
this.notesSubject.next(this.notes.map(
note => ({id: note.id, title: note.title})
));
}

private findIndex(id: number): number {
for (let i=0; i<this.notes.length; i++) {
if (this.notes[i].id === id) return i;
}
throw new Error(Note with id ${id} was not found!);
}
}

Near the top of the file you can see two class definitions, NoteInfo and Note. The Note class contains the full information on a note, while NoteInfo only contains the id and the title. The idea is that NoteInfo is much lighter and can be used in a list, displaying all note titles. Both Note and NoteInfo are simple data classes, containing no business logic. The logic is contained in NotesService, which acts as the Model of the application. It contains a number of properties. The notes property is an array of Notes objects. This array acts as the source of truth for the model. The functions addNote, getNote, updateNote, and deleteNote define the CRUD operations on the model. They all directly act on the notes array, creating, reading, updating, and deleting elements in the array. The nextId property is used as a unique ID by which a note can be referenced.

You will notice that, whenever the notes array is modified, the private update method is called. This method does two things. First, it saves the notes in the local storage. As long as the browser’s local storage has not been deleted, this will persist the data locally. This allows users to close the application and open it later on and still have access to their notes. In a real-world application, the CRUD operations would access a REST API on a different server, instead of saving the data locally.

The second action performed by update is to emit a new value on the notesSubject property. notesSubject is a BehaviorSubject from RxJS which contains an array of the condensed NoteInfo objects. The BehaviorSubject act as an observable to which any observer can subscribe. This subscription is made possible through the subscribe method of NotesService. Any observer that has subscribed will be notified whenever update is called.

The main thing to take away from the implementation of the Model is, that the Model is a standalone service that has no knowledge of any View or Controller. This is important in both, the MVC and the MVVM architecture. The Model must not have any dependency on the other components.


The View

Next, I’d like to turn your attention to the View. In Angular applications, the View lives inside the .html templates and the .css style sheets. I have already mentioned one of these templates in the file src/app/app.component.html. Open the file and paste the following content into it.

<mat-toolbar color="primary" class="expanded-toolbar">
<span>
<button mat-button routerLink="/">{{title}}</button>
<button mat-button routerLink="/"><mat-icon>home</mat-icon></button>
</span>
<button mat-button routerLink="/notes"><mat-icon>note</mat-icon></button>
</mat-toolbar>
<router-outlet></router-outlet>

Why not add a bit of styling too? Open src/app/app.component.css and add the following style.

.expanded-toolbar {
justify-content: space-between;
align-items: center;
}

The app.component contains the main page layout, but not any meaningful content. You will have to add some components that will render any content. Use the ng generate command again like this.

ng generate component Home
ng generate component Notes

This generates two components. Each component is made up of a .html, .css, and a .ts file. For now, don’t worry about the .ts file. I’ll get to that in the next section. (Remember, there is also a .spec.ts file that I am ignoring completely in this tutorial.)

Open src/app/home/home.component.html and change the content to the following.

<h1>Angular Notes</h1>
<h2>A simple app showcasing the MVVM pattern.</h2>

Next, open src/app/notes/notes.component.html and replace the content with the code below.

<div fxLayout="row" fxLayout.xs="column" fxLayoutAlign="center" class="notes">
<mat-list fxFlex="100%" fxFlex.gt-sm="20%">
<mat-list-item *ngFor='let note of notes'>
<a>
{{note.title}}
</a>
</mat-list-item>
</mat-list>
<mat-divider fxShow="false" fxShow.gt-sm [vertical]="true"></mat-divider>
<mat-divider fxShow="true" fxShow.gt-sm="false" [vertical]="false"></mat-divider>
<div fxFlex="100%" fxFlex.gt-sm="70%" *ngIf="!editNote" class="note-container">
<h3>{{currentNote.title}}</h3>
<p>
{{currentNote.text}}
</p>
<div fxLayout="row" fxLayoutAlign="space-between center" >
<button mat-raised-button color="primary">Edit</button>
<button mat-raised-button color="warn">Delete</button>
<button mat-raised-button color="primary">New Note</button>
</div>
</div>
<div fxFlex="100%" fxFlex.gt-sm="70%" *ngIf="editNote" class="form-container">
<form [formGroup]="editNoteForm">
<mat-form-field class="full-width">
<input matInput placeholder="Title" formControlName="title">
</mat-form-field>

  &lt;mat-form-field class="full-width"&gt;
    &lt;textarea matInput placeholder="Note text" formControlName="text"&gt;&lt;/textarea&gt;
  &lt;/mat-form-field&gt;
  &lt;button mat-raised-button color="primary"&gt;Update&lt;/button&gt;
&lt;/form&gt;

</div>
</div>

The accompanying src/app/notes/notes.component.css should look like this.

.notes {
padding: 1rem;
}

.notes a {
cursor: pointer;
}

.form-container, .note-container {
padding-left: 2rem;
padding-right: 2rem;
}

.full-width {
width: 80%;
display: block;
}

So far, so good!

Have a look at src/app/notes/notes.component.html which represents the main View of the application. You will notice placeholders such as `` which look like they can be filled with values. In the version shown above, the View does not seem to refer to any piece of code in the application.

If you were to follow the MVC pattern, the View would define slots into which the data could be inserted. It would also provide methods for registering a callback whenever a button is clicked. In this respect, the View would remain completely ignorant of the Controller. The Controller would actively fill the values and register callback methods with the View. Only the Controller would know about both the View and the Model and link the two together.

As you will see below, Angular takes a different approach, called the MVVM pattern. Here the Controller is replaced by a ViewModel. This will be the topic of the next section.


The ViewModel

The ViewModel lives in the .ts files of the components. Open src/app/notes/notes.component.ts and fill it with the code below.

import { Component, OnInit } from '@angular/core';
import { Note, NoteInfo, NotesService } from '../notes.service';
import { BehaviorSubject } from 'rxjs';
import { FormGroup, FormBuilder, Validators } from '@angular/forms';

@Component({
selector: 'app-notes',
templateUrl: './notes.component.html',
styleUrls: ['./notes.component.css']
})
export class NotesComponent implements OnInit {
notes = new BehaviorSubject<NoteInfo[]>([]);
currentNote: Note = {id:-1, title: '', text:''};
createNote = false;
editNote = false;
editNoteForm: FormGroup;

constructor(private formBuilder: FormBuilder,
private notesModel: NotesService) { }

ngOnInit() {
this.notesModel.subscribe(this.notes);
this.editNoteForm = this.formBuilder.group({
title: ['', Validators.required],
text: ['', Validators.required]
});
}

onSelectNote(id: number) {
this.currentNote = this.notesModel.getNote(id);
}

noteSelected(): boolean {
return this.currentNote.id >= 0;
}

onNewNote() {
this.editNoteForm.reset();
this.createNote = true;
this.editNote = true;
}

onEditNote() {
if (this.currentNote.id < 0) return;
this.editNoteForm.get('title').setValue(this.currentNote.title);
this.editNoteForm.get('text').setValue(this.currentNote.text);
this.createNote = false;
this.editNote = true;
}

onDeleteNote() {
if (this.currentNote.id < 0) return;
this.notesModel.deleteNote(this.currentNote.id);
this.currentNote = {id:-1, title: '', text:''};
this.editNote = false;
}

updateNote() {
if (!this.editNoteForm.valid) return;
const title = this.editNoteForm.get('title').value;
const text = this.editNoteForm.get('text').value;
if (this.createNote) {
this.currentNote = this.notesModel.addNote(title, text);
} else {
const id = this.currentNote.id;
this.notesModel.updateNote(id, title, text);
this.currentNote = {id, title, text};
}
this.editNote = false;
}
}

In the @Component decorator of the class, you can see the reference to the View .html and .css files. In the rest of the class, on the other hand, there is no reference to the View whatsoever. Instead, the ViewModel, contained in the NotesComponent class, exposes properties and methods that can be accessed by the View. This means that, compared to the MVC architecture, the dependency is reversed. The ViewModel has no knowledge of the View but provides a Model-like API that can be used by the View. If you take another look at src/app/notes/notes.component.html you can see that the template interpolation, such as `` directly accesses the properties of the NotesComponent.

The last step to make your application work is to tell the router which components are responsible for the different routes. Open src/app/app-routing.module.ts and edit the content to match the code below.

import { NgModule } from '@angular/core';
import { Routes, RouterModule } from '@angular/router';
import { HomeComponent } from './home/home.component';
import { NotesComponent } from './notes/notes.component';

const routes: Routes = [
{ path: '', component: HomeComponent },
{ path: 'notes', component: NotesComponent },
];

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

This will link the HomeComponent to the default route and the NotesComponent to the notes route.

For the main application component, I will define a few methods which will be implemented later on. Open src/app/app.component.ts and update the content to look like the following.

import { Component } from '@angular/core';

@Component({
selector: 'app-root',
templateUrl: './app.component.html',
styleUrls: ['./app.component.css']
})
export class AppComponent {
public title = 'Angular Notes';
public isAuthenticated: boolean;

ngOnInit() {
this.isAuthenticated = false;
}

login() {
}

logout() {
}
}

The component contains two properties title and isAuthenticated. The second one of these is a flag that indicates whether the user has logged into the application. Right now, it is simply set to false. Two empty methods act as callbacks to trigger logging in or logging out. For now, I have left them empty, but you will be filling them in later on.


Complete the View

With this knowledge about the direction of dependency, you can update the View so that the buttons and forms perform actions on the ViewModel. Open src/app/notes/notes.component.html again and change the code to look like this.

<div fxLayout="row" fxLayout.xs="column" fxLayoutAlign="center" class="notes">
<mat-list fxFlex="100%" fxFlex.gt-sm="20%">
<mat-list-item *ngFor='let note of notes | async'>
<a (click)="onSelectNote(note.id)">
{{note.title}}
</a>
</mat-list-item>
</mat-list>
<mat-divider fxShow="false" fxShow.gt-sm [vertical]="true"></mat-divider>
<mat-divider fxShow="true" fxShow.gt-sm="false" [vertical]="false"></mat-divider>
<div fxFlex="100%" fxFlex.gt-sm="70%" *ngIf="!editNote" class="note-container">
<h3>{{currentNote.title}}</h3>
<p>
{{currentNote.text}}
</p>
<div fxLayout="row" fxLayoutAlign="space-between center" >
<button mat-raised-button color="primary" (click)="onEditNote()" *ngIf="noteSelected()">Edit</button>
<button mat-raised-button color="warn" (click)="onDeleteNote()" *ngIf="noteSelected()">Delete</button>
<button mat-raised-button color="primary" (click)="onNewNote()">New Note</button>
</div>
</div>
<div fxFlex="100%" fxFlex.gt-sm="70%" *ngIf="editNote" class="form-container">
<form [formGroup]="editNoteForm" (ngSubmit)="updateNote()">
<mat-form-field class="full-width">
<input matInput placeholder="Title" formControlName="title">
</mat-form-field>

  &lt;mat-form-field class="full-width"&gt;
    &lt;textarea matInput placeholder="Note text" formControlName="text"&gt;&lt;/textarea&gt;
  &lt;/mat-form-field&gt;
  &lt;button mat-raised-button color="primary"&gt;Update&lt;/button&gt;
&lt;/form&gt;

</div>
</div>

You can see (click) handlers in various places directly referring to the methods of the NotesComponent class. This means that the View needs to know about the ViewModel and its methods. The reason for reversing the dependency is the reduction of boilerplate code. There is a two-way data binding between the View and the ViewModel. The data in the View is always in sync with the data in the ViewModel.


Add Authentication to Your Angular App

A good application is not complete without proper user authentication. In this section, you will learn how to quickly add authentication to your existing Angular application. Okta provides single sign-on authentication which can be plugged into the app with just a few lines of code.

To start off, you will need a free developer account with Okta. In

your favorite browser, open https://developer.okta.com, locate Create Free Account in the center of the page and click it. Simply fill in the form that appears with your details, accept the terms & conditions, and submit it by pressing Get Started. Once you have completed the registration you will be taken to the Okta dashboard. Here you can see an overview of all the applications registered with the Okta service.

Click on Add Application to register a new application. On the next screen that appears you will be given a choice of the type of application. The Single-Page Application is the right choice for your Angular app. On the page that follows, you will be shown the application settings. You will need to change the port number to 4200 when you are testing your application with ng serve.

That’s it. Now you should be seeing a Client ID which you will need later on. Now you are ready to include the authentication service into your code. Okta provides a convenient library for Angular. You can install it by running the following command in your application root directory.

npm install @okta/[email protected] --save

Open app.module.ts and import the OktaAuthModule.

import { OktaAuthModule } from '@okta/okta-angular';

Further down, in the same file add the following in the list of imports.

    OktaAuthModule.initAuth({
issuer: 'https://{yourOktaDomain}/oauth2/default',
redirectUri: 'http://localhost:4200/implicit/callback',
clientId: '{clientId}'
})

In this snippet, {clientId} needs to be replaced with the client ID that you just obtained in the Okta developer dashboard.

To protect specific routes from being accessed without a password you need to modify src/app/app-routing.module.ts. Add an import for OktaCallbackComponent and OktaAuthGuard.

import { OktaCallbackComponent, OktaAuthGuard } from '@okta/okta-angular';

Next, add another route to the array of routes.

{ path: 'implicit/callback', component: OktaCallbackComponent }

The implicit/callback route will be called by Okta when the user has completed the login process. The OktaCallbackComponent handles the result and redirects the user to the page that requested the authentication process. To guard individual routes, you can now simply add OktaAuthGuard to that route, like this.

{ path: 'notes', component: NotesComponent, canActivate: [OktaAuthGuard] }

Remember that you have left the main application ViewModel un-implemented. Open src/app/app.component.ts again and add the following import to the top of the file.

import { OktaAuthService } from '@okta/okta-angular';

Next, implement all the methods of the AppComponent class.

constructor(public oktaAuth: OktaAuthService) {}

async ngOnInit() {
this.isAuthenticated = await this.oktaAuth.isAuthenticated();
}

login() {
this.oktaAuth.loginRedirect();
}

logout() {
this.oktaAuth.logout('/');
}

There is only one thing left to do. You can now add the Login and Logout buttons to the top bar. Open src/app/app.component.html and add these two lines inside the <mat-toolbar> element, after the closing </span>.

<button mat-button *ngIf="!isAuthenticated" (click)="login()"> Login </button>
<button mat-button *ngIf="isAuthenticated" (click)="logout()"> Logout </button>

The Login and Logout buttons are linked to the login() and logout() methods in the app.component.ts ViewModel. The visibility of these two buttons is determined by the isAuthenticated flag in the ViewModel.

That’s all there is to it! Now you have a complete application based on the MVVM architecture, complete with authentication. You can test it out by firing up the Angular test server in the application root directory.

ng serve

Open your browser and navigate to http://localhost:4200. You should see something like this.

Thanks for reading ❤

If you liked this post, share it with all of your programming buddies!

Follow me on Facebook | Twitter

Learn More

Angular 7 (formerly Angular 2) - The Complete Guide

Angular & NodeJS - The MEAN Stack Guide

The Web Developer Bootcamp

Node, Express, Angular 7, GraphQL and MongoDB CRUD Web App

React vs. Vue vs. Angular

Angular + Typescript = Powerful Web Apps

Angular + WebSocket + Node.js Express = RxJS WebSocketSubject ❤️

Ionic 4, Angular 7 and Cordova Crop and Upload Image

Angular 7 Routing Tutorial with Example

What’s New in Angular 8.0

Originally published on https://developer.okta.com


Mobile App Development Company India | Ecommerce Web Development Company India

Mobile App Development Company India | Ecommerce Web Development Company India

Best Mobile App Development Company India, WebClues Global is one of the leading web and mobile app development company. Our team offers complete IT solutions including Cross-Platform App Development, CMS & E-Commerce, and UI/UX Design.

We are custom eCommerce Development Company working with all types of industry verticals and providing them end-to-end solutions for their eCommerce store development.

Know more about Top E-Commerce Web Development Company

Web Development Services

Web Development Services

As one of the best Web Application Development Company, it provides a fine quality mobile app development service at an affordable price. Especially, it encourage start-ups that have unique ideas, by offering a more competitive price

HireFullStackDeveloperIndia is rated as one of the top Web Application Development Company in India by various industry magazines and review sites. They have a right blend of award-winning designers, expert programmers and Google certified digital marketers which make them a unique one-stop solution for hundreds of our clients, spread across all countries.

A Good website reflects not only your business but also it is one of the main factors why a potential customer would convert into Client. A good website design helps increase traffic driving leads to grow business. The best web design company create a custom design for each corporate website so as to help them meet their business goals.

Get Quote: https://hirefullstackdeveloperindia.com/get-a-quote/

Top Web Application Developer

Top Web Application Developer

You can also contact a web application development company for your business but then why not to contact the best web application development company that can turn up your business and customer satisfaction ratio to sky touching heights.

Not long-ago internet came into existence and the world has never been the same ever since. The Internet made sure that people and business do evolve at a faster rate than ever and was never merciful to the slow ones. Because of this competition and availability of any business with few clicks made India one of the hubs as IT center. This trend of constantly updating has given rise to smartphones, smart machines, wearable gadgets and a lot more is yet to come in the upcoming years. In such time it is always a good idea to hire Web App Developer from India at your service. They are expert in developing not only websites but web applications as well.

We at HireFullStackDeveloperIndia, have a huge team of experienced developers that have grasped over different domains in front and back end development. You can hire web app developers in India from us with many advantages that you won’t get anywhere else in the industry.

Here is why we are the best option if you are looking forward to hiring web app developers in India:

  • Flexible hiring models available, as per your convenience and requirement
  • Maximum ROI, compared to any other company or team of developers.
  • We provide you with Source code Authorization meaning code written for you belong only to you. It cannot be used or copied anywhere else.
  • All of our developers are sound with agile development methodology, so you will be in a constant loop of suggestions, ideas, trends, and updates about your project.
  • Our developers are good with creating custom web applications as well; it guarantees you a better product without any compromise of non-existing functionalities.
  • You can save huge costing on infrastructure by utilizing our hire web app developer in India program.
  • Your hired developer or team will be easily accessible to your preferred mode of communication.
  • You get to exercise complete control over your team or individual that you hire.
  • We believe in Integrity and Transparency.
  • Our developers are highly creative and motivated to deliver excellence.


HireFullStackDeveloperIndia is a Web Application Development Company in India that is known worldwide for our Innovative guaranteed solutions. You can inquire with us about your project and we will be providing you multiple suitable developers that are the best fit for your requirements. You can evaluate them and select one or multiple whosoever deems fit to you. After this, all you have to do is provide your valuable input to the resource through sprint base project development until you get delivery of your project.

Our engagement model will also allow you to get our development team to your site location and proceed with development from your premises.