How to send events from nodeJS/express to angular - node.js

I have a long running transaction, and I would like to inform the client of the progress. My front end is Angular 4 and backend is nodeJS/Express . The client initiates the transaction via HTTP Post .
Angular does provide a facility to listen to event progress . https://angular.io/guide/http#listening-to-progress-events
My question is, how can I send events from my express App to Angular app?
As of the moment I don't want to use sockets.io .

Listening to upload progress events is actually a client-side feature. What it does behind the scenes is that it tells you the progress based on how much data the client i.e. the browser, has sent to the server. It doesn't actually get a response from the server (as I assume what you are thinking) for how much data the server has received and then displaying the progress to the user. So, if you would think logically and technically, it can not help you in any way. Also, as far as my knowledge goes, sockets are the only way to get a real-time update of the things happening on the server side.

Based on Angular's documentation, progress events can be handled by client, and after doing some searching I cam across server side events - SSE, which is basically sending response headers with connection alive header, and then progress data .
I was able to do it, but I still have issues sending and handling custom user events per angular. Here is what that I have.
App component.ts
import { Component ,OnInit} from '#angular/core';
import { CommonService} from './common.service';
import { Observable,Subscription } from "rxjs/Rx";
import 'rxjs/add/operator/timeout';
#Component({
selector: 'app-root',
templateUrl: './app.component.html',
styleUrls: ['./app.component.css','../../node_modules/bootstrap/dist/css/bootstrap-
theme.min.css']
})
export class AppComponent implements OnInit {
private sseStream: Subscription;
messages:Array<string> = [];
progress:number=0;
totalProgress:number=7;
constructor(private commonService: CommonService ) { }
ngOnInit(){
this.commonService.getHttpObj().subscribe(event=>{
if(event){
if(event['loaded']){
console.log(event['loaded']);
this.progress=(event['loaded'] / this.totalProgress)*100;
}
}
});
}
title = 'Angular4';
}
common.service.ts
import { Injectable } from '#angular/core';
import {HttpRequest} from '#angular/common/http';
import { Observable } from "rxjs/Rx";
import { catchError, map, tap , last} from 'rxjs/operators';
import { HttpClient } from '#angular/common/http';
import { HttpEventType } from '#angular/common/http';
const req = new HttpRequest('GET', 'http://localhost:9080/event', {
reportProgress: true
});
#Injectable()
export class CommonService {
constructor(private http: HttpClient) { }
getHttpObj(){
return this.http.request(req).pipe(
map(event => this.getEventMessage(event)),
tap(message => this.showProgress(message)),
// last(), // return last (completed) message to caller
// catchError(this.handleError())
);
};
private getEventMessage(event: any) {
switch (event.type) {
// case HttpEventType.Sent:
// return `Uploading file `;
case HttpEventType.UploadProgress:
// Compute and show the % done:
const percentDone = Math.round(100 * event.loaded / event.total);
return `File is ${percentDone}% uploaded.`;
case HttpEventType.Response:
return `Complete`;
case HttpEventType.User:
return event;
case HttpEventType.UploadProgress:
return `${JSON.stringify(event)}`;
case HttpEventType.DownloadProgress:
return event;
default:
return event;
}
}
showProgress(a:any){
//console.log(a);
return a;
}
private handleError<T> () {
return (error: any): Observable<T> => {
// TODO: send the error to remote logging infrastructure
// console.error('error'); // log to console instead
// TODO: better job of transforming error for user consumption
// console.log(`${error.message}`);
// Let the app keep running by returning an empty result.
return null;
};
}
}
app.component.html
`<div class="container">
<div style="text-align:center">
<h1>
Welcome to {{title}}!!
</h1>
<input type="text" [(ngModel)]="test">
<p>{{test}}</p>
</div>
<div class="progress">
<div class="progress-bar bg-success" [ngStyle]="{'width':progress + '%'}"></div>
</div>
</div> `
app.module.ts
import { BrowserModule } from '#angular/platform-browser';
import { NgModule } from '#angular/core';
import { FormsModule } from '#angular/forms'
import { AppComponent } from './app.component';
import { ServerComponent } from './server/server.component';
import { ServersComponent } from './servers/servers.component';
import { HttpClientModule } from '#angular/common/http';
import {CommonService } from './common.service';
import { HttpModule } from '#angular/http';
#NgModule({
declarations: [
AppComponent,
ServerComponent,
ServersComponent
],
imports: [
BrowserModule,
FormsModule,
HttpClientModule,
HttpModule
],
providers: [CommonService],
bootstrap: [AppComponent]
})
export class AppModule { }
server.js
var express=require('express');
var app=express();
app.listen(9080);
app.get('/event',(req,res)=>{
res.writeHead(200, {
'Content-Type': 'text/event-stream',
'Cache-Control': 'no-cache',
'Connection': 'keep-alive'
});
setTimeout(() => {
res.write( "\n") ;
setTimeout(() => {
res.write("\n") ;
setTimeout(() => {
res.write( "\n") ;
setTimeout(() => {
res.write( "\n") ;
setTimeout(() => {
res.write( "\n") ;
res.write(JSON.stringify({})) ;
res.end();
},
2000);
},
2000);
},
2000);
},
2000);
},
2000);
[enter image description here][1]});

