Angular6 Div вокруг массивов от того же пользователя

У меня есть данные для мессенджера:

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
    }
];

Я вставляю это в область сообщений Messenger: <mark6-messenger-message [messages] = "messages" [avatarMe] = "false" [avatarOthers] = "true"></mark6-messenger-message>

Внешний вид <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>

Результат в браузере: (я меняю сообщения в коде, не беспокойтесь об этом)

Angular6 Div вокруг массивов от того же пользователя

Проблема в том, что когда один человек спамит несколько сообщений, я хочу удалить аватары из последнего сообщения. и я хочу удалить пробел между двумя сообщениями от одного и того же человека. Как это:

Angular6 Div вокруг массивов от того же пользователя

но у двух разных людей это должно выглядеть так:

Angular6 Div вокруг массивов от того же пользователя

мне очень сложно понять, как это решить. и все, о чем я прошу, говорят мне, что нет способа не потерять много производительности. Мне нужно сделать это таким образом, чтобы все это обрабатывалось автоматически только с данными direction и user (что означает имя пользователя) или, может быть, я добавляю поле для user_id, тогда у меня есть 2 обязательных поля (direction и user_id). сделайте это настолько простым и автоматическим, насколько это возможно, потому что это для библиотеки с открытым исходным кодом, которую, я надеюсь, в будущем многие люди будут использовать. Я не хочу заставлять их делать много логических вещей. :)

Вот репозиторий, если вам нужно проверить другие файлы или дополнительную информацию: https://github.com/DevMonkeysDE/ngx-mark6/tree/master/projects/mark6-lib/src/lib/messenger

Лучше всего, если бы каждый пакет сообщений получал контейнер div, тогда я мог бы просто работать с css: first-child и css: last-child. но тогда он должен «на лету» преобразовывать каждое сообщение целиком HTML DOM, это, возможно, действительно очень сильно нарушает производительность.

Я бы сделал это с помощью условных элементов CSS, таких как: убрать отступ между сообщениями и добавить div 'маржа' после каждого сообщения и добавить к нему условный класс, который проверяет, совпадает ли пользователь из этого индекса и прошлого индекса , затем выберите малый класс полей, если нет, выберите большой класс заполнения, и то же самое с аватаром, если это тот же пользователь, не показывайте его.

dAxx_ 16.07.2018 12:21

Можете ли вы сделать мне пример условий? Я так плохо разбираюсь в этом ... И вы не думаете, что лучше обернуть div вокруг сообщений от одного и того же направления и пользователя? тогда мне нужно только одно логическое условие, а все остальное можно сделать с помощью простого css (first и last-child). Но я никогда раньше не видел, чтобы кто-то обертывал div в реальном времени вокруг элементов. (возможно, действительно к нарушению производительности)

Budi 16.07.2018 12:25

Вы не можете сделать это только в css, потому что nth: child css не принимает условий, поэтому вы не можете сопоставить его с пользователями в реальном времени. Если никто не ответит, пока я не вернусь домой, я попытаюсь предоставить вам простой пример.

dAxx_ 16.07.2018 12:29

Если у вас есть хорошая модель со структурой, соответствующей тому представлению, которое вы хотите сгенерировать, все будет проще. В вашем описании указано, что вы не хотите отображать список сообщений. Вы хотите отобразить список групп сообщений, где каждая группа имеет от 1 до N последующих сообщений, все отправленные одним пользователем. Итак, используйте TypeScript для создания массива групп сообщений из ваших сообщений (путем обнаружения последующих сообщений, отправленных одним и тем же пользователем). Затем вы можете перебирать каждую группу и отображать каждую группу с одним аватаром, а внутри этой группы перебирать сообщения группы.

JB Nizet 16.07.2018 12:31

@JBNizet, судя по его теме и изображениям, похоже, что он реализует какой-то чат? Поэтому, если это чат в реальном времени, запуск этой группировки каждый раз, когда приходит новое сообщение, потребует много ресурсов. Лучше не полагаться на контроллер, так как это касается только представления.

dAxx_ 16.07.2018 12:34

Да, я хочу создать чат в реальном времени, который можно использовать в больших чатах более 200 человек одновременно. Но самое главное здесь то, что я создаю репозиторий, открытый для всех, поэтому он должен быть максимально настраиваемым и приносить разработчикам лучший опыт.

Budi 16.07.2018 12:35

@dAxx_ Итак, вы хотите проверять каждый раз, когда приходит сообщение, принадлежит ли оно последней группе или новой. Это очень далеко от того, чтобы требовать много ресурсов. Это займет всего несколько наносекунд.

JB Nizet 16.07.2018 13:04
Как сделать HTTP-запрос в Javascript?
Как сделать HTTP-запрос в Javascript?
В JavaScript вы можете сделать HTTP-запрос, используя объект XMLHttpRequest или более новый API fetch. Вот пример для обоих методов:
3
7
120
2
Перейти к ответу Данный вопрос помечен как решенный

Ответы 2

Одно из возможных решений - немного усложнить условие *ngIf.

Измените директиву *ngFor, чтобы отслеживать индекс сообщений следующим образом: *ngFor = "let message of messages; let i=index"

