Observables

What is an Observable?

  • Observable can be thought of like an data source.
  • Observable is implemented in a way that it follows Observable pattern.
  • Observer is the subscribe functionality where we handle the Observable in one of the three ways(hooks).
    • Handle Normal Data.
    • Handle Error.
    • Handle Completion.
  • We can execute our code when any of the above section is completed.

HttpClientModule

Below example males a http get request using angular HttpClientModule

app.componnet.html

<input type="text" [(ngModel)]="moduleName" />
<button (click)="getUrl()">Click Me</button>

app.component.ts

import { Component, OnInit } from '@angular/core';
import { HttpClient } from '@angular/common/http';

@Component({
  selector: 'app-root',
  templateUrl: './app.component.html',
  styleUrls: ['./app.component.css']
})
export class AppComponent implements OnInit {
  title = 'HttpClientDemo';
  moduleName: string;
  reponse: any;
  constructor(private http: HttpClient) {}
  ngOnInit(): void {}
  getUrl() {
    console.log('URL Called');
    let url = 'https://jsonplaceholder.typicode.com/comments/' + this.moduleName;
    console.log(url);
    this.reponse = this.http.get(url).subscribe(
      (response) => {
        this.reponse = response;
      }
    );
      console.log(this.reponse);
  }
}

app.module.ts

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

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

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

Validation in ngForm

  • Below example disables the submit button if the validation fails
  • Highlights the field which has failed validation
  • Shows the validation message

app.component.html

<link href="//maxcdn.bootstrapcdn.com/bootstrap/4.0.0/css/bootstrap.min.css" rel="stylesheet" id="bootstrap-css">
<script src="//maxcdn.bootstrapcdn.com/bootstrap/4.0.0/js/bootstrap.min.js"></script>

<script src="//code.jquery.com/jquery-2.1.3.min.js"></script>

<div class="container">
    <div class="row">
        <div class="col-md-3"></div>
        <div class="card border-primary mb-3">
          <div class="card-header" style="background-color: #007BFF; color : white">
            Registeration Form
          </div>
          <div class="card-body">
              <form (ngSubmit)="onSubmit()" #f=ngForm>
                  <div class="form-row">
                      <div class="form-group">
                        <input
                          type="text"
                          class="form-control form-control-sm col-md-12"
                          id="fullName"
                          placeholder="Name"
                          ngModel
                          name="fullName"
                          required><br/>
                          <input
                          type="text"
                          class="form-control form-control-sm col-md-12"
                          id="emailId"
                          placeholder="Email Id"
                          ngModel
                          name="emailId"
                          required email
                          #email="ngModel">
                          <span class="help-block" *ngIf="email.invalid &amp;&amp; email.touched">Please enter a valid email!</span>
                      </div>
                  </div>
                  <div style="text-align:center">
                    <button type="submit" [disabled]="!f.valid" class="btn btn-primary" >Click Me</button>
                </div>
                </form>
          </div>
        </div>
      </div>
</div>

app.component.ts

import { Component, ViewChild } from '@angular/core';
import { NgForm } from '@angular/forms';

@Component({
  selector: 'app-root',
  templateUrl: './app.component.html',
  styleUrls: ['./app.component.css']
})
export class AppComponent {
  @ViewChild('f', {static: false}) registerFrom: NgForm;

  title = 'RegForm';
  onSubmit() {
    console.log(this.registerFrom); // Complete JSON is printed
  }
}

app.component.css

input.ng-invalid.ng-touched{
  border : 1px solid red;
}

app.module.ts

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

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

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

Passing Data in Forms using ngForm and @ViewChild

app.component.html

<link href="//maxcdn.bootstrapcdn.com/bootstrap/4.0.0/css/bootstrap.min.css" rel="stylesheet" id="bootstrap-css">
<script src="//maxcdn.bootstrapcdn.com/bootstrap/4.0.0/js/bootstrap.min.js"></script>

<script src="//code.jquery.com/jquery-2.1.3.min.js"></script>

