Subscribe is not a Function Angular / Socket.IO - node.js

I'm writing a chat app and I'm using a Node server and socket.io server side and Angular and socket.io-client client side. Unfortunately I get a "this.socketService.getMessage(...).subscribe is not a function" error when I run it. Everything else seems to work.
app.component.ts:
import { Component, OnInit, OnDestroy } from '#angular/core';
import { SocketService } from './socket.service';
import { Observable } from 'rxjs';
#Component({
selector: 'app-root',
templateUrl: './app.component.html',
styleUrls: ['./app.component.css']
})
export class AppComponent implements OnInit {
title = 'chat';
registered = false;
username: string;
constructor(public socketService: SocketService) {
}
ngOnInit(): void {
this.socketService
.getMessage()
.subscribe(msg => {
if (msg === 'Registered') {
this.registered = true;
console.log(this.registered)
}
});
}
register(username: string): void {
console.log(username)
this.username = username;
this.socketService.sendMessage('register', username);
}
send(message: string): void {
this.socketService.sendMessage('message', message);
}
}
socket.service.ts:
import { Injectable } from '#angular/core';
import { io } from 'socket.io-client';
import { Observable } from 'rxjs';
#Injectable({
providedIn: 'root'
})
export class SocketService {
socket;
constructor() {
this.socket = io('http://localhost:3000/');
}
getMessage(): Observable<any> {
return this.socket.on('message', msg => {
console.log(msg);
});
}
sendMessage(type: string, words: any): void {
console.log(type, words);
this.socket.emit(type, words);
}
}

this.socket.on doesn't returns an Observable so you can't .subscribe() as you would do with an observable.
It returns a Socket as described into the docs : https://socket.io/docs/v3/client-api/#socket-on-eventName-callback

Related

New Entry Not reflacting in table after inserting New record in CRUD angular app

In Angular 13, I created basic CRUD app of employee. When i insert new data and hit submit it run the createEmployee() service and print data on console. Then it redirect to home page that shows all entry in table which call getEmployeeList(). But sometimes it shows newly created entry and sometimes it not shows. In database it perfectly entered. When i goto some other page and come back to home page it shows.
create-employee.components.ts
import { Component, OnInit } from '#angular/core';
import { Router } from '#angular/router';
import { Employee } from 'src/app/employee';
import { EmployeeService } from 'src/app/Services/employee.service';
#Component({
selector: 'app-create-employee',
templateUrl: './create-employee.component.html',
styleUrls: ['./create-employee.component.css']
})
export class CreateEmployeeComponent implements OnInit {
employee: Employee = new Employee();
constructor(private employservice: EmployeeService,private router: Router) { }
ngOnInit(): void {
}
saveEmployee() {
this.employservice.createEmployee(this.employee).subscribe(data => {
console.log(data);
})
console.error();
}
gotoemployeelist() {
this.router.navigate(['employees']);
}
onSubmit() {
this.saveEmployee();
this.gotoemployeelist();
}
}
employee-service.ts
import { HttpClient } from '#angular/common/http';
import { Injectable } from '#angular/core';
import { map, Observable } from 'rxjs';
import { Employee } from '../employee';
#Injectable({
providedIn: 'root'
})
export class EmployeeService {
public baseURL="http://localhost:8080/api/v1/employees";
constructor(private httpClient: HttpClient) {}
getEmployeeList(): Observable<Employee[]> {
return this.httpClient.get<Employee[]>(this.baseURL).pipe(
map(employees => {
for(let employee of employees) {
employee.bdate = new Date(employee.bdate);
employee.jdate = new Date(employee.jdate);
}
console.log("Employees Service: GetEmployee() called");
return employees;
}));
}
createEmployee(employee: Employee): Observable<Object> {
console.log("Employees Service: CreateEmployee() called");
return this.httpClient.post(this.baseURL,employee);
}
getEmployeebyId(id: Number): Observable<Employee> {
console.log("Employees Service: GetEmployeeById() called");
return this.httpClient.get<Employee>(this.baseURL+"/"+id);
}
updateEmployee(employee: Employee,id: number): Observable<Object> {
console.log("Employees Service: UpdateEmployee() called");
console.log(employee);
return this.httpClient.put(this.baseURL+"/"+id,employee);
}
deleteEmployee(id: number): Observable<Object> {
console.log("Employees Service: DeleteEmployee() called");
return this.httpClient.delete(this.baseURL+"/"+id);
}
}
Home-component.ts
import { Component, OnInit } from '#angular/core';
import { Router } from '#angular/router';
import { Employee } from 'src/app/employee';
import { EmployeeService } from 'src/app/Services/employee.service';
#Component({
selector: 'app-employee-list',
templateUrl: './employee-list.component.html',
styleUrls: ['./employee-list.component.css',]
})
export class EmployeeListComponent implements OnInit {
employees: Employee[];
constructor(private employeeservice: EmployeeService,private router: Router) {
}
ngOnInit(): void {
this.getEmployees();
}
private getEmployees() {
this.employeeservice.getEmployeeList().subscribe(data => {
console.log(data);
this.employees=data;
});
}
updateEmployee(id: Number) {
this.router.navigate(['update-employee',id]);
}
deleteEmployee(id: number) {
this.employeeservice.deleteEmployee(id).subscribe(data => {
console.log(id+"employeed Deleted.");
this.getEmployees();
});
}
employeeDetails(id: number) {
this.router.navigate(['emloyee-details',id]);
}
}
In console you can see that createEmployee() called then GetEmployee() called and then it print console.log(newEntry) after navigation.
What is happening program is not waiting to complete createEmployee API before navigating to the home page. You need to call gotoemployeelist() only after the you get response from createEmployee API. You can do this by calling the method inside the subscribe
saveEmployee() {
this.employservice.createEmployee(this.employee).subscribe(data => {
console.log(data);
this.gotoemployeelist();
})
console.error();
}
gotoemployeelist() {
this.router.navigate(['employees']);
}
onSubmit() {
this.saveEmployee();
}

