Search code examples
arraysangularchatngforstring-interpolation

Angular ngFor - get a property in ts


I need some help here please...

Im making a chat app with angular and material, and i need to show the user nickname only one time before the next user messages enter in action (like whatsapp or similar), something like this:

user1: message 1 message 2 message 3 user2: message 1 message 2 user1: message 4 etc

This is my component.ts:

  messages: any[] = [
    {
      nickname: 'user1',
      msg: 'hola que tal 1',
      img: "https://images-na.ssl-images-amazon.com/images/I/81PohdE46lL.jpg"
    },
    {
      nickname: 'user1',
      msg: 'hola que tal 2',
      img: "https://images-na.ssl-images-amazon.com/images/I/81PohdE46lL.jpg"
    },
    {
      nickname: 'user1',
      msg: 'hola que tal 3',
      img: "https://images-na.ssl-images-amazon.com/images/I/81PohdE46lL.jpg"
    },
    {
      nickname: 'user2',
      msg: 'hola que tal 4',
      img: "https://images-na.ssl-images-amazon.com/images/I/81PohdE46lL.jpg"
    },
    {
      nickname: 'user2',
      msg: 'hola que tal 4',
      img: "https://images-na.ssl-images-amazon.com/images/I/81PohdE46lL.jpg"
    },
  ];

username: string = "test";
temp_user: string = "";

changeTemp(nickname: string): void {
    this.temp_user = nickname;
  }

And this is my HTML with the (ugly) solution im achieved:

            <mat-card-content>
                <div class="chat-window" #chat_window>
                    <mat-list role="list">
                        <div *ngFor="let msg of messages">
                            <div *ngIf="temp_user !== msg.nickname" class="avatar"
                                [ngClass]="{ 'right-avatar': msg.nickname == username}">
                                <!-- <img class="avatar-img" [src]="msg.img" alt="..."> -->
                                {{ msg.nickname }}
                                {{ changeTemp(msg.nickname) }}
                            </div>
                            <mat-list-item class="chat-message" role="listitem"
                                [ngClass]="{ 'right-message': msg.nickname == username}">
                                {{ msg.msg }}
                            </mat-list-item>
                        </div>
                    </mat-list>
                </div>
            </mat-card-content>

This is the result: result I called the changeTemp() function with string interpolation to save temp user and check if its repeated before next user msg with an ngIf but this (call a function in this situation) not sounds like a good practice to me...

There is a clean way to solve this? get temp user in some way or an alternative? i have tried with a directive that emits an event, and a trackBy solution, but not worked fine in this situation.

Thanks a lot.


Solution

  • You can group your list in a more convenient order before you throw it to HTML to do its job.

    Assume that your array is,

    var arr = [
      {tag: 'one', content: 'A'},
      {tag: 'one', content: 'B'},
      {tag: 'two', content: 'C'},
      {tag: 'two', content: 'D'},
      {tag: 'three', content: 'E'},
      {tag: 'three', content: 'F'}
    ];
    

    Convenient order would like,

    {
          one: [
            {tag: 'one', content: 'A'},
            {tag: 'one', content: 'B'}
          ],
          two: [
            {tag: 'two', content: 'C'},
            {tag: 'two', content: 'D'}
          ],
          three: [
            {tag: 'three', content: 'E'},
            {tag: 'three', content: 'F'}
          ]
        }
    

    you can group the array by using "group-array" package or manually. If you wish to use "group-array" package, bellow I have mentioned the link and a example copied from original documentation to group the array. Link : https://www.npmjs.com/package/group-array#examples

    //npm i group-array
    // group by the `tag` property
       groupArray(arr, 'tag');
    

    In your HTML

    <div *ngFor="let item of yourList | keyvalue">
       <div >
           Message From - {{item.key}}
       </div>
       <div>
           <p  *ngFor="let message of item.value">
                {{message.content}}
           </p>
       </div>
     </div>
    

    Output :

    Message From - one
    A
    B
    
    Message From - three
    E
    F
    
    Message From - two
    C
    D