How can i asynchronouslycall this service function in another component? Angular 11 - node.js

I have an async function getIdentByInfo and in the console i get the right output if i log it in this function. As soon as i call it in another component it doesnt work and i only get 'undefined'. I know it has something to do with beeing ssynchrone and Promises but i cant figure out how to solve my issue. I need the Model class filled with attributes coming from the http request in another component to send them to another service
import { EventEmitter, Injectable } from '#angular/core';
import { HttpClient } from '#angular/common/http';
import { IdentModel } from "../models/identmodel.model";
import { IdentteilComponent } from "../pages/identteil/identteil.component";
#Injectable({
providedIn: 'root',
})
export class InfoWebservice {
url = 'http://localhost:4201';
ident: IdentModel[];
constructor(private http: HttpClient) { }
// promise vom typ IdentModel zurückgeben
getIdentByInfo(id: string, vwk: string) {
this.http.get(this.url).toPromise().then(data => {
for (let i in data){
this.ident.push(data[i])
if ( this.ident[i].identNr == id && this.ident[i].vwk == vwk){
return this.ident[i];
}
}
});
}
}
import { Component, OnInit } from '#angular/core';
import { Router } from '#angular/router';
import { InfoWebservice } from '../../webservices/info.webservice'
import { ImageWebservice } from '../../webservices/image.webservice'
import { IdentModel } from "../../models/identmodel.model";
#Component({
selector: 'app-identteil',
templateUrl: './identteil.component.html',
styleUrls: ['./identteil.component.scss']
})
export class IdentteilComponent implements OnInit {
ident = [];
identNr:string;
vwk:string;
imgFrontLink:string;
imgBackLink:string;
constructor(private router: Router, private service: InfoWebservice, private image: ImageWebservice) { }
getIdentNr() : string {
var split = this.router.url.split("/");
this.identNr = split[2];
return this.identNr;
}
//return type is STRING
getVwk() {
// output von window.location.host = repapp-maw.dbl.de
// var splitHost = window.location.host.split(".");
var splitHost = 'repapp-maw';
var splitV = splitHost.split("-");
this.vwk = splitV[1];
return this.vwk;
}
callInfoService = async () => {
return await this.service.getIdentByInfo(this.getIdentNr(), this.getVwk());
}
ngOnInit() {
console.log(this.callInfoService());
}
}

When you use angular, its always preferred not to use await/Promise. Angular has an in-built RX-JS library which has tonnes of super-awesome functionalities that you can use.
For Example, in your case, you can do something like this:
// Your Service File can make use of 'Behavior Subject'
// Please read more about it here: https://www.learnrxjs.io/learn-rxjs/subjects/behaviorsubject
import { EventEmitter, Injectable } from '#angular/core';
import { HttpClient } from '#angular/common/http';
import { IdentModel } from "../models/identmodel.model";
import { IdentteilComponent } from "../pages/identteil/identteil.component";
#Injectable({
providedIn: 'root',
})
export class InfoWebservice {
url = 'http://localhost:4201';
ident: IdentModel[];
initialIdentValues: IdentModel = [];
private identSource: BehaviorSubject<IdentModel[]> = new BehaviorSubject<IdentModel[]>(this.initialIdentValues);
public identValuesObs$: Observable<IdentModel[]> = this.identSource.asObservable();
// Create a method to set the values in component-1
setIdentValues(identValues: IdentModel[]) {
this.identSource.next(identValues);
}
// Create a method to return values in component-2 or any component
returnIdentValues() {
return this.identValuesObs$;
}
constructor(private http: HttpClient) { }
// Change your service call to this:
getIdentByInfo(id: string, vwk: string): Observable<any> {
return this.http.get(this.url);
}
}
Now in your component-1 where you want to set the values of this identvalues:
// Component-1
constructor(private infoWebService: InfoWebService){}
// Create a method where you get the values
someMethod() {
// Call the API method here and subscribe and then set the values
this.infoWebService.getIdentInfoById(id, vwk).subscribe((data: any) => {
// Your logic goes here ANDD
if (data) {
for (let i in data){
this.ident.push(data[i])
let localIdentsWithRequiredLogic = [];
if ( this.ident[i].identNr == id && this.ident[i].vwk == vwk){
localIdentsWithRequiredLogic.push(this.ident[i]);
}
// THIS IS IMPORTANT
this.infoWebService.setIdentValues(localIdentsWithRequiredLogic);
}
}
})
}
Then in component-2 or whatever component you want, you can retrieve it using the returnIdentValues method like this:
// In component-2
inSomeMethodWhereYouRequireIdentValues() {
this.infoWebService.returnIdentValues().subscribe(data => {
console.log(data) // this is data that you set in component one
})
}