print user name after successful login in angular8 application

i want to display my user name after a successful login. As i am working with the login token and my login logic is separately written and not in my auth file , so i will be including all my required files, for a better understanding
the following is the login component file
export class MyErrorStateMatcher implements ErrorStateMatcher {
isErrorState(control: FormControl | null, form: FormGroupDirective | NgForm | null): boolean {
const isSubmitted = form && form.submitted;
return !!(control && control.invalid && (control.dirty || control.touched || isSubmitted));
}
}
#Component({
selector: 'app-login',
templateUrl: './login.component.html',
styleUrls: ['./login.component.css']
})
export class LoginComponent implements OnInit {
mode: ProgressBarMode = 'indeterminate';
value = 50;
bufferValue = 75;
loading:boolean=false;
matcher = new MyErrorStateMatcher();
loginForm = new FormGroup({
email: new FormControl('', [Validators.email, Validators.required ]),
password: new FormControl('', [Validators.required, Validators.min(3) ])
})
get email() { return this.loginForm.get('email'); }
get password() { return this.loginForm.get('password'); }
errorMail = '';
getErrorMessage(emailInput:HTMLInputElement) {
const mail=/^\w+([\.-]?\w+)*#meltwater\.com/;
if(!emailInput.value.match(mail)){
this.errorMail='Email or password are not valid'
}
else{
this.loading=true;
}
}
constructor(
private loginService: LoginService,
private saveUserData:AuthService,
private router: Router,
private cookie:CookieService) { }
userDisplayName = ''
ngOnInit(): void {
}
rememberMe(e){
if(e.target.checked){
this.cookie.set('value',this.loginForm.value);
}
}
onSubmit(event) {
event.preventDefault()
console.log('value',this.loginForm.value)
if(this.errorMail.length === 0) {
this.loginService.login(this.loginForm.value).subscribe((res:any) => {
console.log('login response', res)
if(res.auth.success === true) {
localStorage.setItem('auth', JSON.stringify(res.auth))
this.loginService.loggedIn$.next(res.auth)
this.saveUserData.saveAuthData(res)
sessionStorage.setItem('loggedUser', res.Username);
this.router.navigateByUrl('/search/list')
} else {
this.errorMail='Email or password is not valid'
}
})
}
}
// returnUserName(){
// return this.userDisplayName = sessionStorage.getItem('loggedUser');
// }
}
the following is the login service file
export class LoginService {
constructor(private http: HttpClient) { }
loggedIn$ = new BehaviorSubject(null)
login(creds) {
console.log('creds',creds)
return this.http.post<LoginResponse>('https://backend.url/login', {creds})
}
}
this is my auth service file , probably its not needed i suppose
import { Injectable } from "#angular/core";
import { HttpClient } from "#angular/common/http";
import { Router } from "#angular/router";
import { Subject } from "rxjs";
import { BehaviorSubject } from 'rxjs';
#Injectable({ providedIn: "root" })
export class AuthService {
private isAuthenticated = false;
private token: string;
private email: string;
private authStatusListener = new Subject<boolean>();
constructor(private http: HttpClient, private router: Router) {}
getToken() {
return this.token;
}
getIsAuth() {
return this.isAuthenticated;
}
getAuthStatusListener() {
return this.authStatusListener.asObservable();
}
public saveAuthData(token: string) {
localStorage.setItem("token", token);
}
private clearAuthData() {
localStorage.removeItem("token");
}
logout() {
// this.token = null;
this.clearAuthData();
this.router.navigate(["/"]);
}
autoAuthUser() {
const authInformation = this.getAuthData();
this.token = authInformation.token;
this.isAuthenticated = true;
this.authStatusListener.next(true);
}
private getAuthData() {
const token = localStorage.getItem("token");
if (!token ) {
return;
}
return {
token: token
}
}
userDetails(){
sessionStorage.setItem('loggedUser', .email);
}
}
this is where i want to display my user name : the header
following is the header component
import { AuthService } from './../../auth/auth.service';
import { LoginComponent } from './../../login/login.component';
import { Router } from '#angular/router';
import { Component, OnInit } from '#angular/core';
import { Subscription } from 'rxjs';
#Component({
selector: 'app-header',
templateUrl: './header.component.html',
styleUrls: ['./header.component.css']
})
export class HeaderComponent implements OnInit {
public userIsAuthenticated;
public userName = '';
constructor(public authService: AuthService,public router:Router, public user:LoginComponent) { }
onLogout(){
this.authService.logout();
this.router.navigateByUrl('/');
}
ngOnInit(): void {
this.userName = this.user.returnUserName() ;
}
}
this is throwing me error , probably bcz login component is been imported , i am not so sure about why the error is occuring
You can not inject Component as service, use #ViewChild(LoginComponent) instead, but this will not solve the problem.
To get username try moving that method in AuthService.
Hope it helps.