<div class="container">
    <div class="row">
        <div class="col-md-3"></div>
        <div class="card border-primary mb-3">
          <div class="card-header" style="background-color: #007BFF; color : white">
            Registeration Form
          </div>
          <div class="card-body">
              <form (ngSubmit)="onSubmit()" #f=ngForm>
                  <div class="form-row">
                      <div class="form-group">
                        <input
                          type="text"
                          class="form-control form-control-sm col-md-12"
                          id="fullName"
                          placeholder="Name"
                          ngModel
                          name="fullName"><br/>
                          <input
                          type="text"
                          class="form-control form-control-sm col-md-12"
                          id="emailId"
                          placeholder="Email Id"
                          ngModel
                          name="emailId">

                      </div>
                  </div>
                  <div style="text-align:center">
                    <button type="submit" class="btn btn-primary" >Click Me</button>
                </div>
                </form>
          </div>
        </div>
      </div>
</div>

app.component.ts

import { Component, ViewChild } from '@angular/core';
import { NgForm } from '@angular/forms';

@Component({
  selector: 'app-root',
  templateUrl: './app.component.html',
  styleUrls: ['./app.component.css']
})
export class AppComponent {
  @ViewChild('f', {static: false}) registerFrom: NgForm;

  title = 'RegForm';
  onSubmit() {
    console.log(this.registerFrom); // Complete JSON is printed
  }
}

app.module.ts

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

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

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

Output :

Understanding Form State Json

  • As we saw in the previous blog we are able to get ngForm when printed will provide us the json of all the form elements on which ngModel directive is applied on.
  • When we go in details with in this json we can see control object as key in the json which nested objects in it for each form element present in the form.
  • Now each form control object has a set of property example : dirty, disabled, enabled, touched,vallid and errors
  • Note when viewing the form dom we can see ng-dirty, ng-touched and ng-invalid/ng-valid directives in the form’s input tag

HttpModule : Making HTTP Request

First you need to install HttpModule using npm’s command

npm install -g @angular/http@latest

Demo Below :

app.component.html

<div class="container">
  <div class="row">
    <div class="col-xs-12 col-sm-10 col-md-8 col-sm-offset-1 col-md-offset-2">
      <input type="text" #serverName>
      <button class="btn btn-primary" (click)="onAddServer(serverName.value)">Add Server</button>
      <br><br>
      <button class="btn btn-primary" (click)="onSave()">Save Serves</button>
      <button class="btn btn-primary" (click)="onGet()">Get Serves</button>
      <hr>
      <ul class="list-group" *ngFor="let server of servers">
        <li class="list-group-item">{{ server.name }} (ID: {{ server.id }})</li>
      </ul>
    </div>
  </div>
</div>

app.component.ts

import { ServersService } from './servers.service';
import { Component } from '@angular/core';
import { Response } from '@angular/http';

@Component({
  selector: 'app-root',
  templateUrl: './app.component.html',
  styleUrls: ['./app.component.css']
})
export class AppComponent {
  constructor(private serversService: ServersService) {}
  servers = [
    {
      name: 'Testserver',
      capacity: 10,
      id: this.generateId()
    },
    {
      name: 'Liveserver',
      capacity: 100,
      id: this.generateId()
    }
  ];
  onAddServer(name: string) {
    this.servers.push({
      name: name,
      capacity: 50,
      id: this.generateId()
    });
  }
  onSave(){
    console.log('On Save called');
    this.serversService.storeServers(this.servers)
      .subscribe(
        (response) => console.log(response),
        (error) => console.log(error)
        );
  }
  onGet(){
    console.log('On Get Called');
    this.serversService.getServers().subscribe(
      (response: Response) => {
        const data = response.json();
        console.log(data);
      },
      (error) => console.log(error)
    );
  }
  private generateId() {
    return Math.round(Math.random() * 10000);
  }
}

app.component.css

.container {
  margin-top: 50px;
}

app.module.ts

