廢話就先很少說了,直接上圖顯示最後的效果。
css
step1:任務分析
咱們須要有兩個界面:html
step2:生成界面android
cd 當前工程目錄下 // 生成消息列表頁 ionic generate page chat // 生成聊天界面 ionic generate page chat-message
step3:添加一個自定義的管道
這個自定義的管道用於咱們發送消息以後,顯示多長時間以前發送。這裏只是一個簡版。ios
在工程的目錄下,新建一個 pipes 的文件夾
。git
新建管道:moment.pipe.tsgithub
import { Pipe } from '@angular/core'; import moment from 'moment'; @Pipe({ name: 'moment' }) export class MomentPipe { transform(value, args) { args = args || ''; return args === 'ago' ? moment(value).fromNow() : moment(value).format(args); } }
新建 module:pipes.module.tspipes.module.ts
用於存放以後所新增的個性化的管道web
import { NgModule } from '@angular/core'; import { MomentPipe } from './moment.pipe'; export const pipes = [ MomentPipe ]; @NgModule({ declarations:[pipes], exports: [pipes] }) export class PipesModule { }
將 pipes.module.ts
加載到 app.module.ts 中app
@NgModule({ imports: [PipesModule] })
chat.html
界面上面使用 ion-list
組件做爲基礎組件,也是用這個組件來區分出不一樣時間段(天)的聊天信息。咱們能夠模版語法*ngFor
來動態生成今天的消息。ionic
<ion-header> <ion-navbar> <ion-title>聊天</ion-title> </ion-navbar> </ion-header>、 <ion-content class="chats"> <!-- 顯示今天的消息 --> <ion-list> <ion-list-header>今天</ion-list-header> <!-- 點擊的時候跳轉界面 --> <ion-item *ngFor="let chat of chats" (click)="viewMessages(chat)"> <ion-avatar item-start> <img [src]="chat.imageUrl"> </ion-avatar> <h2>{{chat.title}}</h2> <p>{{chat.lastMessage}}</p> <ion-note item-end>{{chat.timestamp | date:'HH:mm:ss'| lowercase}}</ion-note> </ion-item> </ion-list> <ion-list> <ion-list-header>昨天</ion-list-header> <ion-item> <ion-avatar item-start> <img src="assets/img/avatar/marty-avatar.png"> </ion-avatar> <h2>大逼哥</h2> <p>拉薩挺不錯的。。。。</p> <ion-note item-end>11:11</ion-note> </ion-item> <ion-item> <ion-avatar item-start> <img src="assets/img/avatar/marty-avatar.png"> </ion-avatar> <h2>人事主管</h2> <p>此次給你漲工資了。你查看下工資白條</p> <ion-note item-end>11:11</ion-note> </ion-item> </ion-list> <ion-list> <ion-list-header>前天</ion-list-header> <ion-item> <ion-avatar item-start> <img src="assets/img/avatar/ian-avatar.png"> </ion-avatar> <h2>祥</h2> <p>下次我請客吃飯。。。。</p> <ion-note item-end>11:11</ion-note> </ion-item> <ion-item> <ion-avatar item-start> <img src="assets/img/avatar/marty-avatar.png"> </ion-avatar> <h2>濤</h2> <p>何時回武漢?</p> <ion-note item-end>11:11</ion-note> </ion-item> </ion-list> </ion-content>
chat.ts
咱們定義一個 mock數據
,經過他來渲染出咱們的界面ide
import {Component} from '@angular/core'; import {IonicPage, NavController, NavParams} from 'ionic-angular'; @IonicPage() @Component({ selector: 'page-chat', templateUrl: 'chat.html', }) export class ChatPage { // mock 數據 chats = [{ id: '001', imageUrl: 'assets/img/avatar/marty-avatar.png', title: '房東', lastMessage: '這個月的房租怎麼尚未交?', timestamp: new Date() }, { id: '002', imageUrl: 'assets/img/avatar/ian-avatar.png', title: '房產中介', lastMessage: '上次給你推薦的房子,你看了沒有?我這邊有新的房源,你要不要過來看看?', timestamp: new Date() }, { id: '003', imageUrl: 'assets/img/avatar/sarah-avatar.jpg', title: '公司前臺', lastMessage: '你有新的快遞,請注意查收', timestamp: new Date() }]; constructor(public navCtrl: NavController, public navParams: NavParams) { } ionViewDidLoad() { console.log('ionViewDidLoad ChatPage'); } // 界面跳轉而且傳值 viewMessages(chat) { this.navCtrl.push('ChatMessagePage', {chatId: chat.id}); } }
chat-message.html
聊天界面分爲兩大塊:消息展現區域以及輸入消息區域。
消息展現區域分爲本身發送
以及別人發送
。
消息輸入區:就固定在屏幕下方。
<ion-header> <ion-navbar> <ion-title>chat</ion-title> </ion-navbar> </ion-header> <ion-content> <div *ngFor="let message of messages" class="message-wrapper" on-hold="onMessageHold($event, $index, message)"> <!-- 判斷消息是發送 --> <div *ngIf="user._id !== message.userId"> <img (click)="viewProfile(message)" class="profile-pic left" [src]="toUser.pic" onerror="onProfilePicError(this)" /> <!-- wave--> <div class="chat-bubble left slide-left"> <div class="message" [innerHTML]="message.text" autolinker> </div> <div class="message-detail"> <span (click)="viewProfile(message)" class="bold">{{toUser.username}}</span>, <span>{{message.date | moment:"ago" | lowercase}}</span> </div> </div> </div> <!-- 判斷消息是發送 --> <div *ngIf="user._id === message.userId"> <img (click)="viewProfile(message)" class="profile-pic right" [src]="user.pic" onerror="onProfilePicError(this)" /> <div class="chat-bubble right slide-right"> <div class="message" [innerHTML]="message.text" autolinker></div> <div class="message-detail"> <span (click)="viewProfile(message)" class="bold">{{user.username}}</span>, <span>{{message.date | moment:"ago" | lowercase}}</span> </div> </div> </div> <div class="cf"></div> </div> </ion-content> <!-- 底部固定的輸入框 --> <ion-footer> <form [formGroup]="messageForm" (submit)="send(chatBox)" novalidate> <ion-item> <ion-input formControlName="message" [(ngModel)]="chatBox" placeholder="Send {{toUser.username}} a message..."></ion-input> <button ion-button clear (click)="send(chatBox)" item-end><ion-icon class="footer-btn" name="send"></ion-icon></button> </ion-item> </form> </ion-footer>
chat-message.ts
咱們新建一個 messages 列表,有了新的消息就向這個模型中添加就能夠了。
import { FormControl, FormBuilder } from '@angular/forms'; import { Component, ViewChild } from '@angular/core'; import {IonicPage, NavController, Content, NavParams} from 'ionic-angular'; @IonicPage() @Component({ selector: 'page-chat-message', templateUrl: 'chat-message.html', }) export class ChatMessagePage { toUser = { _id: '534b8e5aaa5e7afc1b23e69b', pic: 'assets/img/avatar/ian-avatar.png', username: 'Venkman', }; user = { _id: '534b8fb2aa5e7afc1b23e69c', pic: 'assets/img/avatar/marty-avatar.png', username: 'Marty', }; doneLoading = false; messages = [ { _id: 1, date: new Date(), userId: this.user._id, username: this.user.username, pic: this.user.pic, text: 'OH CRAP!!' }, { _id: 2, date: new Date(), userId: this.toUser._id, username: this.toUser.username, pic: this.toUser.pic, text: 'what??' }, { _id: 3, date: new Date(), userId: this.toUser._id, username: this.toUser.username, pic: this.toUser.pic, text: 'Pretty long message with lots of content' }, { _id: 4, date: new Date(), userId: this.user._id, username: this.user.username, pic: this.user.pic, text: 'Pretty long message with even way more of lots and lots of content' }, { _id: 5, date: new Date(), userId: this.user._id, username: this.user.username, pic: this.user.pic, text: '哪尼??' }, { _id: 6, date: new Date(), userId: this.toUser._id, username: this.toUser.username, pic: this.toUser.pic, text: 'yes!' } ]; @ViewChild(Content) content: Content; public messageForm: any; chatBox: any; constructor(public navParams: NavParams, public navCtrl: NavController, public formBuilder: FormBuilder) { this.messageForm = formBuilder.group({ message: new FormControl('') }); this.chatBox = ''; } ionViewDidLoad() { let modelData: string = '用戶名:' + this.navParams.get('chatId'); console.log(modelData); } // 發送消息 send(message) { if (message && message !== '') { // this.messageService.sendMessage(chatId, message); const messageData = { toId: this.toUser._id, _id: 6, date: new Date(), userId: this.user._id, username: this.toUser.username, pic: this.toUser.pic, text: message }; this.messages.push(messageData); this.scrollToBottom(); setTimeout(() => { const replyData = { toId: this.toUser._id, _id: 6, date: new Date(), userId: this.toUser._id, username: this.toUser.username, pic: this.toUser.pic, text: 'Just a quick reply' }; this.messages.push(replyData); this.scrollToBottom(); }, 1000); } this.chatBox = ''; } scrollToBottom() { setTimeout(() => { this.content.scrollToBottom(); }, 100); } viewProfile(message: string){ console.log(message); } }
chat-message.scss
page-chat-message { /* allows the bar-footer to be elastic /* /* optionally set a max-height */ /* maxlength on the textarea will prevent /* /* it from getting too large also */ .bar-footer { overflow: visible !important; } .bar-footer textarea { resize: none; height: 25px; } /* fixes an ios bug bear */ button.ion-android-send { padding-top: 2px; } .footer-btn { font-size: x-large; } img.profile-pic { width: 40px; height: 40px; border-radius: 50%; position: absolute; bottom: 10px; } img.profile-pic.left { left: 10px; } img.profile-pic.right { right: 10px; } .ion-email { float: right; font-size: 32px; vertical-align: middle; } .message { font-size: 14px; } .message-detail { white-space: nowrap; font-size: 14px; } .bar.item-input-inset .item-input-wrapper input { width: 100% !important; } .message-wrapper { position: relative; } .message-wrapper:last-child { margin-bottom: 10px; } .chat-bubble { border-radius: 5px; display: inline-block; padding: 10px 18px; position: relative; margin: 10px; max-width: 80%; } .chat-bubble:before { content: "\00a0"; display: block; height: 16px; width: 9px; position: absolute; bottom: -7.5px; } .chat-bubble.left { background-color: #e6e5eb; float: left; margin-left: 55px; } .chat-bubble.left:before { background-color: #e6e5eb; left: 10px; -webkit-transform: rotate(70deg) skew(5deg); } .chat-bubble.right { background-color: #158ffe; color: #fff; float: right; margin-right: 55px; } .chat-bubble.right:before { background-color: #158ffe; right: 10px; -webkit-transform: rotate(118deg) skew(-5deg); } .chat-bubble.right a.autolinker { color: #fff; font-weight: bold; } .user-messages-top-icon { font-size: 28px; display: inline-block; vertical-align: middle; position: relative; top: -3px; right: 5px; } .msg-header-username { display: inline-block; vertical-align: middle; position: relative; top: -3px; } input, textarea, .item-input, .item-input-wrapper { background-color: #f4f4f4 !important; } .bold { font-weight: bold; } .cf { clear: both !important; } a.autolinker { color: #3b88c3; text-decoration: none; } /* loading */ .loader-center { height: 100%; display: -webkit-box; display: -moz-box; display: -ms-flexbox; display: -webkit-flex; display: flex; -webkit-box-direction: normal; -moz-box-direction: normal; -webkit-box-orient: horizontal; -moz-box-orient: horizontal; -webkit-flex-direction: row; -ms-flex-direction: row; flex-direction: row; -webkit-flex-wrap: nowrap; -ms-flex-wrap: nowrap; flex-wrap: nowrap; -webkit-box-pack: center; -moz-box-pack: center; -webkit-justify-content: center; -ms-flex-pack: center; justify-content: center; -webkit-align-content: stretch; -ms-flex-line-pack: stretch; align-content: stretch; -webkit-box-align: center; -moz-box-align: center; -webkit-align-items: center; -ms-flex-align: center; align-items: center; } .loader .ion-loading-c { font-size: 64px; } }
到此咱們就完成了。
在作這個的時候,參考了 https://github.com/yannbf/ion...。有興趣的話,你們能夠去看看。