Adding an active class on a selected chat list item in Angular - here's how!

We are currently developing a chat component where users can click on the left side chat item to open messages with the selected user. We have implemented an active class that changes the color of the selected chat list item. Our goal is to apply the active class when any chat list item is selected. Here is the code snippet:

Messages service

import { Observable } from 'rxjs';
import { HttpClient } from '@angular/common/http';
import { Injectable,EventEmitter } from '@angular/core';
import { Message } from '@angular/compiler/src/i18n/i18n_ast';

const BASEURL = 'http://localhost:3000/api/chatapp';

@Injectable({
  providedIn: 'root'
})
export class MessageService {
  constructor(private http: HttpClient) {}

  SendMessage(senderId, receiverId, receiverName, message): Observable<any> {
    return this.http.post(`${BASEURL}/chat-messages/${senderId}/${receiverId}`, {
      receiverId,
      receiverName,
      message
    });
  }

  GetAllMessages(senderId, receiverId): Observable<any> {
    return this.http.get(`${BASEURL}/chat-messages/${senderId}/${receiverId}`);
  }

  MarkMessages(sender, receiver): Observable<any> {
    return this.http.get(`${BASEURL}/receiver-messages/${sender}/${receiver}`);
  }

  MarkAllMessages(): Observable<any> {
    return this.http.get(`${BASEURL}/mark-all-messages`);
  }

  chatSelected = new EventEmitter<Message>();
}

Route

path: 'chat/:name',
component: MessageComponent,
canActivate: [AuthGuard]

HTML

    <div id="frame">
        <div id="sidepanel">
            <div id="contacts" >
                <ul *ngFor="let chat of chatList">
                    <li class="contact" 
[ngClass]="{'active': chat.receiverId.username === this.selectedChat.receiverId.username}">
                        <div class="wrap" (click)="GoToChatPage(chat.receiverId.username)">
                            <span class="contact-status online"></span>
                            <img src="http://res.cloudinary.com/ratingapp/image/upload/v{{chat.receiverId.picVersion}}/{{chat.receiverId.picId}}" alt="" />
                            <div class="meta">
                                <p class="name">{{chat.receiverId.username}}</p>
                                <p class="preview">
                                        {{chat.msgId.message[chat.msgId.message.length - 1].body.substr(0, 10)}}...</p>
                            </div>
                        </div>
                    </li>
                </ul>
            </div>
      </div>
        <div class="content">
            <div class="contact-profile">
                <img src="http://emilcarlsson.se/assets/harveyspecter.png" alt="" />
                <p>Harvey Specter</p>
                <div class="social-media">
                    <i class="material-icons">cancel</i>

                </div>
            </div>
            <div class="messages">
                <ul *ngFor="let message of messagesArray">

                    <li class="sent" *ngIf="user.username !== receiver && user.username !== message.sendername">
                        <img src="http://res.cloudinary.com/ratingapp/image/upload/v{{chat.receiverId.picVersion}}/{{chat.receiverId.picId}}" alt="" />
                        <p>{{message.body}}</p>
                    </li>
                    <li class="replies" *ngIf="user.username === message.sendername">
                        <img src="http://res.cloudinary.com/ratingapp/image/upload/v{{chat.receiverId.picVersion}}/{{chat.senderId.picId}}" alt="" />
                        <p>{{message.body}}</p>
                    </li>
                </ul>
            </div>
            <form (ngSubmit)="SendMessage()">
            <div class="message-input">
                <div class="wrap">
                <input [(ngModel)]="message" (keypress)="IsTyping()" type="text" placeholder="Write your message..." />
                <button class="emojiBtn" (click)="Toggled()">😄</button>
                <div class="emoji-content-editable" (emojiPickerCaretEmitter)="HandleCurrentCaret($event)" (input)="content = $event.target.textContent"
                [textContent]="content" contenteditable="true"></div>

              <i class="emoji-toggle-button imgBtn" [(emojiPickerIf)]="toggled" [emojiPickerPreserveSelection]="false" [emojiPickerDirection]="direction"
                [emojiPickerAutofocus]="true" (emojiPickerSelect)="HandleSelection($event)"></i>
                <button class="submit"><i class="fa fa-paper-plane" aria-hidden="true"></i></button>
                </div>
            </div>
            </form>
        </div>
    </div>

TypeScript

export class MessageComponent implements OnInit {

  @Input() users;
  receiver: string;
  user: any;
  message: string;
  receiverData: any;
  messagesArray = [];
  socket: any;
  typingMessage;
  typing = false;
  isOnline = false;
  chatList = [];

  public eventMock;
  public eventPosMock;

  public direction =
    Math.random() > 0.5 ? (Math.random() > 0.5 ? 'top' : 'bottom') : Math.random() > 0.5 ? 'right' : 'left';
  public toggled = false;
  public content = ' ';