TypeError: token.split is not a function

I Have get some error while login which is not expected but i try to solve it from 2 days but i cannot. I am using Nodejs for the api and angular 7 for the frontend
i have posted some of the code with the error
please help me to solve this
THANK YOU SO MUCH IN ADVANCE
auth.service.ts
import { Injectable } from "#angular/core";
import { Observable } from "rxjs";
import "rxjs/Rx";
import { JwtHelperService } from "#auth0/angular-jwt";
import "core-js/es7/reflect";
import { HttpClient } from "#angular/common/http";
const jwt = new JwtHelperService();
#Injectable()
export class AuthService {
private decodedToken;
constructor(private http: HttpClient) {}
public register(userData: any): Observable<any> {
return this.http.post("/api/v1/users/register", userData);
}
public login(userData: any): Observable<any> {
return this.http.post("/api/v1/users/auth", userData).map(token => {
//debugger;
return this.saveToken(token);
});
}
private saveToken(token): string {
//debugger;
this.decodedToken = jwt.decodeToken(token);
localStorage.setItem("bwm_auth", token.token);
localStorage.setItem("bwm_meta", JSON.stringify(this.decodedToken));
return token;
}
}
login.component.ts
import { Component, OnInit } from "#angular/core";
import { Router, ActivatedRoute } from "#angular/router";
import { AuthService } from "../shared/auth.service";
import { FormBuilder, FormGroup, Validators } from "#angular/forms";
#Component({
selector: "bwm-login",
templateUrl: "./login.component.html",
styleUrls: ["./login.component.scss"]
})
export class LoginComponent implements OnInit {
loginForm: FormGroup;
mistake: any[] = [];
notifyMessage: string = "";
constructor(
private fb: FormBuilder,
private auth: AuthService,
private router: Router,
private route: ActivatedRoute
) {}
ngOnInit() {
this.initForm();
this.route.params.subscribe(params => {
if (params["registered"] == "success") {
//debugger;
this.notifyMessage =
"You have been successfully registered you can login now";
}
});
}
initForm() {
this.loginForm = this.fb.group({
email: [
"",
[
Validators.required,
Validators.pattern(
"^[a-zA-Z0-9.!#$%&’*+/=?^_`{|}~-]+#[a-zA-Z0-9-]+(?:.[a-zA-Z0-9-]+)*$"
)
]
],
password: ["", Validators.required]
});
}
isInvalidForm(fieldName): boolean {
return (
this.loginForm.controls[fieldName].invalid &&
(this.loginForm.controls[fieldName].dirty ||
this.loginForm.controls[fieldName].touched)
);
}
isRequiredForm(fieldName): boolean {
return this.loginForm.controls[fieldName].errors.required;
}
login() {
// debugger;
//console.log(this.loginForm.value);
this.auth.login(this.loginForm.value).subscribe(
token => {
//debugger;
this.router.navigate(["/rentals"]);
},
errorResponse => {
// debugger;
console.log(errorResponse);
// this.mistake = errorResponse.error.errors;
//this.mistake = errorResponse.error;
}
);
}
}
error in browser
TypeError: token.split is not a function
at JwtHelperService.push../node_modules/#auth0/angular-jwt/src/jwthelper.service.js.JwtHelperService.decodeToken (jwthelper.service.js:70)
at AuthService.push../src/app/auth/shared/auth.service.ts.AuthService.saveToken (auth.service.ts:26)
at MapSubscriber.project (auth.service.ts:20)
at MapSubscriber.push../node_modules/rxjs/_esm5/internal/operators/map.js.MapSubscriber._next (map.js:35)
at MapSubscriber.push../node_modules/rxjs/_esm5/internal/Subscriber.js.Subscriber.next (Subscriber.js:54)
at MapSubscriber.push../node_modules/rxjs/_esm5/internal/operators/map.js.MapSubscriber._next (map.js:41)
at MapSubscriber.push../node_modules/rxjs/_esm5/internal/Subscriber.js.Subscriber.next (Subscriber.js:54)
at FilterSubscriber.push../node_modules/rxjs/_esm5/internal/operators/filter.js.FilterSubscriber._next (filter.js:38)
at FilterSubscriber.push../node_modules/rxjs/_esm5/internal/Subscriber.js.Subscriber.next (Subscriber.js:54)
at MergeMapSubscriber.push../node_modules/rxjs/_esm5/internal/operators/mergeMap.js.MergeMapSubscriber.notifyNext (mergeMap.js:84)
error in browser
As it seems the token that is returned from server is not string type
public login(userData: any): Observable<any> {
return this.http.post("/api/v1/users/auth", userData).map(token => {
//debugger;
return this.saveToken(token);
});
}
try to check it what is returned from server