Related

Emit events between different tabs

I'm writing a web app where tab1 opens tab2 and needs to reload when tab2 is closed but nothing happens when I send the next() value (in debug I saw it get executed only once when the component is initialized). I assume it has something to do with the different browser tabs
the shared service that should allow communication between the two:
private tabClosedSource = new ReplaySubject<boolean>();
tabClosedEvent = this.tabClosedSource.asObservable();
toggleTabClosed() {
this.tabClosedSource.next(true);
}
on tab1 :
constructor(private service: ExampleService) {}
ngOnInit() {
this.service.tabClosedEvent.subscribe(
event => {
if (event) {
location.reload();
}
});
// had some issues with the path starting with '#' so I ended up replacing values
openTab2() {
window.open(window.location.href.replace('tab1', 'tab2'));
}
on tab2:
constructor(private service: ExampleService) {}
async onContinueClicked() {
api requests...
procces data...
this.service.toggleTabClosed();
window.close();
}
Your requirement is achievable with the help of Broadcast Channel API. Full credit goes to another blog, I've just updated it to suit your needs and by the way I learned some new things too.
First you need to create a service to communicate.
import { Injectable, NgZone } from '#angular/core';
import { MonoTypeOperatorFunction, Observable, Subject } from 'rxjs';
import { map } from 'rxjs/operators';
// helper method to notify zone
function runInZone<T>(zone: NgZone): MonoTypeOperatorFunction<T> {
return (source) => {
return new Observable(observer => {
const onNext = (value: T) => zone.run(() => observer.next(value));
const onError = (e:any) => zone.run(()=> observer.error(e));
const onComplete = () => zone.run(()=> observer.complete());
return source.subscribe(onNext, onError, onComplete);
});
};
}
#Injectable({
providedIn: 'root'
})
export class BroadcastHelperService {
private broadcastChannel: BroadcastChannel;
private onMessage = new Subject<any>();
constructor(
private ngZone: NgZone) {
this.broadcastChannel = new BroadcastChannel('testChannel');
this.broadcastChannel.onmessage = (message: any) => { this.onMessage.next(message) }
}
publish(message: string): void {
this.broadcastChannel.postMessage(message);
}
getMessage(): Observable<any> {
return this.onMessage.pipe(
runInZone(this.ngZone),
map((message: any) => message.data));
}
}
In tab1 component write code to reload page.
export class Tab1Component implements OnInit {
worker: any;
constructor(
public broadcastHelper: BroadcastHelperService
) { }
ngOnInit(): void {
this.broadcastHelper.getMessage().subscribe((message: any) => {
console.log(message);
if(message === 'close') {
window.location.reload();
}
})
}
}
In tab2 component, write code to broadcast message when the tab is closed.
export class Tab2Component {
constructor(
public broadcastHelper: BroadcastHelperService
) { }
#HostListener('window:beforeunload', ['$event'])
beforeUnloadHander(event: any) {
this.broadcastHelper.publish('close');
}
}

How can I get the data type from an HTTP response from a Nodejs server in Angular