Related

JWT library error: Generic type 'ModuleWithProviders<T>' requires 1 type argument(s) in Angular 10

For an authentication project I am using:
Angular CLI: 11.0.4 for the frontend
Node: 10.19.0 for the backend
OS: linux x64
I receive the following error after ng serve and I am not sure why that is happening, the error seems to be in the library node_modules/angular2-jwt/angular2-jwt.d.ts and not in the code I wrote:
node_modules/angular2-jwt/angular2-jwt.d.ts:88:41 - error TS2314:
Generic type 'ModuleWithProviders' requires 1 type argument(s).
static forRoot(config: AuthConfig): ModuleWithProviders;
Also connected to that (so I believe the errors are concurrent or even, I am afraid, interchangeable), because it was shown as soon as the 'ModuleWithProviders<T>' error was shown, so I though it would make sense to show them both as they are linked together:
Error: node_modules/angular2-jwt/angular2-jwt.d.ts:1:77 - error
TS2307: Cannot find module '#angular/http' or its corresponding type
declarations.
1 import { Http, Request, RequestOptions, RequestOptionsArgs, Response
} from "#angular/http";
So the difficulty I have is also due to the fact that I am not sure which parts of the code are affected so I will put for the sake of completeness the app.module.ts and the files carrying the jwt include
app.module.ts:
import { ValidateService } from './services/validate.service';
import { FlashMessagesModule } from 'angular2-flash-messages';
import { HttpClientModule } from '#angular/common/http';
import { AuthService } from './services/auth.service';
import { AuthGuard } from './guards/auth.guards';
const appRoutes: Routes = [
{path:'', component: HomeComponent},
{path:'register', component: RegisterComponent},
{path:'login', component: LoginComponent},
{path:'dashboard', component: DashboardComponent, canActivate: [AuthGuard]},
{path:'profile', component: ProfileComponent, canActivate: [AuthGuard]},
]
#NgModule({
declarations: [
AppComponent,
NavbarComponent,
LoginComponent,
RegisterComponent,
HomeComponent,
DashboardComponent,
ProfileComponent
],
imports: [
BrowserModule,
AppRoutingModule,
FormsModule,
RouterModule.forRoot(appRoutes),
FlashMessagesModule.forRoot(),
HttpClientModule,
],
providers: [ValidateService, AuthService, AuthGuard],
bootstrap: [AppComponent]
})
export class AppModule { }
auth.service.ts
import { Injectable } from '#angular/core';
import { HttpClient, HttpHeaders } from '#angular/common/http';
import { map } from 'rxjs/operators';
import { tokenNotExpired } from 'angular2-jwt';
#Injectable({
providedIn: 'root'
})
export class AuthService {
authToken: any;
user: any;
constructor(private httpClient: HttpClient) { }
registerUser(user) {
const httpOptions = {
headers: new HttpHeaders({
'Content-Type': 'application/json',
})
};
return this.httpClient.post('http://localhost:3000/users/register', user, httpOptions);
}
authenticateUser(user) {
const httpOptions = {
headers: new HttpHeaders({
'Content-Type': 'application/json',
})
};
return this.httpClient.post('http://localhost:3000/users/authenticate', user, httpOptions);
}
getProfile() {
const httpOptions = {
headers: new HttpHeaders({
'Content-Type': 'application/json',
Authorization: this.authToken,
})
};
this.loadToken();
return this.httpClient.get('http://localhost:3000/users/profile', httpOptions);
}
storeUserData(token, user) {
localStorage.setItem('id_token', token);
localStorage.setItem('user', JSON.stringify(user));
this.authToken = token;
this.user = user;
}
loadToken() {
const token = localStorage.getItem('id_token');
this.authToken = token;
}
loggedIn() {
return tokenNotExpired();
}
logout() {
this.authToken = null;
this.user = null;
localStorage.clear();
}
}
profile.components.ts
import { Component, OnInit } from '#angular/core';
import { AuthService } from '../../services/auth.service';
import { Router } from '#angular/router';
#Component({
selector: 'app-profile',
templateUrl: './profile.component.html',
styleUrls: ['./profile.component.css']
})
export class ProfileComponent implements OnInit {
user: Object = {};
constructor(private authService: AuthService, private router: Router) { }
ngOnInit(): void {
this.authService.getProfile().subscribe(profile => {
this.user = profile;
},
err => {
console.log(err);
return false;
})
}
}
I did research on how to solve the problem and here is was I was able to find so far:
this post is very useful because it has my same exact problem.
The answer calls for a bug report repo that, however, does not provide any answer to that.
The answer that was provided suggests to insert the following code:
declare module "#angular/core" {
interface ModuleWithProviders<T = any> {
ngModule: Type<T>;
providers?: Provider[];
}
}
Unfortunately this was not an accepted answer and I am not sure where I can put this piece of code in any part of the app.module.ts I provided above.
I also studied this post which was also useful but did not use the suggestion above.
The strange fact I understand from the error is that it seems to come from the library itself and not from the code that I wrote.
Following this I proceeded with:
rm -rf all the node_modules
rm -rf the package jason file
clean the cache
npm install
But outcome is the same, I always receive the same error on the same library.
Please if anyone had the same problem can you share how it was solved and what should I do more to take care of that.
Insert this piece of code into the angular2-jwt.d.ts class and confirm the class change:
declare module "#angular/core" {
interface ModuleWithProviders<T = any> {
ngModule: Type<T>;
providers?: Provider[];
}
}
But you should use a newer library than this, like #auth0/angular-jwt
After installing this library, you must register its module in the class app.module.ts :
import {JwtModule} from '#auth0/angular-jwt'
imports: [
JwtModule.forRoot({
config: {
tokenGetter:() => {
return localStorage.getItem('access_token');
},
},
})
],
And then you can use it in your AuthService class:
import {JwtHelperService} from '#auth0/angular-jwt';
constructor(public jwtHelper: JwtHelperService) {
}
isAuthenticated(): boolean {
return !this.jwtHelper.isTokenExpired(this.token);
}
All this is explained in a short documentation with examples (https://www.npmjs.com/package/#auth0/angular-jwt), so don't be lazy to read it before using any library.

How to pass the data from one component to another component using angular5

I am creating test application using angular so in that i need to display user details while i click on edit button then, that user details will need to display in another component.i have written query to get user details while clicking edit button, but unable to get data so what is the exact procedure for changes need to be done in query.
This is my manageusers.component.html
<tr *ngFor="let detail of userDetails" style="text-align:center">
<td><input type="checkbox"></td>
<td>{{detail.username}}</td>
<td>{{detail.uemail}}</td>
<td>Inactive</td>
<td>{{detail.added_on}}</td>
<td>
<a routerLink="/dashboard-info/basic-settings">
<i class="fas fa-edit" (click)="editIssue(i,detail._id)"></i>
</a>
This is my manageusers.component.ts
import { Component, OnInit } from '#angular/core';
import { FormBuilder, FormGroup , Validators } from '#angular/forms';
import { DataService } from '../../../services/data.service';
import { AccountService } from '../../../services/account.service';
import { Router } from '#angular/router';
#Component({
selector: 'app-manage-users',
templateUrl: './manage-users.component.html',
styleUrls: ['./manage-users.component.css'],
})
export class ManageUsersComponent implements OnInit {
userDetails:any = [];
detail:Detail={
added_on:'',
username:'',
uemail:'',
upassword:'',
};
constructor(private router: Router,private fb:FormBuilder,private dataService: DataService, private accountService: AccountService) {}
editIssue(id,detail){
alert(detail);
let data = {
_id:detail,
};
this.accountService.editDetail(data).subscribe(
response => {
console.log(response);
this.userDetails = JSON.parse(response);
//this.router.navigate(['/dashboard-info/basic-settings', this.userDetails]);
},
err => {
console.error('User Not found');
})
}
ngOnInit() {}
}
interface Detail{
added_on:string
username:string,
upassword:string,
uemail:string,
}
accountService.ts
editDetail(data) {//Getting User with userId
return this.http.post(this.apiPath + 'user/editDetail', data,{responseType: 'text'});
}
usercontroller.js
userRouter.post('/editDetail', function (req, res) {
console.log(req.body._id);
Collections.user.findOne({_id: req.body._id}, function (err, result) {
if (err) return res.status(500).send("There was a problem finding the user");
if (result) {
console.log(result);
res.status(200).send(result);
} else {
return res.status(500).send("User Not Found with Details: " + JSON.stringify(user));
}
});
});
I think it would be better to set the User you want to show in the component as input, get the User you need in an http call and pass it to the component afterwards by it's input. See more for component input here: https://toddmotto.com/passing-data-angular-2-components-input
To retrieve data by http call you should use Angulars Http Client, which is really easy to use and saves you from using plain javascript. See here: https://blog.angular-university.io/angular-http/