import { ServersService } from './servers.service';
import { BrowserModule } from '@angular/platform-browser';
import { NgModule } from '@angular/core';
import { FormsModule } from '@angular/forms';
import { HttpModule } from '@angular/http';

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

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

servers.service.ts

import { Injectable } from '@angular/core';
import { Http, Headers } from '@angular/http';
import 'rxjs/Rx';

@Injectable()
export class ServersService{
  constructor(private http: Http){}
  storeServers(servers: any[]) {
    const headers = new Headers({'Content-Type' : 'application/json'})
    return this.http.put('https://angulardemo-64fa2.firebaseio.com/data.json', servers,
    {headers: headers});
  }
  getServers(){
    return this.http.get('https://angulardemo-64fa2.firebaseio.com/data.json');
  }
  storeServersPost(servers: any[]) {
    const headers = new Headers({'Content-Type' : 'application/json'})
    return this.http.post('https://angulardemo-64fa2.firebaseio.com/data.json', servers,
    {headers: headers});
  }
}

Final

app.component.css

.container {
  margin-top: 50px;
}

app.component.html

<div class="container">
  <div class="row">
    <div class="col-xs-12 col-sm-10 col-md-8 col-sm-offset-1 col-md-offset-2">
      <h1>{{ appName | async }}</h1>
      <input type="text" #serverName>
      <button class="btn btn-primary" (click)="onAddServer(serverName.value)">Add Server</button>
      <br><br>
      <button class="btn btn-primary" (click)="onSave()">Save Servers</button>
      <button class="btn btn-primary" (click)="onGet()">Get Servers</button>
      <hr>
      <ul class="list-group" *ngFor="let server of servers">
        <li class="list-group-item">{{ server.name }} (ID: {{ server.id }})</li>
      </ul>
    </div>
  </div>
</div>

app.component.ts

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

import { ServerService } from './server.service';

@Component({
  selector: 'app-root',
  templateUrl: './app.component.html',
  styleUrls: ['./app.component.css']
})
export class AppComponent {
  appName = this.serverService.getAppName();
  servers = [
    {
      name: 'Testserver',
      capacity: 10,
      id: this.generateId()
    },
    {
      name: 'Liveserver',
      capacity: 100,
      id: this.generateId()
    }
  ];
  constructor(private serverService: ServerService) {}
  onAddServer(name: string) {
    this.servers.push({
      name: name,
      capacity: 50,
      id: this.generateId()
    });
  }
  onSave() {
    this.serverService.storeServers(this.servers)
      .subscribe(
        (response) => console.log(response),
        (error) => console.log(error)
      );
  }
  onGet() {
    this.serverService.getServers()
      .subscribe(
        (servers: any[]) => this.servers = servers,
        (error) => console.log(error)
      );
  }
  private generateId() {
    return Math.round(Math.random() * 10000);
  }
}

app.module.ts

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

import { AppComponent } from './app.component';
import { ServerService } from './server.service';

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

server.service.ts

import { Injectable } from '@angular/core';
import { Headers, Http, Response } from '@angular/http';
import 'rxjs/Rx';
import { Observable } from 'rxjs/Observable';

@Injectable()
export class ServerService {
  constructor(private http: Http) {}
  storeServers(servers: any[]) {
    const headers = new Headers({'Content-Type': 'application/json'});
    // return this.http.post('https://udemy-ng-http.firebaseio.com/data.json',
    //   servers,
    //   {headers: headers});
    return this.http.put('https://udemy-ng-http.firebaseio.com/data.json',
      servers,
      {headers: headers});
  }
  getServers() {
    return this.http.get('https://udemy-ng-http.firebaseio.com/data')
      .map(
        (response: Response) => {
          const data = response.json();
          for (const server of data) {
            server.name = 'FETCHED_' + server.name;
          }
          return data;
        }
      )
      .catch(
        (error: Response) => {
          return Observable.throw('Something went wrong');
        }
      );
  }
  getAppName() {
    return this.http.get('https://udemy-ng-http.firebaseio.com/appName.json')
      .map(
        (response: Response) => {
          return response.json();
        }
      );
  }
}

