I'm trying to create a chat-app and would like the message that the user sends, to be displayed in the chatroom (called feed in my project), just like all the other already existing messages are shown (i have a getMessages()
for that)
how can I push the message that the user types, to my chatMessage[]
?
here is my message.Service.ts
@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"); //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});
}
}
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)}))
}
}
my feed.html
<div *ngFor="let message of ( chatMessages$ | async )" >
<app-message [chatMessage]="message"> </app-message>
</div>
my chat-message.model.ts
export class ChatMessage {
$id?: string;
email?: string;
userName?: string;
msg_content?: string;
timeSent?: Date = new Date();
}
here's what I already tried:
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
You can't just push something to an observable as it's a stream. What you described can be done with many different approaches. For example you can create BehaviorSubject of ChatMessage[] which will store "recent" messages and then chatMessages$ should be a combination of backend messages and recent ones.
My proposition (the first one which came to my mind, did some conception testing in the playground here: 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);
// you can move this to 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(); // creating subscription for sendMessage subject to update recentMessages collection
}
ngOnDestroy(){
this._destroy.next();
this._destroy.complete();
}
}