Тогда вы можете попробовать что-то вроде *ngIf = "(avatarMe && message.direction === 'me' && (messages[i+1].direction !== 'me') || (avatarOthers && message.direction === 'others' && (messages[i+1].user_id !== message.user_id))", чтобы показать аватар только если следующее сообщение не от того же пользователя.

Однако это может не сработать для последнего сообщения, так как messages[i+1] не найдется.

Другим решением было бы изменить, если возможно, структуру данных массива messages, чтобы включить timestamp (поскольку это приложение для обмена сообщениями, имеет смысл отслеживать время). Затем мы можем упорядочить по метке времени и показать аватар с сообщением с максимальной меткой времени.

Это имеет смысл с меткой времени, но как я могу изменить другие вещи, такие как поля и радиус границы? для меня это было бы самым простым решением, когда в группах сообщений есть div. а вы думаете, это из-за производительности не вариант, да?

Budi 16.07.2018 12:58

Поправьте меня, если я ошибаюсь, div с class = "msg-content" отвечает за отображение серой закругленной рамки, содержащей сообщение. Если это так, почему бы не использовать [ngClass] = "{msg-content: <<condition for NOT showing avatar>>, msg-content2: <<condition for showing avatar>>}", где msg-content2 - это другой класс с другими границами?

Sourangshu Sengupta 16.07.2018 13:14

Аватар находится в div с классом msg-avatar на том же уровне DOM, что и div с msg-content. Если нет возможности обернуть с помощью message group div, мне нужны условия для многих элементов. на msg-avatar, чтобы скрыть аватар, если не последнее сообщение, на msg-content, чтобы изменить границы. (сначала удалите нижний радиус границы, средние блоки div удалите весь радиус границы, а последнее сообщение удалите радиус верхней границы: ..) и в div с классом msg, чтобы изменить поле вокруг сообщений ... Это большая логика ... div, который работает поскольку группа сообщений сделает все намного проще, потому что я могу делать все с помощью css.

Budi 16.07.2018 13:29

Это компромисс: либо вы изменяете структуру данных, чтобы упростить шаблон, либо сохраняете структуру данных простой и усложняете шаблон с большим количеством логики. Я бы посоветовал воспользоваться предложением @JBNizet для группировки сообщений, исходящих от одного и того же пользователя. Однако я не могу понять, как можно различить сообщения пользователя A (пронумерованные, например: 1,2,3), где сообщение другого пользователя B было перед сообщением A № 3. В этом случае вам понадобится временная метка, чтобы сгруппировать [1,2] и [3] отдельно. Я не уверен, есть ли другой способ напрямую обернуть группу сообщений div.

Sourangshu Sengupta 16.07.2018 13:50

У вас может быть один div сверху и один снизу, который является разделителем с фиксированным полем, если это тот же пользователь, который находится выше, вы не применяете стиль, кроме того, что вы делаете, то же самое для низа, просто проверьте следующее сообщение. Я предлагаю создать новый компонент или директиву для каждого сообщения, которая будет обрабатывать всю эту логику с гораздо большим упорядочением и простотой.

dAxx_ 16.07.2018 15:00
Ответ принят как подходящий

То, что вам нужно, может быть достигнуто путем простого сравнения текущего + следующего направления сообщения в цикле ngFor и посмотреть, отличаются ли они, а затем добавить «последний» или как вы хотите назвать его class. Потому что вам, по сути, нужно сообщение «последний в этом направлении».

<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>
  1. [class] = "message.direction"

Это даст нам направление сообщения в виде класса me или others в вашем случае, довольно просто.

Теперь позвольте мне объяснить «последнюю» часть класса:

  1. *ngFor = "let message of messages; let isLast = last; let i = index;"

Директива ngFor предоставляет некоторые локальные переменные, такие как last, first,, index, поэтому мы можем воспользоваться этим и назначить нашу переменную isLast локальной переменной last из цикла ngFor, в то же время наша переменная i будет содержать текущий цикл index ngFoor. Документы для ngFor

  1. [ngClass] = "{last: messages[i+1] && messages[i+1].direction !== message.direction || isLast, message:true}

Мы используем директиву ngClass для применения класса last, если наше условие истинно, в этом случае условие просто сравнивает, если следующее сообщение в цикле (messages[i+1].direction) не совпадает с текущим сообщением в цикле message.direction. Единственная проблема с этим состоит в том, что самое последнее сообщение в цикле messages[i+1] будет undefined, и именно здесь проходит проверка или || isLast, так как это всегда будет верно для последнего сообщения в цикле. Документы для ngClass

В стороне, messages[i+1] && messages[i+1].direction, вам нужно сделать это таким образом, чтобы вы закоротили направление проверки состояния, только если messages[i+1] определен, иначе ваш код выйдет из строя на последнем элементе. Вы можете узнать больше об этом в документации Javascript.

Для отображения / скрытия аватаров это просто css, если у вас есть класс last.

Вот рабочий пример: https://stackblitz.com/edit/angular-gwqqee

Надеюсь, это поможет.

Обновлено: Я забыл, что у вас более двух пользователей, но применяется та же концепция, просто проверьте user_id вместо направления, см. Обновленный пример.

[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
  }"

Большое спасибо за это подробное объяснение и отличный stackbliz! Это очень помогло мне достичь именно того, чего я хочу достичь.

Budi 16.07.2018 20:07

Другие вопросы по теме