How to identify the first and last element of a chain in an array of objects

Guys, I was adding up when I was trying to set the correct chat message classes.
I have a component with the following pattern:

...
<ul>
    <li *ngFor="#mes of chatMessages; #index = index;">
        user {{mes.user.id}}:
        <span 
                [class.header-recent]="isRecentHeader(chatMessages, mes, index)"
                [class.is-recent]="isRecent(chatMessages, mes, index)"
                [class.last-recent]="isLastRecent(chatMessages, mes, index)">
            {{mes.text}}
        </span>
    </li>
</ul>
...

      

I'm trying to apply three different classes to each post .header-recent

, which should be applied to the first post in a thread from one user. Class .is-recent

for messages that were sent by the user in less than 60 seconds. And .is-last-recent

for the last message in the last message thread. Like on a picture.

enter image description here

The message array looks like

[...
{
    "type": "message",
    "id": "19372",
    "text": "43\n",
    "created": 1495026352,
        "user": {
            "id": 2
        }
  },
  {
    "type": "message",
    "id": "19373",
    "text": "safaf",
    "created": 1495026357,
        "user": {
            "id": 1
        }
  },
  {
    "type": "system",
    "id": "19374",
    "text": "SYSTEM MESSAGE",
    "created": 1495027801,
        "user":{
            "id": "SYSTEM"
        }
   }
...]

      

I need to write 3 functions, each defining one class for messages. I currently have two of them.

    isRecentHeader(mess, mes, index){
        let lastMessage;
        if( index == mess.length-1){
            lastMessage = mess[index];
        } else if( index => 0 && index != mess.length-1) {
            lastMessage = mess[index-1];
        }
        return lastMessage && !this.calcRecent(mes, lastMessage) && lastMessage.type != 'system';

    }

    isRecent(mess, mes, index){
        let lastMes = mess[index-1];
        return this.calcRecent(mes, lastMes);
    }

    isLastRecent(mess, mes, index){
        // confuse here
    }

    calcRecent(message, lastMessage){
        const messageDate = moment(message.created * 1000);
        const lastMessageDate = lastMessage ? moment(lastMessage.created *1000) : null;

        return lastMessage && (lastMessage.type != 'system')
        && (message.type != 'system')
        && (lastMessage.user.id == message.user.id)
        && ((messageDate.valueOf() - lastMessageDate.valueOf()) / 1000 <= 60)
    }

      

So the function isRecent()

works fine, isRecentHeader()

works pretty much the same as we will see, but applies the class .header-recent

to a single message. And the function doesn't work isLastRecent()

. So my question is how to change the code in isRecentHeader()

so that it doesn't apply the class to a single post and what should I do in the function isLastRecent()

. Here's the codepen of my example.

PS If my question seems unclear or confusing to you, please tell me and I will try to change or clarify everything.

+3


source to share


4 answers


Thanks to my team leader at work, he gave me the answer. He used setter and getter to handle incoming messages in component.

...
private _messages: any[];
recents = [];

@Input()
set messages(messages: any[]) {
    this._messages = messages;

    this.recents = [];

    for(let i = 0; i < messages.length; i++) {
        const message = messages[i];
        const nextMessage = i == messages.length - 1 ? null : messages[i + 1];
        const prevMessage = i == 0 ? null : messages[i - 1];

        const isRecentWithNext = this.isRecentMessages(message, nextMessage);
        const isRecentWithPrevious = this.isRecentMessages(message, prevMessage);

        if(!isRecentWithPrevious && isRecentWithNext) {
            this.recents[i] = 'first-recent'
        } else if(isRecentWithPrevious && isRecentWithNext) {
            this.recents[i] = 'recent';
        } else if(isRecentWithPrevious && !isRecentWithNext) {
            this.recents[i] = 'last-recent';
        }
    }
}

get messages() {
    return this._messages;
}

isRecentMessage(index) {
    return this.recents[index] == 'recent' || this.recents[index] == 'last-recent';
}

isRecentMessages(message, message2?) {
    return message && message2
    && message.type!='system' && message2.type != 'system'
    && (message.user.id == message2.user.id)
    && (Math.abs((message.created - message2.created)) <= 60)
}

      



And in the template for installing classes, now the following code

<li *ngFor="#mes of messages; #index = index;">
    <span [class]="recents[index]>
        {{mes.text}}
    </span>
</li>

      

0


source


You can achieve this easily using basic conditions as shown below

<ul>
    <li *ngFor="#mes of chatMessages; #index = index;">
    user {{mes.user.id}}:
    <span 
            [class.header-recent]="index === 0"
            [class.is-recent]="index > 0 && index < (chatMessages.length + 1)"
            [class.last-recent]="(chatMessages.length) === index+1">
        {{mes.text}}
    </span>
</li>
</ul>

      



Updated pen

+1


source


You can use the first and last boolen specified in * ngFor as shown below and use [ngClass] to set the class based on the value:

<li *ngFor="#mes of chatMessages; let first = first; let last = last" [ngClass]="{ first: first, last: last }">

+1


source


Regarding your problems:

Single message error:

  • In order not to add the class to one post, you need to make sure that the next post is from the same user id.

Last message:

  1. The last message is the same logic of the first message, you just need to check that the next message is from a different user ID.

I just made your code work, although some refactoring can be done.

isRecentHeader(mess, mes, index){
    let lastMessage;
    let nextMessage;
    if( index == mess.length-1){
        lastMessage = mess[index];
    } else if( index >= 0 && index != mess.length-1) {
        lastMessage = mess[index-1];
        if (index < mess.length-1){
            nextMessage = mess[index+1];
        }
    }

    return lastMessage && nextMessage && mes.user.id==nextMessage.user.id && !this.calcRecent(mes, lastMessage) && lastMessage.type != 'system';
}

      

Updated CodePen

+1


source







All Articles