add the string to the chat messages array in the observable

Currently, I am in the process of developing a chat application and my goal is to showcase the user's messages in the chatroom, referred to as the feed in this project. I have already implemented a function called getMessages() that displays all existing messages. Now, I am looking for guidance on how to add the user's typed message to my chatMessage[].

In my message.Service.ts, I have:

@Injectable({
  providedIn: 'root'
})
export class MessageService {
  user: User;
  userName: Observable<string>;

  chatMessage: ChatMessage;
  chatMessages$: Observable<ChatMessage[]>;

    constructor(
    private http: HttpClient,
    private accountService: AccountService) {
      this.user = this.accountService.userValue;
     }
 

sendMessage(msg: string){
  const timestamp = this.getTimeStamp()

this.chatMessages$ = this.getMessages();
this.chatMessages$.push({ //having trouble here
  msg_content: msg,       //Property 'push' does not exist on type 'Observable<ChatMessage[]>'.
  timeSent: timestamp,
  userName: this.userName,
  email: this.user.email });

var formdata = new FormData();
formdata.append("sender_email", this.user.email);
formdata.append("sender_key", this.user.api_key);
formdata.append("msg_content", msg);
this.http.post("http://11.123.456.78:5000/messages/send_message", formdata )
  .subscribe(response => console.log(response), error => console.log('oops', error));
}

getMessages(){
  let params = new HttpParams().set("sender_email", "this.user.email").set("sender_key", "someapikey"); 
  var url = "http://11.123.456.78:5000/messages/get_all_messages";
   return this.chatMessages$ = this.http.get<ChatMessage[]>(url, { params: params});
 }
}

In my feed.component.ts:

export class FeedComponent implements OnInit {
  chatMessages$: Observable<ChatMessage[]>;
  messages;
  messageObjects: string[] = [];

  constructor(private chat: MessageService) { 
  }
 
  ngOnInit() {
  this.messages = this.chat.getMessages().subscribe((allmessagesdata => { this.messages = allmessagesdata
  for (let messageObject of this.messages){
    this.messageObjects.push(messageObject.msg_content)
  };
 }));
 this.chatMessages$ = this.chat.getMessages();
 
  }
  
  ngOnchanges(){
    this.chat.getMessages().subscribe((allmessagesdata => { this.messages = allmessagesdata,  console.log(this.messages)}))
    
  }
}

In my feed.html:

<div *ngFor="let message of ( chatMessages$ | async )" >
<app-message [chatMessage]="message"> </app-message>    
</div>

In my chat-message.model.ts:

export class ChatMessage {
    $id?: string;
    email?: string;
    userName?: string;
    msg_content?: string;
    timeSent?: Date = new Date();
}

I have attempted:

I tried setting

private subject = new Subject<string>();
and then saying this.subject.next(msg) in my sendMessage() function and then I created

getMessage():Observable<string> {
  return this.subject.asObservable();
}

and subscribed to it in my feed, but this only allows for one value of message.

My knowledge of observables and subjects is limited and I can't seem to fully grasp this concept yet, but I know I had to use them for this purpose, so any help would be really appreciated

Answer №1

Utilizing observables requires a different approach than simply pushing data to them, as they are streams. One way to achieve the functionality you described is by creating a BehaviorSubject for storing "recent" messages and combining it with backend messages in chatMessages$.

Here's my suggested solution (after some testing on this playground: https://stackblitz.com/edit/rxjs-evjqhb)

@Injectable({
  providedIn: 'root'
})
export class MessageService implements OnInit, OnDestroy{
  user: User;
  userName: Observable<string>;

  private _sendMessage = new Subject<ChatMessage>();
  private _destroy = new Subject<ChatMessage>();
  private _recentMessages = new BehaviorSubject<ChatMessage[]>([]);
  
  
  chatMessage: ChatMessage;
  existedChatMessages$: Observable<ChatMessage[]>;
  recentChatMessages$: Observable<ChatMessage[]>;
  chatMessages$: Observable<ChatMessage[]>;

    constructor(
    private http: HttpClient,
    private accountService: AccountService) {
      this.user = this.accountService.userValue;
     }
 

sendMessage(msg: string){
  const timestamp = this.getTimeStamp()


const message = {
  msg_content: msg,
  timeSent: timestamp,
  userName: this.userName,
  email: this.user.email };

  
  this._sendMessage.next(message);

// consider moving this logic into a tap() operator in the _sendMessage subject
var formdata = new FormData();
formdata.append("sender_email", this.user.email);
formdata.append("sender_key", this.user.api_key);
formdata.append("msg_content", msg);
this.http.post("http://11.123.456.78:5000/messages/send_message", formdata )
  .subscribe(response => console.log(response), error => console.log('oops', error));
}

getMessages(){
  let params = new HttpParams().set("sender_email", "this.user.email").set("sender_key", "someapikey"); //Create new HttpParams 
  var url = "http://11.123.456.78:5000/messages/get_all_messages";
   return this.chatMessages$ = this.http.get<ChatMessage[]>(url, { params: params});
 }
 
 ngOnInit(){
    chatMessages$ = this.existedChatMessages$.pipe(
        switchMap(existed=> 
            this.recentChatMessages$].pipe(
                map(recent)=>[...(existed||[]), ...recent]
            )
        )
    ); // combine streams of backend & recent messages
    
    this.existedChatMessages$ = this.getMessages();
    this.recentChatMessages$ = this._recentMessages.asObservable();
    this._recentMessages.pipe(
        switchMap(recentMessages => this._sendMessage.pipe(
                map(message=> [...recentMessages, message]))
            )
        ),
        tap(x=> this._recentMessages.next(x)),
        takeUntil(this._destroy)
    ).subscribe(); // subscribing to sendMessage subject to update recentMessages collection
    
    
 }
 
 ngOnDestroy(){
    this._destroy.next();
    this._destroy.complete();
 }
 
}