I'm creating an Angular application connected to a Nodejs backend server. The Nodejs server response can be an array or Json object. I have to catch the correct data type according to the server response.
This is my Angular service code.
Note that my HttpClient functions return Json objects. Is there any function that returns any type of data?
import { Injectable } from '#angular/core';
import { HttpClient } from '#angular/common/http';
import { map } from 'rxjs/operators'
import { City } from '../models/City';
import { Observable } from 'rxjs';
#Injectable({
providedIn: 'root'
})
export class CityService {
constructor(private http: HttpClient) { }
API_URI = 'http://localhost:5000'
getCities() {
return this.http.get(`${this.API_URI}/City`);
}
getCity(id: string) {
return this.http.get(`${this.API_URI}/City/${id}`);
}
deleteCity(id: string) {
return this.http.delete(`${this.API_URI}/City/${id}`);
}
saveCity(city: City) {
return this.http.post(`${this.API_URI}/City`, city);
}
updateCity(id: string|number|undefined, updatedCity: City): Observable<City> {
return this.http.put(`${this.API_URI}/City/${id}`, updatedCity);
}
}
Thanks a lot !
Check the returned type and transform the response in a map operator, you can then handle it accordingly in your subscribe
getCities() {
return this.http.get(`${this.API_URI}/City`).pipe(
map( body => {
return {
isArray: Array.isArray(body),
data: body
}
})
)
}
It's strange behavior for an API to return either an array or object (usually it's always one or the other). I suspect there may be something missing in your understanding/processing of the response, but I could be wrong😊

compoent interaction with rxjs in angular

