I am getting the following error when returning from a http service and attempting to push to response onto an array :
Cannot read property 'messages' of undefined
This is my chat.component.ts file :
import { Component, OnInit, OnDestroy } from '#angular/core';
import { ChatService } from './chat.service';
#Component({
selector: 'chat-component',
template: `
<div *ngIf="messages">
<div *ngFor="let message of messages">
{{message.text}}
</div>
</div>
<input [(ngModel)]="message" /><button (click)="sendMessage()">Send</button>
`,
providers: [ChatService]
})
export class ChatComponent implements OnInit, OnDestroy {
messages = [];
connection;
message;
loading;
constructor(private chatService: ChatService) { }
sendMessage() {
this.chatService.sendMessage(this.message);
this.message = '';
}
ngOnInit() {
this.chatService.initPlaylist().subscribe(tracks => {
tracks.forEach(function(item) {
this.messages.push({
message: item.trackID,
type: "new-message"
});
});
})
this.connection = this.chatService.getMessages().subscribe(message => {
this.messages.push(message);
})
}
ngOnDestroy() {
this.connection.unsubscribe();
}
}
This is my chat.service.ts
import { Injectable } from '#angular/core';
import { Http, Response } from '#angular/http';
import { Subject } from 'rxjs/Subject';
import { Observable } from 'rxjs/Rx';
import * as io from 'socket.io-client';
#Injectable()
export class ChatService {
private url = 'http://localhost:1337';
private socket;
constructor(private http: Http) {
}
sendMessage(message) {
this.socket.emit('add-message', message);
}
initPlaylist() {
return this.http.get(this.url + '/playlist')
.map(this.extratData)
.catch(this.handleError);
}
getMessages() {
let observable = new Observable(observer => {
this.socket = io(this.url);
this.socket.on('message', (data) => {
observer.next(data);
});
return () => {
this.socket.disconnect();
};
})
return observable;
}
private extratData(res: Response) {
let body = res.json();
return body || {};
}
private handleError(error: Response | any) {
// In a real world app, we might use a remote logging infrastructure
let errMsg: string;
if (error instanceof Response) {
const body = error.json() || '';
const err = body.error || JSON.stringify(body);
errMsg = `${error.status} - ${error.statusText || ''} ${err}`;
} else {
errMsg = error.message ? error.message : error.toString();
}
console.error(errMsg);
return Observable.throw(errMsg);
}
}
I currently have a form on the front end, in which users can add a message, this is then pushed onto this.messages and through socket.io sent out to all connected sockets.
What I am now doing is storing messages in a mongodb via an express app using mongoose.
On page load, I would like to retrieve these messages from the document store, and push them onto this.messages - so the view is updated with previous messages, then socket.io should take over on new messages, adding them to the array.
As this is an initial call, once on load, I am not using socket.io to grab these, instead I have an api route setup through express, returning json that looks as follows :
[
{
"_id": "58109b3e868f7a1dc8346105",
"trackID": "This is my message...",
"__v": 0,
"status": 0,
"meta": {
"played": null,
"requested": "2016-10-26T12:02:06.979Z"
}
}
]
However when I get to this section of code within chat.component.ts, everything breaks down with the previously mentioned error..
this.chatService.initPlaylist().subscribe(tracks => {
tracks.forEach(function(item) {
this.messages.push({
message: item.trackID,
type: "new-message"
});
});
})
I using Angular 2, Socket.io, ExpressJS and MongoDB.
don't use function () use instead () => (arrow function) for this.... to keep pointing to the local class instance
tracks.forEach((item) => {
https://developer.mozilla.org/en/docs/Web/JavaScript/Reference/Functions/Arrow_functions
Related
I am writing a web application in Nuxt 3 and want to integrate a SockJs over Stomp websocket. Now I have written the component...
<template>
<div>
<h1>Test</h1>
</div>
</template>
<script lang="ts" setup>
import {useRouter, ref} from "#imports";
import SockJS from "sockjs-client";
import {Stomp} from "#stomp/stompjs";
let consoleText = ref<string>("");
interface ConsoleMessage {
messageContent: string,
user: string,
}
const props = defineProps<{
originURL:string,
publicURL:string,
privateURL:string
}>();
let stompClient: any = null;
function connect() {
const socket = new SockJS(props.originURL);
stompClient = Stomp.over(socket);
stompClient.connect({}, function () {
stompClient.subscribe(props.publicURL, function (message: { body: string; }) {
showMessage(JSON.parse(message.body));
});
stompClient.subscribe(props.privateURL, function (message: { body: string; }) {
showMessage(JSON.parse(message.body));
});
});
}
function showMessage(message: ConsoleMessage) {
consoleText.value = "\n" + message.messageContent + "\n";
}
connect();
</script>
I have imported #stomp/stompjs and sockjs-client via yarn add. What do I have to change in the nuxt.config.ts to make it so that the application loads the modules.
Edit:
Following the advice given by nur_iyad I attempted to write a plugin
import {defineNuxtPlugin} from "nuxt/app";
import SockJS from "sockjs-client";
import {CompatClient, Stomp} from "#stomp/stompjs";
import {Message} from "~/composables/display";
export default defineNuxtPlugin((nuxtApp) => {
nuxtApp.provide('sock', () => new NuxtSockJs())
})
declare module '#app' {
interface NuxtApp {
$sock (): NuxtSockJs
}
}
export class NuxtSockJs {
stompInstance: CompatClient | null;
constructor() {
this.stompInstance = null;
}
connect(originURL:string,
subscribeURLs:Array<string>,
displayMessage: (message: Message) => any): void {
const socket = new SockJS(originURL);
let stompClient: CompatClient = Stomp.over(socket);
stompClient.connect({}, function () {
for(const subscribeURL of subscribeURLs) {
stompClient.subscribe(subscribeURL, function (message: { body: string; }) {
displayMessage(JSON.parse(message.body));
});
}
});
this.stompInstance = stompClient;
}
sendMessage(sendURL: string, message: Message):void {
(this.stompInstance)!.send(sendURL, {}, JSON.stringify(message));
}
}
This does not work and just throws the following error:
Uncaught ReferenceError: global is not defined
at node_modules/sockjs-client/lib/utils/browser-crypto.js
I am currently writing an angular project that opens a websocket connection with a NodeJS server. This is the service:
export class WebsocketService {
socket : any;
constructor() { }
setupSocketConnection(){
this.socket = io(environment.SOCKET_ENDPOINT);
this.socket.emit('message', 'The client wants to intruduce itself to the server');
this.socket.on('broadcast', (data: string) => {
console.log(data);
});
}
disconnect() {
if (this.socket) {
this.socket.disconnect();
}
}
}
and this is my component:
export class AppComponent {
title = '-';
constructor(private websocket : WebsocketService) { }
ngOnInit(){
this.websocket.setupSocketConnection();
}
ngOnDestroy() {
this.websocket.disconnect();
}
}
My question is: how can I pass "data" from the broadcast event listener into the component to display it there? Another service would be a solution, but I dont think it would be a good one. I could also put the listener into a function and call it from the component, but wouldn't that violate the encapsulation concepts of services?
Thank you
You could use BehaviorSubject by following theses steps:
Imagine sending JSON object holding a "type" field: Make sure to stringify data sent using
1- Server side:
JSON.stringify({type: "message", value: "whatever"})
2- Now client side
export class WebsocketService {
// Put the right data type here
message = new BehaviorSubject<string>('');
connection = new BehaviorSubject<string>('');
socket : any;
constructor() { }
setupSocketConnection(){
this.socket = io(environment.SOCKET_ENDPOINT);
this.socket.emit('message', 'The client wants to intruduce itself to the server');
this.socket.on('broadcast', (data: string) => {
const jsonObject = JSON.parse(data);
switch (jsonObject.type) {
case "message":
this.message.next(jsonObject.value);
break;
case "connection":
this.connection.next(jsonObject.value);
break;
default:
throw new Error('Unknown message type' + jsonObject.type)
break;
}
});
}
disconnect() {
if (this.socket) {
this.socket.disconnect();
}
}
}
And on there other hand, just subscribe to your data behaviorSubject emited values.
export class AppComponent implements OnInit, OnDestroy {
title = '-';
subscriptions: Subscription[] = [];
constructor(private websocket : WebsocketService) { }
ngOnInit(){
this.websocket.setupSocketConnection();
this.websocket.message.subscribe(value => {
// Do your stuff here.
console.log(value);
})
this.websocket.connection.subscribe(value => {
// Do your stuff here.
console.log(value);
})
}
ngOnDestroy() {
this.websocket.disconnect();
this.subscriptions.forEach(s => s.unsubscribe());
this.subscription = [];
}
}
I am working on chat application, using socketIO
Whenever user signed in sucessfully, user is navigated to dashboard and list of current loggedin users will be displayed.
Whenever new user is signed in, existing user list is not getting updated.
Adding the necessary code here
events: backend
let verifyClaim = require("./tokenLib");
let socketio = require("socket.io");
let tokenLibs = require('./tokenLib');
let setService = (server) => {
let onlineUsers = [];
let io = socketio.listen(server);
let myio = io.of('')
myio.on('connection', (socket) => {
console.log(' emitting verify user');
socket.emit("verifyUser", "");
socket.on('set-user', (authToken) => {
console.log(authToken);
tokenLibs.verifyTokenWithoutSecret(authToken, (user, err,) => {
if (user) {
console.log(user);
let currentUser = user;
socket.userId = currentUser._id;
let fullName = `${currentUser.name}`
console.log(`${fullName} is online`);
socket.emit(currentUser._id, `${fullName} is online`)
let userObj = { userId: currentUser._id, name: fullName }
onlineUsers.push(userObj);
console.log(onlineUsers)
socket.emit('userlist', onlineUsers)
}
else {
socket.emit('auth-error', { status: 500, error: 'Please provide valid token ' })
}
})
})
socket.on('disconnect', () => {
console.log('user is disconnected');
let removeUserId = onlineUsers.map(function (user) { return user.userId }).indexOf(socket.userId)
onlineUsers.splice(removeUserId, 1)
console.log(onlineUsers)
})
})
}
module.exports = { setService: setService }
socket service:
import { Injectable } from '#angular/core';
import { HttpClient } from '#angular/common/http';
import * as io from 'socket.io-client';
import { Observable } from 'rxjs';
#Injectable({
providedIn: 'root'
})
export class SocketService {
public prod = 'https://todolistbe.herokuapp.com/api/v1';
public dev = 'http://localhost:3001';
public baseUrl = this.dev;
private socket;
constructor(public http: HttpClient) {
this.socket=io('http://localhost:3001')
}
public verifyUser=()=>{
return Observable.create((observer)=>{
this.socket.on('verifyUser',(data)=>{
observer.next(data);
})
})
}
public setUser=(authToken)=>{
this.socket.emit("set-user",authToken)
}
public userList=()=>{
return Observable.create((observer)=>{
this.socket.on('userlist',(data)=>{
observer.next(data);
})
})
}
public welcomeUser=(userid)=>{
return Observable.create((observer)=>{
this.socket.on(userid,(data)=>{
observer.next(data);
})
})
}
public disconnectUser = () => {
return Observable.create((observer) => {
this.socket.on('disconnect', () => {
observer.next()
})
})
}
}
dashboard:
import { Component, OnInit } from '#angular/core';
import { ThemePalette } from '#angular/material/core';
import { SocketService } from '../../socket.service';
import { ToastrService } from 'ngx-toastr';
export interface Task {
name: string;
completed: boolean;
color: ThemePalette;
subtasks?: Task[];
}
#Component({
selector: 'app-dashboard',
templateUrl: './dashboard.component.html',
styleUrls: ['./dashboard.component.css'],
providers: [SocketService]
})
export class DashboardComponent implements OnInit {
public authToken: any = localStorage.getItem('authToken');
public userList: any = [];
public userNotification;
allComplete: boolean = false;
ngOnInit(): void {
this.verifyUserConfirmation();
this.getOnlineUsers();
}
public verifyUserConfirmation: any = () => {
this.SocketService.verifyUser().subscribe((data) => {
console.log(this.authToken)
this.SocketService.setUser(this.authToken);
this.getOnlineUsers();
})
}
selected = 'option2';
toggleNavbar() {
console.log('toggled' + this.isMenuOpened);
this.isMenuOpened = !this.isMenuOpened;
}
getOnlineUsers() {
// this.SocketService.welcomeUser(localStorage.getItem('id')).subscribe((data)=>{
// this.userNotification=data;
// console.log("hi:"+this.userNotification)
// })
this.SocketService.userList().subscribe((user) => {
this.userList = [];
for (let x in user) {
let tmp = { 'user': x, 'name': user[x] }
this.userList.push(tmp);
}
console.log(this.userList)
})
}
}
Whenever you want to emit an event with all other users, we should use myio.emit instead of socket.io.
Issue is resolved when i made necessary changed in my backend events library
I have created a MEAN stack application which does the basic job of inserting,delete,update and viewing the data from mongoDB.
first of all i cloned this MEAN stack application from github. the application was based on the employee, but i renamed all the components, routing, etc from 'employee' to 'sensor'. what i have done literally is changed the word 'employee' to 'sensor'.
and i had not issues in compiling the code.
the build was successful.
but when i launched localhost:4200 , the first page was displayed properly,which is insert component. the data is inserted into mongodb. so this component has no issues.
but when i click on view sensor component,it shows a blank page.
so when i checked on chrome console by clicking on f12,it showed a list of errors.
please check for the errors in the below screenshot.4
the service.api code is below
import { Injectable } from '#angular/core';
import { throwError } from 'rxjs';
import { Observable } from 'rxjs/Observable';
import { catchError, map } from 'rxjs/operators';
import { HttpClient, HttpHeaders, HttpErrorResponse } from '#angular/common/http';
#Injectable({
providedIn: 'root'
})
export class ApiService {
baseUri:string = 'http://localhost:4000/api';
headers = new HttpHeaders().set('Content-Type', 'application/json');
getSensors: any;
constructor(private http: HttpClient) { }
// Create
createSensor(data): Observable<any> {
let url = `${this.baseUri}/create`;
return this.http.post(url, data)
.pipe(
catchError(this.errorMgmt)
)
}
// Get Sensor
getSensor(id): Observable<any> {
let url = `${this.baseUri}/read/${id}`;
return this.http.get(url, {headers: this.headers}).pipe(
map((res: Response) => {
return res || {}
}),
catchError(this.errorMgmt)
)
}
// Update Sensor
updateSensor(id, data): Observable<any> {
let url = `${this.baseUri}/update/${id}`;
return this.http.put(url, data, { headers: this.headers }).pipe(
catchError(this.errorMgmt)
)
}
// Delete Sensor
deleteSensor(id): Observable<any> {
let url = `${this.baseUri}/delete/${id}`;
return this.http.delete(url, { headers: this.headers }).pipe(
catchError(this.errorMgmt)
)
}
// Error handling
errorMgmt(error: HttpErrorResponse) {
let errorMessage = '';
if (error.error instanceof ErrorEvent) {
// Get client-side error
errorMessage = error.error.message;
} else {
// Get server-side error
errorMessage = `Error Code: ${error.status}\nMessage: ${error.message}`;
}
console.log(errorMessage);
return throwError(errorMessage);
}
}
SENSOR-LIST.COMPONENT.TS is below
import { Component, OnInit } from '#angular/core';
import { ApiService } from './../../service/api.service';
#Component({
selector: 'app-Sensor-list',
templateUrl: './Sensor-list.component.html',
styleUrls: ['./Sensor-list.component.css']
})
export class SensorListComponent implements OnInit {
Sensor: any = [];
constructor(private apiService: ApiService) {
this.readSensor();
}
ngOnInit() {}
readSensor() {
this.apiService.getSensors.subscribe ((data) => {
this.Sensor = data;
});
}
removeSensor(Sensor, index) {
if (window.confirm('Are you sure?')) {
this.apiService.deleteSensor(Sensor._id).subscribe((data) => {
this.Sensor.splice(index, 1);
}
);
}
}
}
some of the screenshots
img 12
img 23
img 34
please help me out in this problem
getSensors is not a function first of all. You declared it in ApiService as a variable of type any. So if you want the list of sensors. Create the getSensors() method which will allow you to retrieve the list of sensors via the URL intended for it
This is my Page-datail.component.ts
export class PageDetailComponent implements OnInit {
private apiUrl="http://localhost:3000/pages"
pages={};
// data={};
constructor(private route:ActivatedRoute,private page:PageService,private router: Router,private http:Http) { }
ngOnInit() {
this.getpage(this.route.snapshot.params['title']);
// this.getPages();
// this.getData();
}
getpage(title) {
this.page.getPage(title)
.subscribe(pages => {
console.log(pages);
this.pages = pages;
}, err => {
console.log(err);
});
}
This is the response I am getting in the Console. I am getting this Object Object Error and it fails to render it in html.
<a *ngFor="let p of pages" class="list-group-item list-group-item-action">
{{p.title}}
</a>
This is the response I am getting in the console
try this,
export class PageDetailComponent implements OnInit {
private apiUrl="http://localhost:3000/pages"
pages=[];
// data={};
constructor(private route:ActivatedRoute,private page:PageService,private router: Router,private http:Http) { }
ngOnInit() {
this.getpage(this.route.snapshot.params['title']);
// this.getPages();
// this.getData();
}
getpage(title) {
this.page.getPage(title)
.subscribe(page => {
console.log(page);
this.pages.push(page);
}, err => {
console.log(err);
});
}
The reason is i am getting this that was i am Returning my data in Object not Array I done it this way like
{{pages.title}}
and You Can make it to Objects Like this
pages:any={}; //For Objects
page:any=[]; //For Arrays