Similar questions

If you have not found the answer to your question or you are interested in this topic, then look at other similar questions below or use the search

What exactly is happening with this Plunkr code?

When variables or expressions are used within brackets [] in Angular, what is the scope of these variables? In the Plunkr code provided below, the variable helloWorld is defined in the rio-app but utilized in the rio-hello. Does Angular search all the way ...

The State of NgRX Entity is encountering undefined IDs

I decided to experiment with @ngrx/entity in a simple "Todo" project, where I had only one AppModule, one reducer, and a single component. However, as I delved into it, I encountered some challenges. The actions I defined were quite basic, focusing on CRU ...

SQL Exception: The value for the first parameter is not defined

I'm encountering an issue with a SqlError while trying to retrieve data from my database. It seems like the problem is within my fetchData function where I might not be passing the two parameters (startDate and endDate) correctly. The specific SqlErr ...

Error code 1 in Ionic V5 Capacitor - cordova-plugin-media indicates a problem with media playback

Despite installing the plugin and ensuring all necessary permissions are set, I am still encountering error code 1 with the media plugin. I have also included <application android:requestLegacyExternalStorage="true" /> in <edit-config&g ...

What is the best method for compressing and decompressing JSON data using PHP?

Just to clarify, I am not attempting to compress in PHP but rather on the client side, and then decompress in PHP. My goal is to compress a JSON array that includes 5 base64 images and some text before sending it to my PHP API. I have experimented with l ...

Retrieve the object property based on an array of indices

I am looking to create a function that can retrieve a specific property of an object based on an array of property names const getObjectProperty = (arr: string[], object: any) { // This function should return the desired object property } Expected Outco ...

The TypeScript error occurs when attempting to assign a type of 'Promise<void | Object>' to a type of 'Promise<Object>' within a Promise.then() function

I'm currently working on a service to cache documents in base64 format. The idea is to first check sessionStorage for the document, and if it's not there, fetch it from IRequestService and then store it in sessionStorage. However, I've encou ...

Using TypeScript to validate the API response against specific types

I'm intrigued by the scenario where you expect a specific data type as a response from fetch / Axios / etc, but receive a different type instead. Is there a way to identify this discrepancy? interface HttpResponse<T> extends Response { parsed ...

The serverTimeStamp() function in firebase.firestore.FieldValue does not allow for the Timestamp data type to be used

async addNewUser(id: string, email: string) { await this.afs.doc<MemberProfileModel>(FirestoreDbConstant.MEMBER_PROFILES + `/${id}`).set({ email, registeredDate: firebase.firestore.FieldValue.serverTimestamp(), }); } This appro ...

Secure a reliable result from a function utilizing switch statements in Typescript

There's a function in my code that takes an argument with three possible values and returns a corresponding value based on this argument. Essentially, it can return one of three different values. To achieve this, I've implemented a switch statem ...

The explanation of the Angular tutorial is not displayed correctly

Hi there! I was working on a tutorial in Angular about using geofire location queries with Google Maps. It was quite interesting and I followed all the instructions provided in this video tutorial: . However, when I completed the project and ran it, I ende ...

Accessing the value of an object nested within another object in Angular

I have encountered numerous similar topics, but after going through all of them, I still cannot pinpoint what I am doing incorrectly. The data I retrieve is from DEXIE (indexedDB) where my record is stored in the following format: async addRequestToLocalD ...

Attempting to modify read-only properties is prohibited in strict mode within the context of [background: url({{XXX}}) no-repeat center center

I encountered an issue in Edge, but everything works fine in Chrome. I can't figure out what's causing the problem... <div class="container-fluid project_img" style="background: url({{_project.images.web}}) no-repeat center center;"> ...

Extracting data from response body in Angular after encountering 403 error during HTTP Post request

I am currently working on an Angular 9 project where I handle login functionality using HTTP post and HttpClient. In case of a failed login attempt, the server responds with HTTP status code 403 and a JSON object containing the error message that needs to ...

Typescript Code Coverage with karma-jasmine and istanbul: A complete guide

I am attempting to calculate the Code Coverage for my typescript Code in karma framework using Istanbul. In the karma.conf file, typescript files are added and through karma typescript-preprocessor we are able to conduct unit testing and code coverage of t ...

The absence of a 'body' argument in the send() json() method within the Next.js API, coupled with TypeScript, raises an important argument

Currently, I have set up an API route within Next.js. Within the 'api' directory, my 'file.tsx' consists of the following code: import type { NextApiRequest, NextApiResponse } from "next"; const someFunction = (req: NextApiReq ...

What is the best way to display noscript content within my Angular application?

What is the best way to show HTML content for users who do not have JavaScript enabled in my Angular application? Inserting the noscript tag directly into the index.html file does not seem to be effective. <body> <noscript>Test</noscrip ...

Tips for dynamically implementing a pipe in Angular 5

In my Angular application, I have implemented a filter using a pipe to search for option values based on user input. This filter is applied at the field level within a dynamically generated form constructed using an ngFor loop and populated with data from ...

Ionic 4 FormBuilder fails to recognize automatically filled data retrieved from database during submission

I am brand new to working with Ionic. I've set up an ionic form with ReactiveFormsModule and FormsModule initialized in edit-profile.page.module.ts, with the intention of updating a user's profile information. However, despite filling out all the ...

Using optional function arguments with destructured arguments in TypeScript can result in throwing an error

interface Type1 { attr1: string; attr2: string; } interface Type2 { attr1: string; attr2: string; attr3: string; // additional attribute } function fn(config: Type1 | Type2): void { // The error code is displayed above. I am ...