Skip to content

Angular

Setting up the Angular Development Environment

  • Node
  • Angular CLI (command npm install -g @angular/cli)

Creating your First Angular Application

To create your first Angular Application in CMD type the below command to create a project. Select angular routing and styling you want to use. the second command is used to run the app and open it in the browser

ng new Appname
ng serve --open

Angular Folder Structure

e2eEnd to End (this is where we write our automated tests).
node_modulethis stores all our 3rd party libraries. At the time of deployment, this is usually converted to a bundle and is deployed
srcsources contain the actual source code.
app folderit contains Modules and components.
assets folderHere we store the static assets of our app.
environment folderIt contains the configuration settings for different environments.
index.htmlDefault file that gets loaded contains angular entry point also note all CSS and scripts are dynamically loaded
main.tsthe starting point to our angular application
polyfill.tsAdvance features that are not supported by most browsers will be converted to their local version and handled
.angular-cli.jsonangular configuration
.editorconfigif working in a team environment make sure this is the same across all users
.gitignoreignore files and folders from the git repo
karmaconfig the JS file it’s a test runner for JS code.
package.jsonevery node project must-have. It shows all the dependencies both for prod and dev.
tsconfig.jsonconfig file for typescript which converts to JS code.

WebPack: Angular uses Webpack a build Automation tool that takes all scripts and files and bundles them and minifies them.

Hot Module Replacement (HMR): Angular uses HMR to compile and reload the projects whenever there are changes made to source code.

Type Script

Lets have a quick review of typescripts:

It is a superset of JavaScript with additional features like strong typing, some Object-oriented features, handles most error at compile-time, and has great tooling.

First Type Script Program

npm install -g typescript

tsc --version

code main.ts //add the below code and then compile it using the below code

tsc main.ts // this will traspile the ts to js and main.ts will be created
function log(message){
console.log(message);
}

var message ='Hello World";

log Message;

Declaration of variables

In JS if a variable is declared with var then its scope is global. So ints we have let which has a local scope.

let number = 5;  //typescript
var number = 5; //JavaScript

Type Annotation

declaring the type of the variable is called type annotation

let a =1;
a ='string';  //fails due to let

let a;  //any type
a =1;
a = true;
a ='string' //will work and wont fail

let a: number;
a =1;
a true ; //fails as a can hold only numbers

ENUM

enum color { Red = 0, Green = 1, Blue = 2}
let backgroundcolor = color.Red;

Type Assertions

It is to tell the compiler that the message property is of type string such that intellscence provides all the methods.

let message;
message ='abc';
let endsWithC = (<string>message).endsWith('c');
endsWithC =(message as string).endsWith('c');

Arrow function

it is similar to Lmada function

let log = function(message){    console.log(message);}
let Dolog =(message) => console.log(message);

Interfaces

interface Point{
    x: number,
    y: number
}

let drawPoint =(point: Point)=>{}

Classes and Objects

These are brought to maintain the cohesion principle. i.e encapsulate the methods and its properties into a unit

class Point{
    x: number;
    y: number;

    constructor(x: number, y: number){
        this.x = x;
        this.y =y;
    }

    draw () {
        console.log('X: ' + this.x + ',Y: ' + this.y);
    }
}

//without constructor
// let point  = new Point();
// point.x =1;
// point.y =2;
// point.draw();

//with constructor
let point = new Point(1,2);
point.draw();

Making parameter optional as in js we can only have 1 constructor

    constructor(x?: number, y?: number){
        this.x = x;
        this.y =y;
    }

//the below will not throw error
// let point  = new Point();
// point.x =1;
// point.y =2;
// point.draw();

Access Modifiers

when a point object is initialized we want to restrict the modification of values of x and y.

  • public
  • private
  • protected
class Point{
    private x: number;
    protected y: number;
}

//now we cant access x from outs

Access Modifiers in the Constructor Parameters

class Point{

    constructor(private x: number, private y: number){
    }

    draw () {
        console.log('X: ' + this.x + ',Y: ' + this.y);
    }
}


let point = new Point(1,2);
point.draw();

Properties

Now if we want to read/write the value of x then we would require properties

    getX(){
        return this.x;
    }

    setX(value){
        if(value){
            throw new Error('value ccannot be ss  th 0.');
            this.x =  value;
        }
    }

point.setX(12);
point.getX();

this can be updated as below

    get X(){
        return this.x;
    }

    set X(value){
        if(value){
            throw new Error('value ccannot be ss  th 0.');
            this.x =  value;
        }
    }

point.X =12;
console.log(point.X);

Angular Building Blocks:

Component:

It encapsulates Data, Template, and Logic of the view. Every app will contain at least one component.

