Messages Component
Messages Vue component represents Messages component.
Messages Components
There are following components included:
f7-messages- main Messages containerf7-message- single message elementf7-messages-title- single messages title element
Messages Properties
| Prop | Type | Default | Description | 
|---|---|---|---|
| <f7-messages> properties | |||
| init | boolean | true | Initializes Messages component | 
| new-messages-first | boolean | false | Enable if you want to use new messages on top, instead of having them on bottom | 
| scroll-messages | boolean | true | Enable/disable messages autoscrolling when adding new message | 
| scroll-messages-on-edge | boolean | true | If enabled then messages autoscrolling will happen only when user is on top/bottom of the messages view | 
| typing | boolean | false | Allows to display/toggle typing message indicator | 
| <f7-message> properties | |||
| type | string | sent | Message type: sent (default) or received | 
| text | string | Message text | |
| avatar | string | Message user's avatar URL | |
| name | string | Message user's name | |
| image | string | Message image URL | |
| header | string | Message header | |
| footer | string | Message footer | |
| text-header | string | Message text header | |
| text-footer | string | Message text footer | |
| first | boolean | false | Defines that the message is first in the conversation | 
| last | boolean | false | Defines that the message is last in the conversation | 
| tail | boolean | false | Defines that the message has visual "tail". Usually last message in conversation | 
| same-name | boolean | false | Defines that this message sender name is the same as on previous message | 
| same-header | boolean | false | Defines that this message header text is the same as on previous message | 
| same-footer | boolean | false | Defines that this message footer text is the same as on previous message | 
| same-avatar | boolean | false | Defines that this message user's avatar URL is the same as on previous message | 
Messages Events
| Event | Description | 
|---|---|
| <f7-message> events | |
| click | Event will be triggered when user clicks on message bubble | 
| click:name | Event will be triggered when user clicks on message user's name | 
| click:text | Event will be triggered when user clicks on message text | 
| click:avatar | Event will be triggered when user clicks on message user's avatar | 
| click:header | Event will be triggered when user clicks on message header | 
| click:footer | Event will be triggered when user clicks on message footer | 
| click:bubble | Event will be triggered when user clicks on message bubble | 
Messages Slots
Single message Vue component (<f7-message>) has additional slots for custom elements:
default- element will be inserted as a child of<div class="message-bubble">element in the endstart- element will be inserted in the beginning and direct child of main message element<div class="message">end- element will be inserted in the end and direct child of main message element<div class="message">content-start- element will be inserted in the beginning and direct child of the<div class="message-content">elementcontent-end- element will be inserted in the end and direct child of the<div class="message-content">elementbubble-start- element will be inserted in the beginning and direct child of the<div class="message-bubble">elementbubble-end- element will be inserted in the end and direct child of the<div class="message-bubble">element. Same asdefaultslot
The following slots can be used inside of single message instead of same props if you need to pass there more complext layout:
header- element will be inserted in message headerfooter- element will be inserted in message footertext- element will be inserted in message textname- element will be inserted in message nameimage- element will be inserted in message image (supposed to be an<img>element)text-header- element will be inserted in message text headertext-footer- element will be inserted in message text footer
<f7-message
  type="sent"
  text="Hello World"
  name="John Doe"
  avatar="path/to/image.jpg"
>
  <div slot="start">Start</div>
  <div slot="end">End</div>
  <div slot="content-start">Content Start</div>
  <div slot="content-end">Content End</div>
  <div slot="bubble-start">Bubble Start</div>
  <div slot="bubble-end">Bubble End</div>
</f7-message>
<!-- Renders to: -->
<div class="message message-sent">
  <div>Start</div>
  <div class="message-avatar" style="background-image: url(path/to/image.jpg);"></div>
  <div class="message-content">
    <div>Content Start</div>
    <div class="message-name">John Doe</div>
    <div class="message-bubble">
      <div>Bubble Start</div>
      <div class="message-text">Hello World</div>
      <div>Bubble End</div>
    </div>
    <div>Content End</div>
  </div>
  <div>End</div>