How to implement SSO using an angular 6+ nodejs

I want to implement Single sign-on, but I don't quite understand how to do it. That's what I have at the moment. In fact, I have no mistakes. It's just that Single Sign-on is not working, in both clients I have to log in both times, although the server is one. I use Angular 6 nodejs (express)
token.interceptor.ts:
import { Injectable } from "#angular/core";
import { AuthService } from "../services/auth.service";
import { HttpInterceptor, HttpRequest, HttpHandler, HttpEvent, HttpErrorResponse } from "#angular/common/http";
import { Observable, throwError } from "rxjs";
import { catchError } from "rxjs/operators";
import { Router } from "#angular/router";
#Injectable()
export class TokenInterceptor implements HttpInterceptor {
constructor(private auth: AuthService, private router: Router){
}
intercept(req: HttpRequest<any>, next: HttpHandler): Observable<HttpEvent<any>> {
if (this.auth.isAuthenticated()) {
req = req.clone({
setHeaders: {
Authorization: this.auth.getToken()
}
})
}
return next.handle(req).pipe(
catchError(
(error: HttpErrorResponse) => this.handleAuthError(error)
)
)
}
private handleAuthError(error: HttpErrorResponse): Observable<any> {
if (error.status === 401) {
this.router.navigate(['/login']), {
queryParams: {
sessionFailed: true
}
}
}
return throwError(error)
}
}
app.component.ts:
import { Component, OnInit } from '#angular/core';
import { AuthService } from './shared/services/auth.service';
#Component({
selector: 'app-root',
template: '<router-outlet></router-outlet>'
})
export class AppComponent implements OnInit {
constructor(private auth: AuthService) {}
ngOnInit() {
const potentialToken = localStorage.getItem('auth-token')
if (potentialToken !== null) {
this.auth.setToken(potentialToken)
}
}
}
authService:
import { Injectable } from "#angular/core";
import { HttpClient } from "#angular/common/http";
import { Observable } from "rxjs";
import { tap } from "rxjs/operators";
import { User } from "../interfaces";
#Injectable({
providedIn: 'root'
})
export class AuthService {
private token = null;
constructor(private http: HttpClient) {
}
login(user: User): Observable<{token: string}> {
return this.http.post<{token: string}>('/api/auth/login', user)
.pipe(
tap(
({token}) => {
localStorage.setItem('auth-token', token)
this.setToken(token)
}
)
)
}
setToken(token: string) {
this.token = token
}
getToken(): string {
return this.token
}
isAuthenticated(): boolean {
return !!this.token
}
logout() {
this.setToken(null)
localStorage.clear()
}
}

