У меня есть данные для мессенджера:
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>
Результат в браузере: (я меняю сообщения в коде, не беспокойтесь об этом)
Проблема в том, что когда один человек спамит несколько сообщений, я хочу удалить аватары из последнего сообщения. и я хочу удалить пробел между двумя сообщениями от одного и того же человека. Как это:
но у двух разных людей это должно выглядеть так:
мне очень сложно понять, как это решить. и все, о чем я прошу, говорят мне, что нет способа не потерять много производительности. Мне нужно сделать это таким образом, чтобы все это обрабатывалось автоматически только с данными 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, это, возможно, действительно очень сильно нарушает производительность.
Можете ли вы сделать мне пример условий? Я так плохо разбираюсь в этом ... И вы не думаете, что лучше обернуть div вокруг сообщений от одного и того же направления и пользователя? тогда мне нужно только одно логическое условие, а все остальное можно сделать с помощью простого css (first и last-child). Но я никогда раньше не видел, чтобы кто-то обертывал div в реальном времени вокруг элементов. (возможно, действительно к нарушению производительности)
Вы не можете сделать это только в css, потому что nth: child css не принимает условий, поэтому вы не можете сопоставить его с пользователями в реальном времени. Если никто не ответит, пока я не вернусь домой, я попытаюсь предоставить вам простой пример.
Если у вас есть хорошая модель со структурой, соответствующей тому представлению, которое вы хотите сгенерировать, все будет проще. В вашем описании указано, что вы не хотите отображать список сообщений. Вы хотите отобразить список групп сообщений, где каждая группа имеет от 1 до N последующих сообщений, все отправленные одним пользователем. Итак, используйте TypeScript для создания массива групп сообщений из ваших сообщений (путем обнаружения последующих сообщений, отправленных одним и тем же пользователем). Затем вы можете перебирать каждую группу и отображать каждую группу с одним аватаром, а внутри этой группы перебирать сообщения группы.
@JBNizet, судя по его теме и изображениям, похоже, что он реализует какой-то чат? Поэтому, если это чат в реальном времени, запуск этой группировки каждый раз, когда приходит новое сообщение, потребует много ресурсов. Лучше не полагаться на контроллер, так как это касается только представления.
Да, я хочу создать чат в реальном времени, который можно использовать в больших чатах более 200 человек одновременно. Но самое главное здесь то, что я создаю репозиторий, открытый для всех, поэтому он должен быть максимально настраиваемым и приносить разработчикам лучший опыт.
@dAxx_ Итак, вы хотите проверять каждый раз, когда приходит сообщение, принадлежит ли оно последней группе или новой. Это очень далеко от того, чтобы требовать много ресурсов. Это займет всего несколько наносекунд.

Одно из возможных решений - немного усложнить условие *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. а вы думаете, это из-за производительности не вариант, да?
Поправьте меня, если я ошибаюсь, div с class = "msg-content" отвечает за отображение серой закругленной рамки, содержащей сообщение. Если это так, почему бы не использовать [ngClass] = "{msg-content: <<condition for NOT showing avatar>>, msg-content2: <<condition for showing avatar>>}", где msg-content2 - это другой класс с другими границами?
Аватар находится в div с классом msg-avatar на том же уровне DOM, что и div с msg-content. Если нет возможности обернуть с помощью message group div, мне нужны условия для многих элементов. на msg-avatar, чтобы скрыть аватар, если не последнее сообщение, на msg-content, чтобы изменить границы. (сначала удалите нижний радиус границы, средние блоки div удалите весь радиус границы, а последнее сообщение удалите радиус верхней границы: ..) и в div с классом msg, чтобы изменить поле вокруг сообщений ... Это большая логика ... div, который работает поскольку группа сообщений сделает все намного проще, потому что я могу делать все с помощью css.
Это компромисс: либо вы изменяете структуру данных, чтобы упростить шаблон, либо сохраняете структуру данных простой и усложняете шаблон с большим количеством логики. Я бы посоветовал воспользоваться предложением @JBNizet для группировки сообщений, исходящих от одного и того же пользователя. Однако я не могу понять, как можно различить сообщения пользователя A (пронумерованные, например: 1,2,3), где сообщение другого пользователя B было перед сообщением A № 3. В этом случае вам понадобится временная метка, чтобы сгруппировать [1,2] и [3] отдельно. Я не уверен, есть ли другой способ напрямую обернуть группу сообщений div.
У вас может быть один div сверху и один снизу, который является разделителем с фиксированным полем, если это тот же пользователь, который находится выше, вы не применяете стиль, кроме того, что вы делаете, то же самое для низа, просто проверьте следующее сообщение. Я предлагаю создать новый компонент или директиву для каждого сообщения, которая будет обрабатывать всю эту логику с гораздо большим упорядочением и простотой.
То, что вам нужно, может быть достигнуто путем простого сравнения текущего + следующего направления сообщения в цикле 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>
[class] = "message.direction"Это даст нам направление сообщения в виде класса me или others в вашем случае, довольно просто.
Теперь позвольте мне объяснить «последнюю» часть класса:
*ngFor = "let message of messages; let isLast = last; let i = index;"Директива ngFor предоставляет некоторые локальные переменные, такие как last, first,, index, поэтому мы можем воспользоваться этим и назначить нашу переменную isLast локальной переменной last из цикла ngFor, в то же время наша переменная i будет содержать текущий цикл index ngFoor. Документы для ngFor
[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! Это очень помогло мне достичь именно того, чего я хочу достичь.
Я бы сделал это с помощью условных элементов CSS, таких как: убрать отступ между сообщениями и добавить div 'маржа' после каждого сообщения и добавить к нему условный класс, который проверяет, совпадает ли пользователь из этого индекса и прошлого индекса , затем выберите малый класс полей, если нет, выберите большой класс заполнения, и то же самое с аватаром, если это тот же пользователь, не показывайте его.