</div>
Examples
Here is how the full example of Messages page where it can be used together with Messagebar:
<template>
<f7-page>
  <f7-navbar title="Messages" />
  <f7-messagebar
    ref="messagebar"
    v-model:value="messageText"
    :placeholder="placeholder"
    :attachments-visible="attachmentsVisible"
    :sheet-visible="sheetVisible"
  >
    <template #inner-start>
      <f7-link
        icon-ios="f7:camera_fill"
        icon-aurora="f7:camera_fill"
        icon-md="material:camera_alt"
        @click="sheetVisible = !sheetVisible"
      />
    </template>
    <template #inner-end>
      <f7-link
        icon-ios="f7:arrow_up_circle_fill"
        icon-aurora="f7:arrow_up_circle_fill"
        icon-md="material:send"
        @click="sendMessage"
      />
    </template>
    <f7-messagebar-attachments>
      <f7-messagebar-attachment
        v-for="(image, index) in attachments"
        :key="index"
        :image="image"
        @attachment:delete="deleteAttachment(image)"
      ></f7-messagebar-attachment>
    </f7-messagebar-attachments>
    <f7-messagebar-sheet>
      <f7-messagebar-sheet-image
        v-for="(image, index) in images"
        :key="index"
        :image="image"
        :checked="attachments.indexOf(image) >= 0"
        @change="handleAttachment"
      ></f7-messagebar-sheet-image>
    </f7-messagebar-sheet>
  </f7-messagebar>
  <f7-messages>
    <f7-messages-title><b>Sunday, Feb 9,</b> 12:58</f7-messages-title>
    <f7-message
      v-for="(message, index) in messagesData"
      :key="index"
      :type="message.type"
      :image="message.image"
      :name="message.name"
      :avatar="message.avatar"
      :first="isFirstMessage(message, index)"
      :last="isLastMessage(message, index)"
      :tail="isTailMessage(message, index)"
    >
      <template #text>
        <!-- eslint-disable-next-line -->
        <span v-if="message.text" v-html="message.text"></span>
      </template>
    </f7-message>
    <f7-message
      v-if="typingMessage"
      type="received"
      :typing="true"
      :first="true"
      :last="true"
      :tail="true"
      :header="`${typingMessage.name} is typing`"
      :avatar="typingMessage.avatar"
    ></f7-message>
  </f7-messages>
