I have some Data for a Messenger:
public messages = [
{
direction: 'me', // Required
user_id: 554, // Required
user: 'Sven', // Optional
avatar: 'urltoavatar', // Optional
message: 'Hi du alter babbsack!' // Optional
},
{
direction: 'others', // Required
user_id: 8774, // Required
user: 'Hannes', // Optional
avatar: 'urltoavatar', // Optional
message: 'Hey whats going on?' // Optional
},
{
direction: 'others', // Required
user_id: 8774, // Required
user: 'Hannes', // Optional
avatar: 'urltoavatar', // Optional
message: 'No Idea bro! This ' // Optional
}
];
I insert this inside the Messenger Message Area: <mark6-messenger-message [messages]="messages" [avatarMe]="false" [avatarOthers]="true"></mark6-messenger-message>
The View of the <mark6-messenger-message> :
<ng-container *ngFor="let message of messages">
<div class="msg" [ngClass]="message.direction">
<div class="msg-avatar" *ngIf="(avatarMe && message.direction === 'me') || (avatarOthers && message.direction === 'others')">
<img [src]="message.avatar" [alt]="message.user">
</div>
<div class="msg-content">
<div class="msg-message">{{message.message}}</div>
</div>
</div>
</ng-container>
The Result is this in Browser: (I change the messages in Code, dont worry about this)

The problem is now that when 1 Person Spam some messages, i want to remove the avatars up from the latest message. and i want remove the space between the 2 messages from same person. Like this:

but of 2 different people write it must look like this:

its verry hard for me to figure out how to solve this. and all i ask tell me that there is no way without lost alot of performance. I need to do this in a way, that handle all this stuff automaticly only with the data direction and user (Which means username) or maybe i add a field for user_id then i have 2 required fields ( direction and user_id ) The goal is to make this so easy and automaticly as possible, because this is for the open source lib that i hope in future alot people will use. i dont want to force them to do to much logic stuff. :)
Here is the repository if you need to check other files or more informations: https://github.com/DevMonkeysDE/ngx-mark6/tree/master/projects/mark6-lib/src/lib/messenger
The best thing would be when every pack of messages each person get a div container, then i can work simply with css:first-child and css:last-child. but then it must transform on the fly every message the whole HTML DOM, this is maybe really very very Performance breaking.
One possible solution would be to make the *ngIf condition a little more complicated.
Modify the *ngFor directive to keep track of the index of messages like so: *ngFor="let message of messages; let i=index"
Then you could try something like *ngIf="(avatarMe && message.direction === 'me' && (messages[i+1].direction !== 'me') || (avatarOthers && message.direction === 'others' && (messages[i+1].user_id !== message.user_id))", to show the avatar only if the next message is not from the same user.
However, it might not work for the last message as it won't find messages[i+1].
Another solution would be to modify, if possible, the messages array's data structure to include the timestamp (since its a messaging app, it makes more sense to keep track of time). Then we could order by timestamp and show the avatar with the message with greatest timestamp.
What you are after can be accomplished by simply comparing current + next message direction in the ngFor loop and see if they are different then add a "last" or whatever you want to name it class. Because you essentially want the "last of this direction" message.
<div [class]="message.direction" [ngClass]="{last: messages[i+1] && messages[i+1].direction !== message.direction || isLast, message:true}"
*ngFor="let message of messages; let isLast = last; let i = index;">
{{message.message}}
</div>
[class]="message.direction"This will give us the direction of the message as a class me or others in your case, pretty straight forward.
Now let me explain the "last" class part:
*ngFor="let message of messages; let isLast = last; let i = index;"The ngFor directive exposes some local variables like last, first,, index, so we can take advantage of that and assign our isLast variable to last local variable from ngFor loop, at same time our i variable will hold the current index ngFoor loop. Docs for ngFor
[ngClass]="{last: messages[i+1] && messages[i+1].direction !== message.direction || isLast, message:true}We use the ngClass directive to apply last class if our condition is true, in this case condition is just comparing if the next message in the loop (messages[i+1].direction) is not the same as current message in the loop message.direction. The only problem with this is the very last message in the loop messages[i+1] will be undefined, and that's where the or || isLast check comes thru, as this will always be true for last message in the loop. Docs for ngClass
As an aside, messages[i+1] && messages[i+1].direction you need to do it this way so you short circuit your condition check direction only if messages[i+1] is defined, otherwise your code will crash on the last item. You can read more about that in the Javascript docs
For showing/hiding avatars, that's just simple css once you have the last class in there.
Here's a working example: https://stackblitz.com/edit/angular-gwqqee
Hope that helps.
EDIT: I forgot you have more than two users, but same concept applies, just check user_id instead of direction, see updated example.
[ngClass]="{
'message': true,
'last-from-direction': messages[i+1] && messages[i+1].direction !== message.direction || isLast,
'last-from-user': messages[i+1] && messages[i+1].user_id !== message.user_id || isLast
}"
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With