Component syntax basic:

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

@Component({
    selector: 'courses',
    template: '<h2>Courses</h2>',

})
export class coursesComponent {
}

Reference it in App module

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

import { AppComponent } from './app.component';
import { coursesComponent } from './courses.component';

@NgModule({
  declarations: [
    AppComponent,
    coursesComponent
  ],
  imports: [
    BrowserModule
  ],
  providers: [],
  bootstrap: [AppComponent]
})
export class AppModule { }

the most easy way to generate a component is to use cmd script (ng g c componentname) example : ng g c course

Directives:

*ngForused for iteration<li *ngFor = “let course of courses; index as i”>
*ngIfif logic <div *ngIf = “courses.length>0”></div>
[ngSwitch]/*ngSwitchCaseswitch logic
[ngClass]
[ngStyle]

Binding:

interpolation{{ variable }}adding dynamic values to the markup
Property binding[property] =””only one way. from component to DOM. This will work only only when the attribute has a binding with DOM property.
Attribute binding[attr.attributename] =””when DOM properties dont have one to one binding.
class binding[class.nameoftargetclass] =””we can add classes dynamically with it
Style binding[style.property]=””we can add inline styles dynamically
Event binding(event) =””
two way binding[(ngMode)]=””

Pipes

lower case{{ course.title | lowercase }}
number{{ |number }}
decimal like 1.456{{ |number:int.float-round}}
currency{{ |currency:’AUD’:symboltruth}}
date{{ | date:’shortDate’}}

Custom Pipes

<name>.pipe.ts

import { Pipe, PipeTransform } from '@angular/core';

@Pipe({
    name: 'Summary'
})

export class SummaryPipe implements PipeTransform{
    transform(value: string, Limit?: number) {
        if(!value)
        return null;

        let actuallimit = (Limit)?Limit:50;
        return value.substring(0,actuallimit) + '...';
    }

}

component class

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

import { coursesService } from './courses.service';

@Component({
    selector: 'courses',
    template: `
        {{text | Summary:10}}
        `,

})
export class coursesComponent {
    text ="we are creating custom pipes in angular so as to apply our own pipes";
}

app.module

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

import { AppComponent } from './app.component';
import { coursesComponent } from './courses.component';
import { CourseComponent } from './course/course.component';
import { coursesService } from './courses.service';
import { SummaryPipe } from './summary.pipe';

@NgModule({
  declarations: [
    AppComponent,
    coursesComponent,
    CourseComponent,
    SummaryPipe
  ],
  imports: [
    BrowserModule,
    FormsModule
  ],
  providers: [coursesService],
  bootstrap: [AppComponent]
})
export class AppModule { }

Services:

Fetching data directly into a component will tightly couple components to the HTTP endpoints. which makes unit testing a living hell.

let’s say we want to reuse the component with different data then we have to duplicate the component

we should include logic in component to maintain the single responsibility principle.

export class coursesService{
    getCourses() {
        return ["course 1","course 2","course 3"];
    }
}

App Module:

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

import { AppComponent } from './app.component';
import { coursesComponent } from './courses.component';
import { CourseComponent } from './course/course.component';
import { coursesService } from './courses.service';

@NgModule({
  declarations: [
    AppComponent,
    coursesComponent,
    CourseComponent
  ],
  imports: [
    BrowserModule
  ],
  providers: [coursesService],
  bootstrap: [AppComponent]
})
export class AppModule { }

Courses component:

import { Component } from '@angular/core';
import { coursesService } from './courses.service';

@Component({
    selector: 'courses',
    template: `
        <h2>{{ title }}</h2>
        <ul>
            <li *ngFor = "let course of courses">
                {{ course}}
            </li>
        </ul>
        `,

})
export class coursesComponent {
    title = "List of courses";
    courses;

    constructor(service : coursesService){
        this.courses = service.getCourses();
    }

}

Template Variables:

Adding a variable to the html tag and getting value through it

<input  #email (keyup.enter)="onKeyUp(email .value)" />
    onKeyUp(email ){
        console.log(email);
    }

Modules:

A group of component together become a module

input@input(‘alias’) isSelected: boolean
<p [alias] = “value”></p>
output@Output(‘alias’) change= new EventEmitter();
this.change.emit(value);
(change)=”onFavoriteChage($event)”
ng-contain/ng-container

NG CONTAIN

Lets say you are making a reusable component block like below

<div class="panel panel-default">
    <div class="panel panel-heading">Heading
    </div>
    <div class="panel-body">Body
    </div>
</div>

And you want to get the values for Head and Body from the consumer there are 2 ways
1. using input and Output: this may not feel right as in the case of the body you may be wanting to send a body with additional markup.
in these cases ng-content is useful

<div class="panel panel-default">
    <div class="panel panel-heading">
        <ng-content select=".heading"></ng-content>
    </div>
    <div class="panel-body">
        <ng-content select=".body"></ng-content>
    </div>
</div>

Now in App.component.html we just need to add a class by the name heading and body matching the selector

<app-panel>
    <div class="heading">Heading</div>
    <div class="body">Body</div>
</app-panel>

on checking the above we could see that heading in ng content has been replaced by the div of heading.

So let’s say you don’t want additional markup like that div of heading. we can use ng-container.

<app-panel>
    <ng-container class="heading">Heading</ng-container>
    <div class="body">Body</div>
</app-panel>

Check the console to see the difference.

Directives

there are basically 2 types of directives

  • Structural: which modify the structure of the DOM
  • Attribute: which modify the attributes of DOM elements.

Custom Directive

ng g d directive-name
<input type="text" appInputFormat [format] ="'uppercase'">

import { Directive, HostListener, ElementRef, Input } from '@angular/core';

@Directive({
  selector: '[appInputFormat]'
})
export class InputFormatDirective {
  @Input('format') format;
  constructor(private el:ElementRef) { }

  @HostListener('blur') onBlur(){
    let value: string =this.el.nativeElement.value;
    if(this.format == 'lowercase')
    this.el.nativeElement.value = value.toLowerCase();
    else
    this.el.nativeElement.value = value.toUpperCase();
  }

}

in case you want to have appinputformat and format act as a same thing to get both lowercasing and checking done then simple rename format to appinputformat like below

<input type="text" [appInputFormat] ="'uppercase'">

@Input('appInputFormat') format;

Change Detection and trackbyForms

Forms

In Angular we have 2 types of forms

  • Template Driven
  • Reactive

Template Driven Forms

To Create forms in Angular we would need to create a FormControl class and for each input field in our form, we would need to create Formgroup class(control class) with this we can check the current value stored in an input field. Both FormControl and FormGroup class can check

  • value
  • touched
  • untouched
  • dirty
  • pristine
  • valid
  • error
ReactiveTemplate Driven
More Control over Validation LogicLess Control over validation
Good For Complex FormsGood for Simple Forms
unit TestingLess Code and easier to create

For template driven forms there are 2 import things

  • ngmodel
  • name
  • template variable (#firstName) in case you want to use it for passing the error messages

attributes need to be set for a input like below

<form>
    <div class="form-group">
        <label for="firstName">First Name</label>
        <input  
          required           
          ngModel
          name="firstName"
          #firstName="ngModel"
          (change)="log(firstName)"
          id="firstName"
          type="text"
          class="form-control" />
          <div *ngIf="firstName.touched && !firstName.valid" class="alert alert-danger">First Name is Required</div>
    </div>
    <div class="form-group">
      <label for="comment">Comment</label>
      <textarea id="comment" cols="30" rows="10" class="form-control"></textarea>
    </div>
    <button class="btn btn-primary">Submit</button>
  </form>

validation

<form>
    <div class="form-group">
        <label for="firstName">First Name</label>
        <input  
          required   
          minlength="13"
          maxlength="100"
          ngModel
          name="firstName"
          #firstName="ngModel"
          (change)="log(firstName)"
          id="firstName"
          type="text"
          class="form-control" />
          <div *ngIf="(firstName.touched || firstName.dirty) && firstName.invalid" class="alert alert-danger">
              <div *ngIf="firstName.errors.required">First Name is Required</div>
              <div *ngIf="firstName.errors.minlength">name should be atlest {{ firstName.errors.minlength.requiredLength }} characters</div>
              <div *ngIf="firstName.errors.maxlength">name shouldnot exceed 10 characters</div>
          </div>
    </div>
    <div class="form-group">
      <label for="comment">Comment</label>
      <textarea id="comment" cols="30" rows="10" class="form-control"></textarea>
    </div>
    <button class="btn btn-primary">Submit</button>
  </form>

ngForm is a collection of formcontrols and its attribute value which is json representation of the form. we can bind the formcontrols to the submit button using it

we can directly use the ngForm and with formgroup and formcontrol but the problem is if we have a lot of elements in it then your API would need to take them as arguments individually which are really long and untidy.

If we send it as a complex object it would be easy to read and have an extra level of error check so introducing ngModelGroup

<form #f="ngForm" (ngSubmit)="submit(f)">
    <div ngModelGroup="contact" #contact ="ngModelGroup">
        <div class="form-group">
            <label for="firstName">First Name</label>
            <input  
              required   
              minlength="13"
              maxlength="100"
              ngModel
              name="firstName"
              #firstName="ngModel"
              (change)="log(firstName)"
              id="firstName"
              type="text"
              class="form-control" />
              <div 
              *ngIf="(firstName.touched || firstName.dirty) && firstName.invalid" 
              class="alert alert-danger">
                  <div *ngIf="firstName.errors.required">
                      First Name is Required
                    </div>
                  <div *ngIf="firstName.errors.minlength">
                      name should be atlest {{ firstName.errors.minlength.requiredLength }} characters
                    </div>
                  <div *ngIf="firstName.errors.maxlength">
                      name shouldnot exceed 10 characters
                    </div>
              </div>
        </div>
        <div class="form-group">
          <label for="comment">Comment</label>
          <textarea id="comment" cols="30" rows="10" class="form-control"></textarea>
        </div>
    </div>
    <div class="checkbox">
        <label>
            <input type="checkbox" ngModel name="isSubscribed">
            SUbscribe to mailing
        </label>
    </div>
    <p>
        {{ f.value | json }}
    </p>
    <button class="btn btn-primary">Submit</button>
  </form>

See console and go to the value property of ngForm and you will see a complex object called contact within it we have first-name

what are the difference b/w ngForm and ngModelGroup it’s the submit button present in ngForm while the other doesn’t.

disable the button

 <button class="btn btn-primary" [disabled]="!f.valid">Submit</button>

Adding Dropdown

    <div class="form-group">
        <label for="#contactMethod"></label>
        <select ngModel name="contactMethod" id="contactMethod" class="form-control">
            <option></option>
            <option *ngFor="let method of contactMethods" [value]="method.id">
                {{ method.name }}
            </option>
        </select>
    </div>

in case you want complex objects to be bound to value

    <div class="form-group">
        <label for="#contactMethod"></label>
        <select ngModel name="contactMethod" id="contactMethod" class="form-control">
            <option></option>
            <option *ngFor="let method of contactMethods" [ngValue]="method">
                {{ method.name }}
            </option>
        </select>
    </div>

Radio Buttons

    <div class="radio">
        <label>
            <input ngModel type="radio" name="contactMethod" value ="1">
            Email
        </label>
    </div>
    <div class="radio">
        <label>
            <input ngModel type="radio" name="contactMethod" value ="2">
            Phone
        </label>
    </div>

Dynamic radio buttons

    <div *ngFor="let method of contactMethods" class="radio">
        <label >
            <input ngModel type="radio" name="contactMethod" [value] ="method.id">
            {{method.name}}
        </label>
    </div>
<form #form ="ngForm" (ngSubmit) = "submit(form)">
    <div ngModelGroup = courseFormGroup #courseFormGroup ="ngModelGroup">

        <div class="form-group">
            <label for="course Name">Course Name</label>
            <input required minlength="5" ngModel name="CourseName" #CourseName = "ngModel" (change) ="log(CourseName)" id ="CourseName" type="text" class="form-control" />
            <div 
              *ngIf="(CourseName.touched || CourseName.dirty) && CourseName.invalid" 
              class="alert alert-danger">
                  <div *ngIf="CourseName.errors.required">
                      First Name is Required
                    </div>
                  <div *ngIf="CourseName.errors.minlength">
                      name should be atlest {{ CourseName.errors.minlength.requiredLength }} characters
                    </div>
              </div>
        </div>

        <div class="form-group">
            <label for="Category">Category</label>
            <select required
                ngModel 
                name="CategoryDropDown"
                #CategoryDropDown="ngModel"
                class="form-control">
                <option *ngFor="let item of category" [value]="item.id">
                    {{ item.name }}
                </option>
            </select>
            <div *ngIf="CategoryDropDown.touched && CategoryDropDown.invalid" class="alert alert-danger">Category is required</div>
        </div>

        <div class="form-group">
            <input ngModel name = "hasGuarantee" type="checkbox"/>
            <label for="Guarantee">
                30-day money-back guarantee
            </label>
        </div>

    </div>
    {{courseFormGroup.value | json}}
    <button class="btn btn-primary">Submit</button>
</form>
import { Component, OnInit } from '@angular/core';

@Component({
  selector: 'app-course-creator',
  templateUrl: './course-creator.component.html',
  styleUrls: ['./course-creator.component.css']
})
export class CourseCreatorComponent {

  category=[
    {id:1, name: "Development" },
    {id:2, name: "Art" },
    {id:3, name: "Language" }
  ]

  submit(form){
    console.log(form);
  }

  log(x){
    console.log(x);
  }
}

Published inUncategorised

Be First to Comment

Leave a Reply

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