How to wait for async HTTP requests to return values on Angular 4?

I have some "cards" that I want to get information for them on an Angular service, the problem is that I get this information with http requests on an API, and I want the return to happen after ALL requests are completed.
cards.component.ts
import {Component} from '#angular/core';
import {CardsService} from './cards.service';
import 'easy-pie-chart/dist/jquery.easypiechart.js';
#Component({
selector: 'cards',
templateUrl: './cards.html',
styleUrls: ['./cards.scss']
})
// TODO: move easypiechart to component
export class Cards {
public charts: any;
constructor(private _cardsService: CardsService) {
this.charts = this._cardsService.getData();
}
}
cards.service.ts
import {Injectable} from '#angular/core';
import {BaThemeConfigProvider, colorHelper} from '../../../theme';
import { Http, Headers, Response } from '#angular/http';
import {Observable} from "rxjs/Observable";
#Injectable()
export class CardsService {
_meterCountURL = 'http://localhost:8080/meter/count';
_cardMeter;
_cardConsumption;
constructor(private _baConfig:BaThemeConfigProvider, private http: Http) {
}
getData() {
let pieColor = this._baConfig.get().colors.custom.dashboardPieChart;
let headers = new Headers({'Content-type': 'application/x-www-form-urlencoded',
'Authorization': 'Bearer ' + localStorage.getItem('id_token')});
Observable.forkJoin(
this.http.get(this._meterCountURL, {headers: headers}).map((response) => {response.json()['data'];}),
this.http.get(this._meterCountURL, {headers: headers}).map((response) => {response.json()['data'];})
).subscribe(
data => {
this._cardMeter = data[0];
this._cardConsumption = data[1];
},
error => console.log(error)
);
return [
color: pieColor,
description: 'Consumo do mês atual',
stats: 0 || this._cardConsumption,
icon: 'ion-flash',
}, {
color: pieColor,
description: 'Número de unidades ativas',
stats: 0 || this._cardMeter,
icon: 'ion-ios-speedometer',
}
];
}
}
When It runs, where it should have an Integer, it appears:
[object Object].
If I try to put the return statement INSIDE the subscribe function, 'cards.component.ts' gives me the following error:
Type 'void' is not assignable to type 'Object[]'.
How can I return the card information after the http requests finishes correctly?
You should be returning the observable in your getData() method, then subscribe in your component. This way the component knows when the observable completes (in the subscribe method).
// card.service.ts
getData() {
return Observable.forkJoin(...);
}
// cards.component.ts
this._cardsService.getData().subscribe(data => {
this.charts = ...;
});