Services and Injectable in Angular

Points to remember while creating a service in Angular

  • First create a service first using the command “ng generate service serviceName”
  • Note the above class should have @Injectable annotation above the class.
  • Now in the class where you need to use this service you need to pass a private reference of that class “constructor(private serviceVar: serviceName){}”
  • Now we need to add the service name in the provider section of the @Component so that the instance of the service is created while the constructor is called.
  • Now you can call the service method inside the class using “this.serviceVar.method()”

Demo Application

User.java

package GradleDemo;

import java.util.Date;

public class User {
	private Integer id;
	private String name;
	private Date bithDate;
	
	public User() {
		
	}
	
	public User(Integer id, String name, Date bithDate) {
		super();
		this.id = id;
		this.name = name;
		this.bithDate = bithDate;
	}
	public Integer getId() {
		return id;
	}
	public void setId(Integer id) {
		this.id = id;
	}
	public String getName() {
		return name;
	}
	public void setName(String name) {
		this.name = name;
	}
	public Date getBithDate() {
		return bithDate;
	}
	public void setBithDate(Date bithDate) {
		this.bithDate = bithDate;
	}
	@Override
	public String toString() {
		return "User [id=" + id + ", name=" + name + ", bithDate=" + bithDate + "]";
	}
}

UserDaoService.java

package GradleDemo;

import java.util.ArrayList;
import java.util.Date;
import java.util.List;

import org.springframework.stereotype.Component;

@Component
public class UserDaoService {
	public static int userCount = 3;
	private static List<User> users = new ArrayList<>();
	static {
		users.add(new User(1,"Tyson",new Date()));
		users.add(new User(2,"Justin",new Date()));
		users.add(new User(3,"Martin",new Date()));
	}
	
	public List<User> findAll(){
		return users;
	}
	
	public User save(User user) {
		if(user.getId()==null) {
			user.setId(++userCount);
		}
		users.add(user);
		return user;
	}
	public User findOne(int id) {
		for(User user : users) {
			if(user.getId()==id) {
				return user;
			}
		}
		return null;
	}
}

UserResource.java

package GradleDemo;

import java.net.URI;
import java.util.List;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.servlet.support.ServletUriComponentsBuilder;

@RestController
public class UserResource {
	
	@Autowired
	private UserDaoService userDaoService; 
	
	@GetMapping("/users")
	public List<User> retrieveAllUsers(){
		return userDaoService.findAll();
	}
	
	@GetMapping("/user/{id}")
	public User getUser(@PathVariable int id) {
		return userDaoService.findOne(id);
	}
	
	@PostMapping("/users")
	public ResponseEntity<Object> createUser(@RequestBody User user) {
		User savedUser = userDaoService.save(user);
		URI location = ServletUriComponentsBuilder
				.fromCurrentContextPath()
				.path("{id}")
				.buildAndExpand(savedUser.getId()).toUri();
		return ResponseEntity.created(location).build(); // 201 response is returned
	}
}

App.java

package GradleDemo;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.ApplicationContext;

@SpringBootApplication
public class App {

    public static void main(String[] args) {
        SpringApplication.run(App.class, args);
    }
}

build.gradle

plugins {
    id 'java'
    id 'org.springframework.boot' version '2.2.1.RELEASE'
    id 'io.spring.dependency-management' version '1.0.8.RELEASE'
}

repositories {
    mavenCentral()
}