  private _lastCaretEvent: CaretEvent;

  constructor(
    private tokenService: TokenService,
    private msgService: MessageService,
    private route: ActivatedRoute,
    private usersService: UsersService
  ) {
    this.socket = io('http://localhost:3000');
  }

  ngOnInit() {
    this.user = this.tokenService.GetPayload();
    this.route.params.subscribe(params => {
      this.receiver = params.name;
      this.GetUserByUsername(this.receiver);

      this.socket.on('refreshPage', () => {
        this.GetUserByUsername(this.receiver);
      });
    });

    this.socket.on('is_typing', data => {
      if (data.sender === this.receiver) {
        this.typing = true;
      }
    });

    this.socket.on('has_stopped_typing', data => {
      if (data.sender === this.receiver) {
        this.typing = false;
      }
    });
  }

  ngOnChanges(changes: SimpleChanges) {
    const title = document.querySelector('.nameCol');
    if (changes.users.currentValue.length > 0) {
      const result = _.indexOf(changes.users.currentValue, this.receiver);
      if (result > -1) {
        this.isOnline = true;
        (title as HTMLElement).style.marginTop = '10px';
      } else {
        this.isOnline = false;
        (title as HTMLElement).style.marginTop = '20px';
      }
    }
  }

  ngAfterViewInit() {
    const params = {
      room1: this.user.username,
      room2: this.receiver
    };

    this.socket.emit('join chat', params);
  }

  GetUser(id) {
    this.usersService.GetUserById(id).subscribe(data => {
      this.chatList = data.result.chatList;
    });
  }

  GetUserByUsername(name) {
    this.usersService.GetUserByName(name).subscribe(data => {
      this.receiverData = data.result;

      this.GetMessages(this.user._id, data.result._id);
    });
  }

  GoToChatPage(chat, name) {
        this.selectedChat = chat;
        this.router.navigate(['chat', name]);
        this.msgService.MarkMessages(this.user.username, name).subscribe(data => {
          this.socket.emit('refresh', {});
        });
      }

  GetMessages(senderId, receiverId) {
    this.msgService.GetAllMessages(senderId, receiverId).subscribe(data => {
      this.messagesArray = data.messages.message;
    });
  }

  SendMessage() {
    if (this.message) {
      this.msgService
        .SendMessage(this.user._id, this.receiverData._id, this.receiverData.username, this.message)
        .subscribe(data => {
          this.socket.emit('refresh', {});
          this.message = '';
        });
    }
  }

  HandleSelection(event: EmojiEvent) {
    this.content =
      this.content.slice(0, this._lastCaretEvent.caretOffset) +
      event.char +
      this.content.slice(this._lastCaretEvent.caretOffset);
    this.eventMock = JSON.stringify(event);
    this.message = this.content;

    this.toggled = !this.toggled;
    this.content = '';
  }

  HandleCurrentCaret(event: CaretEvent) {
    this._lastCaretEvent = event;
    this.eventPosMock = `{ caretOffset : ${event.caretOffset}, caretRange: Range{...}, textContent: ${
      event.textContent
    } }`;
  }

  Toggled() {
    this.toggled = !this.toggled;
  }

  IsTyping() {
    this.socket.emit('start_typing', {
      sender: this.user.username,
      receiver: this.receiver
    });

    if (this.typingMessage) {
      clearTimeout(this.typingMessage);
    }

    this.typingMessage = setTimeout(() => {
      this.socket.emit('stop_typing', {
        sender: this.user.username,
        receiver: this.receiver
      });
    }, 500);
  }
}

If you have any ideas on how to add an event emitter for the active class functionality in this case, please share your thoughts!

Answer â„–1

While the logic may be a bit unclear, the solution can be summed up in a few simple steps:

1- Maintain a variable that stores the current user (retrieved from the route), which is likely already available as this.receiver.

2- Utilize [class] to dynamically add the class active to each element.

<li class="content" [ngClass]="{'active': chat.receiverId.username === this.receiver}">

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

Storing numerous string labels and arrays in a TypeScript associative array

I am currently developing a mobile app using Ionic 4 where I need to store various labels and arrays in an associative array. However, I am encountering challenges when it comes to initializing the array, adding new items to it, and updating existing ones ...

SonarQube alerting you to "Eliminate this unnecessary casting"

