2015年11月19日

Ionic Framework 教學 - 8. 簡易聊天室

第六章第七章

設定好 Firebase 後,我們可以來做簡易聊天室,了解資料怎麼和後台連結,為了方便教學,這裡的聊天室我們只做匿名聊天室。
首先我們要做一個聊天室列表,我們增加一個 ChatroomsCtrl Controller,注意這裡 Chatrooms 是複數。

.controller('ChatroomsCtrl', function($scope) {

  $scope.chatrooms = [];
  
  $scope.newChatroom = {};

  $scope.createChatroom = function () {
    console.log($scope.newChatroom);
  };
})

記得 $scope 代表是 View 要使用的變數,這裡我們定義了 chatrooms 陣列, newChatroom 物件和 createChatroom 函式。
console.log 代表在瀏覽器 console 把變數內容印出來看看的意思。
然後建立 www/templates/chatrooms.html
<ion-view view-title="Chatrooms">
  <ion-content>
    <ion-list>
      <ion-item class="item-input-inset">
        <label class="item-input-wrapper">
          <input type="text" placeholder="New Chatroom Name" ng-model="newChatroom.name">
        </label>
        <button class="button button-small" ng-click="createChatroom()">
          Create
        </button>
      </ion-item>
      <ion-item ng-repeat="chatroom in chatrooms" href="#/app/chatrooms/{{chatroom.$id}}">
        {{chatroom.name}}
      </ion-item>
    </ion-list>
  </ion-content>
</ion-view>
其中 ng-model 代表使用變數,ng-repeat 代表使用陣列中的變數。
然後在 www/js/app.js 增加一個 route
  .state('app.chatrooms', {
    url: '/chatrooms',
    views: {
      'menuContent': {
        templateUrl: 'templates/chatrooms.html',
        controller: 'ChatroomsCtrl'
      }
    }
  })

我們順便把 www/js/app.js 最下面的 $urlRouterProvider.otherwise('/app/playlists'); 改掉,這是預設的顯示頁面,如果連到任何未定義的 url 就會改連到這裡,我們改成 /app/chatrooms。
$urlRouterProvider.otherwise('/app/chatrooms');

最後在 www/templates/menu.html 增加一個 Chatrooms 選項

<ion-item menu-close href="#/app/chatrooms">
  Chatrooms
</ion-item>

現在可以按按看按鈕,理解一下程式內容。
然後我們增加一個 www/js/services.js
var firebaseUrl = "https://your-project-id.firebaseio.com/";
angular.module('starter.services', [])
//Firebase datastore reference
.factory("FirebaseRef", function($firebaseAuth) {
  return new Firebase(firebaseUrl);
})
//for log in and sign up
.factory("Auth", function($firebaseAuth) {
  var usersRef = new Firebase(firebaseUrl+"users");
  return $firebaseAuth(usersRef);
})
.factory("ChatroomService", function($firebaseArray) {
  var roomsRef = new Firebase(firebaseUrl+"chatrooms");
  var rooms = $firebaseArray(roomsRef);
  return {
    all: function() {
      console.log(rooms);
      return rooms;
    },
    get: function(roomId) {
      return rooms.$getRecord(roomId);
    },
    create: function(chatroom) {
      console.log('Adding chatroom: ', chatroom.name);
      return rooms.$add(chatroom);
    }
  }
});

其中 your-project-id 請填入你在 Firebase 申請的 Project ID ,這裡我們在做一個和 Firebase 溝通的 Chatroom service 。
這裡我們定義了 ChatroomService 服務,他主要有三個功能 all 來取得所有的聊天室, get 來取得特定 ID 的聊天室, create 來創造一個聊天室。
我們在第六章有說到,在 www/index.html 中,需要引用 services.js 來讓這個服務可以載入到頁面中。
...
<script src="js/app.js"></script>
<script src="js/services.js"></script>
...

在第六章也有提到,在 app.js 最上面要加上 starter app 對 'firebase' 和 'starter.services' 的依賴關係,這樣 angular 才知道要引用這些程式和服務。

angular.module('starter', ['ionic', 'firebase', 'starter.services', 'starter.controllers'])

然後讓我們的 ChatroomsCtrl 來使用這個 service 。