dependencies {
    implementation 'com.google.guava:guava:28.0-jre'
    testImplementation 'junit:junit:4.12'
    implementation 'org.springframework.boot:spring-boot-dependencies:2.2.1.RELEASE'
    implementation 'org.springframework.boot:spring-boot-starter-web'
    implementation 'org.springframework.boot:spring-boot-starter-actuator'
    implementation 'org.springframework.data:spring-data-rest-hal-browser'
	implementation 'org.springframework.boot:spring-boot-devtools:1.3.0.RELEASE'
	implementation 'org.springframework.data:spring-data-jpa:2.2.2.RELEASE'
	implementation 'javax.persistence:javax.persistence-api:2.2'
	implementation 'com.h2database:h2:1.4.200'
    testImplementation 'org.springframework.boot:spring-boot-starter-test'

    components {
        withModule('org.springframework:spring-beans') {
            allVariants {
                withDependencyConstraints {
                    it.findAll { it.name == 'snakeyaml' }.each { it.version { strictly '1.19' } }
                }
            }
        }
    }
}

bootJar {
    mainClassName = 'GradleDemo.App'
}

task runJar{
	dependsOn 'assemble'
	dependsOn 'jar'
	doLast{
  		javaexec { 
    		main="-jar";
    		args = [
            	"build/libs/"+rootProject.name+".jar"
           	]
		} 
	}
}

SpringBoot Dev tools

SpringBoot Dev tools helps developer with :

Property Default

Spring-boot does a lot of auto-configurations, including enabling caching by default to improve performance. One such example is caching of templates used by template engines, e.g. thymeleaf. But during development, it’s more important to see the changes as quickly as possible.

The default behavior of caching can be disabled for thymeleaf using the property spring.thymeleaf.cache=false in the application.properties file. We do not need to do this manually, introducing this spring-boot-devtools does this automatically for us.

Automatic Restart

In a typical application development environment, a developer would make some changes, build the project and deploy/start the application for new changes to take effect, or else try to leverage JRebel, etc.

Live Reload

spring-boot-devtools module includes an embedded LiveReload server that is used to trigger a browser refresh when a resource is changed.

For this to happen in the browser we need to install the LiveReload plugin one such implementation is Remote Live Reload for Chrome.

Remote Debugging via HTTP (Remote Debug Tunnel)

spring-boot-devtools provides out of the box remote debugging capabilities via HTTP, to have this feature it is required that spring-boot-devtools are packaged as part of the application. This can be achieved by disabling excludeDevtools configuration in the plugin in maven.

Remote Update

The remote client monitors the application classpath for changes as is done for remote restart feature. Any change in the classpath causes, the updated resource to be pushed to the remote application and a restart is triggered.

Changes are pushed when the remote client is up and running, as monitoring for changed files is only possible then.

SpringBoot Actuator

  • Actuator brings production-ready features to our application
  • Monitoring our app, gathering metrics, understanding traffic or the state of our database becomes trivial with it.
  • Actuator is mainly used to expose operational information about the running application – health, metrics, info, dump, env, etc. It uses HTTP endpoints or JMX beans to enable us to interact with it.
  • Once this dependency is on the classpath several endpoints are available for us out of the box. As with most Spring modules, we can easily configure or extend it in many ways.

To enable SpringBoot actuator we need to add below line in our build.gradle

implementation 'org.springframework.boot:spring-boot-starter-actuator'
implementation 'org.springframework.data:spring-data-rest-hal-browser:2.4.0.RELEASE'

Here are some of the most common endpoints Boot provides out of the box:

  • /health – Shows application health information (a simple ‘status’ when accessed over an unauthenticated connection or full message details when authenticated); it’s not sensitive by default
  • /info – Displays arbitrary application info; not sensitive by default
  • /metrics – Shows ‘metrics’ information for the current application; it’s also sensitive by default
  • /trace – Displays trace information (by default the last few HTTP requests)

Configure existing endpoints :

Three properties are available:

  • id – by which this endpoint will be accessed over HTTP
  • enabled – if true then it can be accessed otherwise not
  • sensitive – if true then need the authorization to show crucial information over HTTP

For example, add the following properties will customize the /bean endpoint:

endpoints.beans.id=springbeans
endpoints.beans.sensitive=false
endpoints.beans.enabled=true

URLs :

http://localhost:9090/actuator
http://localhost:9090/application