I am not getting response from nodeJS server in angular 2 [duplicate]

This question already has answers here:
How do I return the response from an Observable/http/async call in angular?
(10 answers)
Closed 5 years ago.
I am newbie to MEAN stack development. So, please help me to figure out the problem.
app.js
const express = require('express');
const app = express();
const path = require('path');
app.use(express.static(path.join(__dirname, './darwin-src/public')));
const port = 3000;
app.get('/images', (req, res) => {
console.log('In server');
var data;
var Scraper = require ('images-scraper')
, google = new Scraper.Google();
google.list({
keyword: 'banana',
num: 10,
detail: true,
nightmare: {
show: false
}
})
.then(function (data) {
console.log('first 10 results from google', data);
res.end("" + data);
})
.catch(function(err) {
console.log('err', err);
});
});
app.listen(port, () => {
console.log(`Starting the server at port ${port}`);
});
image-service.service.ts
import { Injectable } from '#angular/core';
import { Http, Headers } from '#angular/http';
import { Observable } from 'rxjs/Observable';
import { Image } from './model/image';
import 'rxjs/add/operator/map';
import 'rxjs/add/operator/catch';
import 'rxjs/add/observable/throw';
import 'rxjs/add/observable/of';
#Injectable()
export class ImageServiceService {
constructor(private http: Http) { }
private serverApi = 'http://localhost:3000';
public getImages(image: string): Observable<Image[]> {
console.log('Inside Service');
let URI = `${this.serverApi}/images`;
return this.http.get(URI)
.map(function(res) {
return res.json();
});
}
}
image-view.component.ts
import { Component, OnInit } from '#angular/core';
import { ImageServiceService } from '../image-service.service';
import { Image } from '../model/image';
#Component({
selector: 'app-image-view',
templateUrl: './image-view.component.html',
styleUrls: ['./image-view.component.css']
})
export class ImageViewComponent implements OnInit {
private data: Image[] = [];
constructor(private imageService: ImageServiceService) { }
ngOnInit() {
}
onSubmit(image: string) {
console.log(image);
this.imageService.getImages(image).subscribe(response => this.data = response);
console.log(this.data.length);
}
}
The length of array is zero and I can't figure out why. The response comes on nodejs console after a while but the frontend displays the result before the response comes. Please help!
Hit the server url separately in browser and see if you get the expected response. If this is okay, then the problem is with the client.
On seeing your client code, one issue seems obvious. You are not using the observable from ImageServiceService properly. All your manipulations should be within the subscribe method.
onSubmit(image: string) {
this.imageService.getImages(image).subscribe(response => {
this.data = response;
console.log(this.data.length);
// Do other manipulations that you wish to do
});
}
If you using the observable to display something in the view, then
consider . using async pipe
The code in the subscribe handler is not executed synchronously. So, your console.log statement is executed before you get a response from your server. I don't see your image-view.component.html markup. But, I believe you need to use the async pipe in your bound option.
private data$: Observable<Image[]>;
onSubmit(image: string) {
console.log(image);
this.data$ = this.imageService.getImages(image);
}
And you HTML:
<div *ngFor="let image of data$ | async">
{{image.value}}
</div>

this.zone.run() - Ngzone error

I have a problem with sails + sails socket + angular4
import { Component, OnInit, NgZone } from '#angular/core';
import { routerTransition } from '../../router.animations';
import { Http } from '#angular/http';
import * as socketIOClient from 'socket.io-client';
import * as sailsIOClient from 'sails.io.js';
#Component({
selector: 'app-tables',
templateUrl: './tables.component.html',
styleUrls: ['./tables.component.scss'],
animations: [routerTransition()]
})
export class TablesComponent implements OnInit {
io:any;
smartTableData:any;
constructor(public http: Http, private zone: NgZone) {
this.io = sailsIOClient(socketIOClient);
this.io.sails.url = "http://localhost:1337";
this.io.socket.get('/posts', function(resData, jwr) {
console.log (resData);
return resData;
});
this.io.socket.on('updateposts', function(data) {
this.zone.run(() => {
this.smartTableData = data;
});
});
}
ngOnInit() {
}
}
When the socket broadcasts data, the line this.socket.on("updataposts") is run, but ngZone does not work.
zone.js:196 Uncaught TypeError: Cannot read property 'run' of undefined
I want to update data to view when receiving socket data. Please help me!
Thank you!

Resources