How to use socket.io in angular with node.js?

Clientside I used Angular 6 and Serverside i used node.js.
Here in angular 6 console it print message and
socket.io id({message: "Hello World", id: "6An-ctwlwbZZWrfMAAAB"})
after using below code.
this code is right or any change in this code bcoz I am not sure about this code kindly help to make correct this.
and another query is I have more than 15 components in my project so how to make common use this socket.io for all components or I have to import this app.component.ts code in all another component.
app.js(serverside)
after installing (npm i socket.io)
const express = require('express');
var app = express();
const http = require('http');
const socketIo = require('socket.io');
const server = http.Server(app);
const io = socketIo(server);
server.listen(3000,function(req,res){
console.log("listen at 3000!");
});
io.on('connection',(socket) => {
socket.emit('hello',{
message : 'Hello World',id: socket.id
})
});
app.component.ts(clientside)
after installing (npm i socket.io)
import * as socketIo from 'socket.io-client';
export class AppComponent implements OnInit {
ngOnInit(){
const socket = socketIo('http://localhost:3000/');
socket.on('hello',(data) => console.log(data));
}
}
}
The one way to achieve this mechanism is using ngx-socket-io, connect your node server at the module level or root level i have implemented like below
app.module.ts code
import { BrowserModule } from '#angular/platform-browser';
import { NgModule } from '#angular/core';
import { SocketIoModule, SocketIoConfig } from 'ngx-socket-io';
import { AppComponent } from './app.component';
const config: SocketIoConfig = { url: 'http://192.168.1.187:9301', options: {} };
#NgModule({
declarations: [
AppComponent
],
imports: [
BrowserModule,
SocketIoModule.forRoot(config),
FormsModule
],
providers: [],
bootstrap: [AppComponent]
})
export class AppModule { }
create one service which handles your incoming and outgoing traffic.
import { Injectable } from '#angular/core';
import { Socket } from 'ngx-socket-io';
#Injectable({
providedIn: 'root'
})
export class SocketService {
constructor(public socket: Socket) { }
getMessage() {
return this.socket
.fromEvent<any>('msg')
.map(data => data.msg);
}
sendMessage(msg: string) {
this.socket.emit('msg', msg);
}
}
Update your code in your component file
export class AppComponent implements OnInit {
constructor(private socketService: SocketService) {}
title = 'app';
incomingmsg = [];
msg = 'First Protocol';
ngOnInit() {
this.socketService
.getMessage()
.subscribe(msg => {
console.log('Incoming msg', msg);
});
this.sendMsg(this.msg);
}
sendMsg(msg) {
console.log('sdsd', msg);
this.socketService.sendMessage(msg);
}
}
Create Service and turn your socket data into Observable stream
import { Injectable } from '#angular/core';
import { BehaviorSubject } from 'rxjs/behaviorSubject';
import { Observer } from 'rxjs/Observer';
import { Observable } from 'rxjs/Observable';
import * as Rx from 'rxjs';
import * as io from 'socket.io-client';
#Injectable()
export class ChatService {
observable: Observable<string>;
socket;
constructor() {
this.socket = io('http://localhost:3000');
}
getData(): Observable<string> {
return this.observable = new Observable((observer) =>
this.socket.on('hello', (data) => observer.next(data))
);
}
// This one is for send data from angular to node
pushData(e) {
this.socket.emit('hello', e);
}
}
Then Call from component
App.component.ts
import { Component } from '#angular/core';
import { ChatService } from './common/chat.service';
#Component({
selector: 'app-root',
templateUrl: './app.component.html',
styleUrls: ['./app.component.scss']
})
export class AppComponent {
title;
chat;
constructor(private cService: ChatService) {
this.cService.getData().subscribe(data => console.log(data));
}
onClick(e: string) {
this.cService.pushData(e);
this.chat = '';
}
}
You can create a service for working with a socket. E.g (of course this is a very simple example):
/* e.g app/shared/io/io.service.ts */
import { Injectable } from '#angular/core';
import { Observable } from 'rxjs/Observable';
import * as socketIo from 'socket.io-client';
const SERVER_URL = '/';
/** Your events enum */
export enum IOEventName {
EVENT_NAME_1 = "EVENT_NAME_1",
EVENT_NAME_2 = "EVENT_NAME_2",
...
}
/** Interfaces for your event messages */
export interface IEventName1Message {
propOne: number,
propTwo: string,
...
}
export interface IEventName2Message {
propOne: Date,
propTwo: Boolean,
...
}
...
#Injectable()
export class SocketService {
private socket: SocketIOClient.Socket;
public initSocket(): void {
this.socket = socketIo(SERVER_URL);
}
public onEvent<T>(event: IOEventName): Observable<T | Array<T>> {
return new Observable<T>(observer => {
this.socket.on(event, (data: T) => observer.next(data));
});
}
public destroy() {
if (this.socket) {
this.socket.removeAllListeners();
this.socket.close();
this.socket = undefined;
}
}
}
And use it in any components:
import { SocketService, IOEventName, IEventName1Message, IEventName2Message }
from 'app/shared/io/io.service';
export class AppComponent implements OnInit, OnDestroy {
constructor(private socketService: SocketService) { }
ngOnInit() {
this.socketService.initSocket();
this.socketService
.onEvent<IEventName1Message>(IOEventName.EVENT_NAME_1)
.subscribe(data => { /* message received */ });
this.socketService
.onEvent<IEventName2Message>(IOEventName.EVENT_NAME_2)
.subscribe(data => { /* message received */ });
}
ngOnDestroy() {
this.socketService.destroy();
}
}

Resources