.controller('ChatroomsCtrl', function($scope, ChatroomService) {

  $scope.chatrooms = ChatroomService.all();
  
  $scope.newChatroom = {};

  $scope.createChatroom = function () {
    console.log($scope.newChatroom);
    ChatroomService.create($scope.newChatroom);
  };
})

這樣就可以順利增加 Chatroom 了,如果你到 Firebase project console 也可以看到對應的資料被增加上去了,比如說我增加了三個聊天室,下面圖片亂數的部分就是 Firebase 給這三個聊天室的特殊 ID。











目前Chatroom bar 點下去是沒有用的,因為對應的路徑 #/app/chatrooms/{{chatroom.$id}} 還沒有定義。

現在我們來製作可以發話的聊天室。
首先我們增加這個聊天室訊息的 Service 。
在 www/js/services.js 下面增加 MessageService 。

.factory("MessageService", function($firebaseArray) {
  var messagesRef = new Firebase(firebaseUrl+"messages");
  var messages;
  return {
    all: function(roomId) {
      console.log('get messages from: ', roomId);
      var roomRef = new Firebase(firebaseUrl + "chatrooms/" + roomId + "/messages");
      messages = $firebaseArray(roomRef);
      return messages;
    },
    send: function(message) {
      console.log('Adding message: ', message);
      return messages.$add(message);
    }
  };
})

這裡我們定義了 MessageService 有兩個功能, all 是根據某個 chatroom 的 ID 找出所有的對話訊息的 messages ,send 是用來發送訊息,就是把新的訊息加入 Firebase 中,這個 chatroom 的 messages 陣列。

然後我們就可以在 Controller 中使用這個 Services 了,我們建立一個 MessagesCtrl

.controller('MessagesCtrl', function($scope, $state, ChatroomService, MessageService) {

  console.log('Chatroom Id: ', $state.params.roomId);

  //get current chatroom from roomId
  $scope.chatroom = ChatroomService.get($state.params.roomId) || {};
  console.log('This is chatroom: ', $scope.chatroom.name);

  //get messages in this chatroom
  $scope.messages = MessageService.all($state.params.roomId);

  $scope.newMessage = {};

  $scope.sendMessage = function () {
    console.log('New message: ', $scope.newMessage);

    //send message
    MessageService.send($scope.newMessage);
  };

})

然後我們來建立這個聊天室的訊息頁面,建立 www/templates/messages.html

<ion-view view-title="{{chatroom.name}}">
  <ion-content>
    <ion-list>
      <ion-item class="item-input-inset">
        <label class="item-input-wrapper">
          <input type="text" placeholder="New Message" ng-model="newMessage.content">
        </label>
        <button class="button button-small" ng-click="sendMessage()">
          Send
        </button>
      </ion-item>
      <ion-item class="item-text-wrap" ng-repeat="message in messages.slice().reverse()">
        {{message.content}}
      </ion-item>
    </ion-list>
  </ion-content>
</ion-view>

這裡可以看到 chatroom 代表當前這個聊天室,newMessage 代表 input 輸入框中新增的訊息, sendMessage 代表按鈕按下去後送出訊息,ng-repeat="message in messages" 代表列出所有在 messages 中的訊息。
然後我們來增加一個 route ,修改 www/js/app.js
.state('app.messages', {
  url: '/chatrooms/:roomId',
  views: {
    'menuContent': {
      templateUrl: 'templates/messages.html',
      controller: 'MessagesCtrl'
    }
  }
})
因為我們可以有很多個聊天室,每個聊天室的 url 不同,所以注意這裡的 :roomId ,代表這個 url 願意接受變數 roomId,並且把變數 roomId 傳給 MessageCtrl Controller 使用,請對照 chatrooms.html 的 href="#/app/chatrooms/{{chatroom.$id}}" 觀察,我們把 url 中的 chatroom.$id 對應到 state route 的 roomId , 使用者進到訊息頁面的時候 route 把 roomId 接受到的值給了新的 controller 中的 $state.params.roomId,我們是用這個介面來達成動態頁面的變數傳遞。

這裡編輯好之後就可以告一段落了。

發送訊息之後,各位也可以到 Firebase console 去看你的資料結構。












下一章,簡易聊天室設定