</f7-page>
</template>
<script>
import { f7, f7ready } from 'framework7-vue';
import $ from 'dom7';
export default {
  data() {
    return {
      attachments: [],
      sheetVisible: false,
      typingMessage: null,
      messageText: '',
      messagesData: [
        {
          type: 'sent',
          text: 'Hi, Kate',
        },
        {
          type: 'sent',
          text: 'How are you?',
        },
        {
          name: 'Kate',
          type: 'received',
          text: 'Hi, I am good!',
          avatar: 'https://cdn.framework7.io/placeholder/people-100x100-9.jpg',
        },
        {
          name: 'Blue Ninja',
          type: 'received',
          text: 'Hi there, I am also fine, thanks! And how are you?',
          avatar: 'https://cdn.framework7.io/placeholder/people-100x100-7.jpg',
        },
        {
          type: 'sent',
          text: 'Hey, Blue Ninja! Glad to see you ;)',
        },
        {
          type: 'sent',
          text: 'Hey, look, cutest kitten ever!',
        },
        {
          type: 'sent',
          image: 'https://cdn.framework7.io/placeholder/cats-200x260-4.jpg',
        },
        {
          name: 'Kate',
          type: 'received',
          text: 'Nice!',
          avatar: 'https://cdn.framework7.io/placeholder/people-100x100-9.jpg',
        },
        {
          name: 'Kate',
          type: 'received',
          text: 'Like it very much!',
          avatar: 'https://cdn.framework7.io/placeholder/people-100x100-9.jpg',
        },
        {
          name: 'Blue Ninja',
          type: 'received',
          text: 'Awesome!',
          avatar: 'https://cdn.framework7.io/placeholder/people-100x100-7.jpg',
        },
      ],
      images: [
        'https://cdn.framework7.io/placeholder/cats-300x300-1.jpg',
        'https://cdn.framework7.io/placeholder/cats-200x300-2.jpg',
        'https://cdn.framework7.io/placeholder/cats-400x300-3.jpg',
        'https://cdn.framework7.io/placeholder/cats-300x150-4.jpg',
        'https://cdn.framework7.io/placeholder/cats-150x300-5.jpg',
        'https://cdn.framework7.io/placeholder/cats-300x300-6.jpg',
        'https://cdn.framework7.io/placeholder/cats-300x300-7.jpg',
        'https://cdn.framework7.io/placeholder/cats-200x300-8.jpg',
        'https://cdn.framework7.io/placeholder/cats-400x300-9.jpg',
        'https://cdn.framework7.io/placeholder/cats-300x150-10.jpg',
      ],
      people: [
        {
          name: 'Kate Johnson',
          avatar: 'https://cdn.framework7.io/placeholder/people-100x100-9.jpg',
        },
        {
          name: 'Blue Ninja',
          avatar: 'https://cdn.framework7.io/placeholder/people-100x100-7.jpg',
        },
      ],
      answers: [
        'Yes!',
        'No',
        'Hm...',
        'I am not sure',
        'And what about you?',
        'May be ;)',
        'Lorem ipsum dolor sit amet, consectetur',
        'What?',
        'Are you sure?',
        'Of course',
        'Need to think about it',
        'Amazing!!!',
      ],
      responseInProgress: false,
    };
  },
  computed: {
    attachmentsVisible() {
      const self = this;
      return self.attachments.length > 0;
    },
    placeholder() {
      const self = this;
      return self.attachments.length > 0 ? 'Add comment or Send' : 'Message';
    },
  },
  mounted() {
    const self = this;
    f7ready(() => {
      self.messagebar = f7.messagebar.get(self.$refs.messagebar.$el);
    });
  },
  methods: {
    isFirstMessage(message, index) {
      const self = this;
      const previousMessage = self.messagesData[index - 1];
      if (message.isTitle) return false;
      if (
        !previousMessage ||
        previousMessage.type !== message.type ||
        previousMessage.name !== message.name
      )
        return true;
      return false;
    },
    isLastMessage(message, index) {
      const self = this;
      const nextMessage = self.messagesData[index + 1];
      if (message.isTitle) return false;
      if (!nextMessage || nextMessage.type !== message.type || nextMessage.name !== message.name)
        return true;
      return false;
    },
    isTailMessage(message, index) {
      const self = this;
      const nextMessage = self.messagesData[index + 1];
      if (message.isTitle) return false;
      if (!nextMessage || nextMessage.type !== message.type || nextMessage.name !== message.name)
        return true;
      return false;
    },
    deleteAttachment(image) {
      const self = this;
      const index = self.attachments.indexOf(image);
      self.attachments.splice(index, 1)[0]; // eslint-disable-line
    },
    handleAttachment(e) {
      const self = this;
      const index = $(e.target).parents('label.checkbox').index();
      const image = self.images[index];
      if (e.target.checked) {
        // Add to attachments
        self.attachments.unshift(image);
      } else {
        // Remove from attachments
        self.attachments.splice(self.attachments.indexOf(image), 1);
      }
    },
    sendMessage() {
      const self = this;
      const text = self.messageText.replace(/\n/g, '<br>').trim();
      const messagesToSend = [];
      self.attachments.forEach((attachment) => {
        messagesToSend.push({
          image: attachment,
        });
      });
      if (text.length) {
        messagesToSend.push({
          text,
        });
      }
      if (messagesToSend.length === 0) {
        return;
      }
      // Reset attachments
      self.attachments = [];
      // Hide sheet
      self.sheetVisible = false;
      // Clear area
      self.messageText = '';
      // Focus area
      if (text.length) self.messagebar.focus();
      // Send message
      self.messagesData.push(...messagesToSend);
      // Mock response
      if (self.responseInProgress) return;
      self.responseInProgress = true;
      setTimeout(() => {
        const answer = self.answers[Math.floor(Math.random() * self.answers.length)];
        const person = self.people[Math.floor(Math.random() * self.people.length)];
        self.typingMessage = {
          name: person.name,
          avatar: person.avatar,
        };
        setTimeout(() => {
          self.messagesData.push({
            text: answer,
            type: 'received',
            name: person.name,
            avatar: person.avatar,
          });
          self.typingMessage = null;
          self.responseInProgress = false;
        }, 4000);
      }, 1000);
    },
  },
};
</script>