Can someone help me understand why SonarQube is flagging this error and suggest a resolution? The unnecessary cast should be removed. Promise.all([ this.customerViewCmr4tProvider.getData(activeNumber), this.customerBillManagementProvider.getData(ind ...

Location of the bundled Webpack index.html file while running locally on a Nativescript WebView

I am currently working on a hybrid app project that involves both NativeScript and Angular. To integrate the two, I have set up a WebView and consolidated all my Angular project files into a folder within my NativeScript project. As part of this setup, I ...

Refining Angular service coding techniques

In my application, I have implemented this specific format to interact with an API and retrieve data from it. Below is the code snippet taken from one of the service.ts files: getCheckoutDetails(): Observable<UserDetail> { let query = `7668`; ...

Why do I keep being told that the property doesn't exist?

I have the following code in one file: function test<T extends {}>(arg:T):any { return arg.name } In another file, I have this code: interface IItem { name: string } console.log(test<IItem>({name:'3'})) When I try to access ...

Angular2 - Easily update form fields with just a click

I have a form that retrieves data from a service and presents it in the following format: @Component({ selector: 'profile', template: `<h1>Profile Page</h1> <form [ngFormModel]="myForm" (ngSubmit)="onSubmit()" #f="ngFor ...

Creating a WebExtension using Angular and TypeScript

I am currently working on creating a WebExtension using Angular and Ionic. The extension successfully loads the application and enables communication between Content Script and Background Script. However, I am facing an issue where the TypeScript code is n ...

Using React and TypeScript, open the initial tab from a mapped array with an accordion component

{accordion.map(accordionItem => ( <AccordionItem key={accordionItem.title} text={accordionItem.text} title={accordionItem.title} ></AccordionItem> ...

Angular 4: Issue with sending POST data via HTTP requests

I am currently working on developing a Web Application and I am facing an issue with testing the HTTP functions. Here is an example of my code snippet: import { Injectable } from '@angular/core'; import { Headers, RequestOptions, Http } from & ...

The implementation of Symbol.species in the Node.js Buffer class to generate a RapidBuffer seems illogical and confusing

While exploring the source code of ws, a popular WebSocket implementation for Node.js, I stumbled upon this specific piece of code: const FastBuffer = Buffer[Symbol.species]; But what exactly is this FastBuffer used for? Surprisingly, it seems that they a ...

What is the proper way to import the Database class from BetterSqlite3 in a TypeScript project?

I am currently working on code that utilizes better-sqlite3 and my goal is to convert it to typescript. The original javascript code includes the following relevant sections: import Database from "better-sqlite3"; /** * @param {string} filenam ...

Issue with tooltip not displaying attribute data-bs-title in Angular 17 with Bootstrap version 5.3

I am currently developing an Angular version 17 application and implementing a dynamic tooltip feature. The tooltip content will be supplied through a @Input() into the component. While a static tooltip works fine, I am facing an issue when trying to make ...

Include a Polyfill in craco, implementing a fallback for 'resolve.fallback'

I'm encountering an issue: If you need to use a polyfill, follow these steps: - include a fallback 'resolve.fallback: { "path": require.resolve("path-browserify") }' - install 'path-browserify' If you prefer n ...

Tips for testing an Angular Service that utilizes AngularFireDatabase by utilizing Jasmine Spy/Mock

I am currently testing my data service, and while I can successfully test it using real services like AngularFireDatabase, I am facing issues with getting the mocked version to work for testing purposes. The DataStorage class in use is designed to combine ...

Incorporating dynamic dependency injection in Angular 2 through the use of @Input()

Let's consider a scenario where an Angular 2 component-directive needs to dynamically determine the injected dependency it uses based on an @Input(). For example, I would like to be able to specify <trendy-directive use="'serviceA'"> ...

Angular2: Retrieve and process a JSON array from an API

I'm currently facing an issue with my service while attempting to fetch a json array from an api and showcase it on the page. I believe there might be an error in my code, but I can't pinpoint exactly where. getAuctions(): Promise<Auction[ ...

The use of dates (YYYY-MM-DD) as identifiers in HTML/Angular 2 is causing issues

I have successfully created a calendar using html in Angular 2, and now I am looking to incorporate events into it. These events should be able to color specific days, add a dot inside the cell, or something similar. I have a json file that contains the ev ...

What is the best way to preserve the information from the previous page when returning to it after visiting a new one?

My current setup involves 4 components, each with a 'next' button (except the last) and a 'previous' button (except the first). When I click on the Next button, it takes me to the following component. However, when I navigate back to th ...

The combination of Object.keys() and the find function

Having trouble figuring out why I'm getting an error when attempting to use ES6 .find on the following data in order to retrieve the record with id number 3. { {id:10,title:'Dairy & Eggs'} {id:7,title:'Laundry & Household'} {id ...

I'm working with Angular 12, Bootstrap 5, and ngPrime, and I'm looking to overlap a p-dialog with another element in my project

Is there a way in Angular 12 with Bootstrap 5 using ngPrime to overlap a p-dialog over any other element without using z-index and CSS, solely relying on PrimeNG? I have tried using z-index with: .my-class{ z-index: 2147483647 !important; } However, ...