i make news application with news api and i fetch data properly and but not sending data from service to component so how can send data from service to component
and component to component through rxjs
import { Injectable } from '#angular/core';
import { Subject } from 'rxjs';
#Injectable()
export class NewService {
private pushSource = new Subject<object>();
Country_Name;
constructor(private http:HttpClient,) {
this.messages$ = this.pushSource.asObservable()
} // define function for call the category
categoryNews(category)
{
var categoryData = this.http.get(`https://newsapi.org/v2/top-headlines?country=in&category=${category}&apiKey=********`)
.subscribe(Data=> this.pushSource.next(Data));
}
news(t_h)
{
console.log(t_h);
this.pushSource.next(t_h);
} }
here is you not import Observable, observable that's why the problem occurred
import { Injectable } from '#angular/core';
import { Subject, Observable, observable } from 'rxjs';
#Injectable()
export class NewService {
messages$: Observable;
private pushSource = new Subject();
Country_Name;
constructor(private http:HttpClient,) {
this.messages$ = this.pushSource.asObservable()
}
categoryNews(category)
{
var categoryData = this.http.get(https://newsapi.org/v2/top-headlines?country=in&category=${category}&apiKey=*****)
categoryData.subscribe(Data=> this.pushSource.next(Data));
}
news(t_h)
{
console.log(t_h);
this.pushSource.next(t_h);
}

Angular 7 / Material DataTable not updating after any operation

I'm building an Angular 7 application using #angular/material. When I load the application for the first time, the Datatable renders correctly, but when I call any function, example - delUser, after deleting a user from the database, it's meant to render the table immediately, but it doesn't until I refresh the whole page. I've tried everything, but to no avail.
Here's my code:
import { Component, OnInit, ViewChild, TemplateRef } from '#angular/core';
import { UserService } from 'src/app/services/user.service';
import { MatTableDataSource } from '#angular/material/table';
import { MatSort } from '#angular/material/sort';
import { MatPaginator } from '#angular/material/paginator';
#Component({
selector: 'app-users',
templateUrl: './users.component.html',
styleUrls: ['./users.component.css']
})
export class UsersComponent implements OnInit {
pgtitle:string = "Manage Users";
dataSource:any;
displayedColumns:string[] = ['userName','email','roleId','userType','Actions'];
#ViewChild(MatSort, {static: true}) sort: MatSort;
#ViewChild(MatPaginator) paginator: MatPaginator;
constructor(
private service:UserService
){}
ngOnInit(): void {
this.getAllUsers();
}
applyFilter(filterValue:String){
this.dataSource.filter = filterValue.trim().toLowerCase();
}
getAllUsers(){
this.service.getAllUsers().subscribe( result => {
this.dataSource = new MatTableDataSource(result);
this.dataSource.paginator = this.paginator;
this.dataSource.sort = this.sort;
});
}
delUser(id){
this.service.deleteUser(id).subscribe(result => {
this.getAllUsers();
});
}
Maybe you can try this:
this.service.getAllUsers().subscribe( result => {
this.dataSource = null; //add this
this.dataSource = new MatTableDataSource(result);
this.dataSource.paginator = this.paginator;
this.dataSource.sort = this.sort;
});

Inversify - Nodejs- Container binding results in max call size exceeded

I am trying to use inversify for DI injection in a small project in typescript. I wanted to create a logging middleware that logs every request with a pino logger customized. I did establish the dependencies manually when the app is created but was wooed by inversify and wanted to try it out.
But I am stuck and unable to proceed. THis is the error that I am facing. Not sure what is making the call to exceed the stack size. Please help me out
public set app_port(app_port: number) {
^
RangeError: Maximum call stack size exceeded
PinoLoggingService.ts
import * as pino from 'pino';
import * as os from 'os';
import * as momentTimeZone from 'moment-timezone';
import { DEFAULT_TIMEZONE, DEFAULT_TIME_FORMAT } from '../../shared/constants/app.constants';
import { AppConfigModel } from './../../shared/model/app.config.model';
import { isNullOrUndefined } from 'util';
import httpContext = require('express-http-context');
import { injectable, inject } from 'inversify';
import { TYPES } from '../../shared/constants/app.types';
import { AppConfigService } from '../../shared/service/app.config.service';
#injectable()
export class PinoLoggingService {
private appConfig: AppConfigModel;
constructor(#inject(TYPES.AppConfigService) private appConfigService: AppConfigService){
this.appConfig = appConfigService.appConfigModel;
}
private getTimeStamp(){
return momentTimeZone().tz(DEFAULT_TIMEZONE).format(DEFAULT_TIME_FORMAT);
}
public infoLogService (fileName): pino.Logger {
return pino({
level: 'info',
name: this.appConfig.app_name,
messageKey: 'feedback-Logs',
base: {pid: process.pid, hostname: os.hostname,
timestamp: this.getTimeStamp(),
appName: this.appConfig.app_name,
fileName: fileName,
request_id: isNullOrUndefined(httpContext.get('reqId')) ? 'Not an actual request ' : httpContext.get('reqId')
},
enabled: true,
useLevelLabels: true,
});
}
}
my AppConfigService.ts - AppConfigModel is just a plain ts with getters and setters.
#injectable()
export class AppConfigService implements IAppConfigService{
private appModel: AppConfigModel;
constructor() {
console.log('parg is:'+path.resolve(__dirname));
}
public get appConfigModel(): AppConfigModel {
if(isNullOrUndefined(this.appModel)) {
this.appModel = new AppConfigModel();
this.appModel.app_port = isNullOrUndefined(process.env.PORT) ? DEFAULT_PORT : parseInt(process.env.PORT);
this.appModel.app_lcp = process.env.LCP;
this.appModel.app_name = process.env.APP_NAME;
this.appModel.app_host = process.env.HOST;
this.appModel.app_node_env = process.env.NODE_ENV;
this.appModel.app_logging = JSON.parse (process.env.PINO_ENABLE_LOGGING);
this.appModel.app_version = process.env.VERSION;
this.appModel.app_context_path = process.env.CONTEXT_PATH;
}
return this.appModel;;
}
}
This is my loggingMiddleware.ts
import * as express from 'express';
import { AppUtilService } from './../util/app.util.service';
import { file } from '#babel/types';
import { inject, injectable } from 'inversify';
import { TYPES } from '../constants/app.types';
import { PinoLoggingService } from '../../logging/service/logging.service';
interface ILoggingMiddleware {
loggingMiddlewareMethod () : express.Router;
}
#injectable()
export class LoggingMiddleware implements ILoggingMiddleware {
constructor(#inject(TYPES.AppUtilService) private utilService: AppUtilService,
#inject(TYPES.PinoLoggingService) private pinoLoggingService: PinoLoggingService){
}
public loggingMiddlewareMethod() {
const appRouter = express.Router();
return appRouter.use((req:express.Request, res:express.Response, next) => {
const fileName = this.utilService.getFileName(__filename);
this.pinoLoggingService.infoLogService(fileName).info('req.url:', req.url);
this.pinoLoggingService.infoLogService(fileName).info('req.headers:', req.headers);
this.pinoLoggingService.infoLogService(fileName).info('req.body:', req.body);
next();
});
}
}
THis is my request tracing middleware
const httpContext = require('express-http-context');
import * as express from 'express';
import { injectable } from 'inversify';
#injectable()
export class RequestTracerMiddleware {
public requestTracer() {
const app = express();
app.use(httpContext.middleware);
}
}
THis is my inversify config
import { Container } from "inversify";
import { TYPES } from "./module/shared/constants/app.types";
import { AppConfigService } from "./module/shared/service/app.config.service";
import { AppUtilService } from "./module/shared/util/app.util.service";
import { LoggingMiddleware } from "./module/shared/middlewares/logging.middleware";
import { CommonMiddleware } from "./module/shared/middlewares/common.middlewares";
import { RequestTracerMiddleware } from "./module/shared/middlewares/request.tracer.middleware";
import { PinoLoggingService } from "./module/logging/service/logging.service";
import { StudentController } from "./module/shared/test.controller";
const DIContainer = new Container({defaultScope: "Singleton"});
DIContainer.bind<AppConfigService>(TYPES.AppConfigService).to(AppConfigService);
DIContainer.bind<AppUtilService>(TYPES.AppUtilService).to(AppUtilService);
DIContainer.bind<LoggingMiddleware>(TYPES.LoggingMiddleware).to(LoggingMiddleware);
DIContainer.bind<CommonMiddleware>(TYPES.CommonMiddleware).to(CommonMiddleware);
DIContainer.bind<RequestTracerMiddleware>(TYPES.RequestTracerMiddleware).to(RequestTracerMiddleware);
DIContainer.bind<PinoLoggingService>(TYPES.PinoLoggingService).to(PinoLoggingService);
DIContainer.bind<StudentController>(TYPES.StudentController).to(StudentController);
export default DIContainer;
This is my server.ts
import 'reflect-metadata';
import * as DIContainer from './inversify.config';
import {InversifyExpressServer } from 'inversify-express-utils'
import { inject } from 'inversify';
import { TYPES } from './module/shared/constants/app.types';
import { CommonMiddleware } from './module/shared/middlewares/common.middlewares';
import { LoggingMiddleware } from './module/shared/middlewares/logging.middleware';
import { RequestTracerMiddleware } from './module/shared/middlewares/request.tracer.middleware';
const container = DIContainer.default;
let server = new InversifyExpressServer(container);
const commMiddleware = container.resolve(CommonMiddleware);
// const requestTracingMiddleware = container.resolve(RequestTracerMiddleware);
const loggingMiddleware = container.resolve(LoggingMiddleware);
server.setConfig(appRouter => {
appRouter.use(commMiddleware.enableBodyParser());
appRouter.use(loggingMiddleware.loggingMiddlewareMethod());
});
// set errorConfig if needed
const app = server.build();
app.listen(2000, () => {
console.log('listening to port');
});
This is a sample controller that I setup.
import {controller, httpGet, httpPost, requestHeaders, requestParam, BaseHttpController, HttpResponseMessage, StringContent} from 'inversify-express-utils';
#controller("/IStudentController")
export class StudentController extends BaseHttpController {
constructor( ) {
super();
console.log('value of stdservuce:');
}
#httpGet("/Iget")
public getStudentDetails(#requestHeaders() reqHeaders: string[] ) {
// sample returning json resuilt - IHttpAcctionResults
const val = 43;
console.log('val from the servuc:', val);
return this.json(val, 200);
}
}

Resources