返回介绍

8.5.2 安装插件 2

发布于 2025-02-26 23:07:11 字数 19508 浏览 0 评论 0 收藏 0

设置控制器

先来,设置每个路由的控制器。打开

www/js/controller.js 清除其中所有内容。添加一个新模组名为 chatapp.controllers :

angular.module('chatapp.controllers', [])

chatapp.controllers 模组添加一个 run 方法。run 方法由监视进入的聊天信息的逻辑所组成。我们利用聊天基本 URL 建立了一个连接,然后监听了他的任何改变。 如果有新的聊天添加或者聊天内容变更,我们将辨别新的聊天信息是否跟当前用户相关。如果是的话,我们广播一个 newChatHistory 事件,这样在聊天历史标签页中,我们将会展示新的聊天信息:

.run(['FBFactory', '$rootScope', 'UserFactory', 'Utils',function(FBFactory, $rootScope, UserFactory, Utils) { $rootScope.chatHistory = []; var baseChatMonitor = FBFactory.chatBase(); var unwatch = baseChatMonitor.$watch(function(snapshot) { var user = UserFactory.getUser(); if (!user) return; if (snapshot.event == 'child_added' || snapshot.event == 'child_changed') { var key = snapshot.key; if (key.indexOf(Utils.escapeEmailAddress(user.email)) >= 0) { var otherUser = snapshot.key.replace(/_/g,'').replace('chat', '').replace(Utils.escapeEmailAddress(user.email), ''); if ($rootScope.chatHistory.join('_').indexOf(otherUser) === -1) { $rootScope.chatHistory.push(otherUser); } $rootScope.$broadcast('newChatHistory'); /* * TODO: PRACTICE * Fire a local notification when a new chat comes in. */ } } }); } ])

为对以上代码更好的理解,我没有实现本地通知那部分。如果你愿意的接受的话,你的任务就是在聊天送到当前用户的时候实现本地通知。通知内容是由聊天信息和来源用户所组成。 当点击通知的时候,将会把当前用户带到聊天界面。

接下来,我们将开始

MainCtrl 。MainCtrl 与我们一样的主视图连接。将以下的 MainCtrl 定义添加到 www/js/controllers.js 文件里的 run 方法后面:

.controller('MainCtrl', ['$scope', 'Loader', '$ionicPlatform','$cordovaOauth', 'FBFactory', 'GOOGLEKEY', 'GOOGLEAUTHSCOPE','UserFactory', 'currentAuth', '$state', function($scope, Loader, $ionicPlatform, $cordovaOauth,FBFactory, GOOGLEKEY, GOOGLEAUTHSCOPE, UserFactory, currentAuth,$state) { $ionicPlatform.ready(function() { Loader.hide(); $scope.$on('showChatInterface', function($event,authData) { if (authData.google) { authData = authData.google; } UserFactory.setUser(authData); Loader.toggle('Redirecting..'); $scope.onlineusers = FBFactory.olUsers(); $scope.onlineusers.$loaded().then(function() { $scope .onlineusers .$add({ picture:authData.cachedUserProfile.picture, name: authData.displayName, email: authData.email, login: Date.now() }) .then(function(ref) { UserFactory.setPresenceId(ref.key()); UserFactory.setOLUsers($scope.onlineusers); $state.go('tab.dash'); }); }); return; }); if (currentAuth) { $scope.$broadcast('showChatInterface',currentAuth.google); } $scope.loginWithGoogle = function() { Loader.show('Authenticating..'); $cordovaOauth.google(GOOGLEKEY, GOOGLEAUTHSCOPE). then(function(result) { FBFactory.auth() .$authWithOAuthToken('google',result.access_token) .then(function(authData) { $scope.$broadcast('showChatInterface',authData); }, function(error) { Loader.toggle(error); }); }, function(error) { Loader.toggle(error); }); } }); } ])

当 promise 在路由的 resolve 方法中解析完成的时候,

MainCtrl 将会被调用,并给他传入 currentAuth 作为依赖。如果用户已经登录的话,currentAuth 将等于 oauth ;相反,则为 null 。我们在
$scope 上注册了 showChatInterface 。这个方法在用户已经登录(具体来说就是 currentAuth 不为 null )或者用户明确登录的时候被调用。当此事件触发的时候,我们通过 UserFactory.setUser 方法将用户数据保存到 localStorage 。 一旦这个操作完成了,我们将发起一个请求以获取所有的在线用户。拿到在线用户列表之前,我们就将当前用户详情添加到 onlineUsers 集合中。添加完成之后,我们在 localStorage 中 setPresenceiD 和 setOLUsers 然后将用户重定向到聊天界面。

当用户的点击 按钮的时候,

Login with Google $scope.loginWithGoogle 方法将被触发。我已经添加了 Firebase Auth 和 cordovaOauth 插件以展示如何同时使用他们。 如果你觉得复杂的话,你可以直接使用 Firebase Auth 登录用户,而不是从 cordovaOauth 获取 token 然后使用 Firebase $authWithOAuthToken 认证用户。一旦认证成功,我们将广播一个
showChatInterface 事件,这个事件将会保存数据并重定向用户。

作为范例的一部分,我只实现了 Google OAuth*。作为练习,你可以整合其他 oAuth 提供者。

一旦用户登录成功,他将被重定向到标签页界面。默认的标签页是 dashboard 标签页,他与

DashCtrl 连接。
DashCtrl 的目标是取得在线用户列表并展示。他需要这样子的代码:

.controller('DashCtrl', ['$scope', 'UserFactory','$ionicPlatform', '$state', '$ionicHistory',function($scope, UserFactory, $ionicPlatform, $state,$ionicHistory) { $ionicPlatform.ready(function() { $ionicHistory.clearHistory(); $scope.users = UserFactory.getOLUsers(); $scope.currUser = UserFactory.getUser(); var presenceId = UserFactory.getPresenceId(); $scope.redir = function(user) { $state.go('chat-detail', { otherUser: user }); } }); } ])

在上面的控制器代码中我使用了

$ionicHistory.clearHistory 方法。这样就保证了,当用户登录成功并且在标签页上的时候,点击设备的返回按钮不会将用户带回登录界面而是直接退出 app。 因此,使用$ionicHistory.clearHistory 方法我们情况历史记录,然后设备的返回按钮将关闭应用。

接下来我们将使用中间位置的标签页,这个标签页用来展示聊天记录的。由于这只是一个范例应用,所以我们不需要严格的维护任何用户资料。我们直接从 auth 对象里面获取值,然后建立 UI。 我们将使用相同的逻辑在历史标签页中显示用户信息。用户对象是从在线用户列表中获取的。

ChatsCtrl 代码如下,添加到 DashCtrl 后面:

.controller('ChatsCtrl', ['$scope', '$rootScope', 'UserFactory','Utils', '$ionicPlatform', '$state', function($scope, $rootScope,UserFactory, Utils, $ionicPlatform, $state) { $ionicPlatform.ready(function() { $scope.$on('$ionicView.enter', function(scopes, states) { var olUsers = UserFactory.getOLUsers(); $scope.chatHistory = []; $scope.$on('AddNewChatHistory', function() { var ch = $rootScope.chatHistory, matchedUser; for (var i = 0; i < ch.length; i++) { for (var j = 0; j < olUsers.length; j++) { if (Utils.escapeEmailAddress(olUsers[j].email) == ch[i]) { matchedUser = olUsers[j]; } }; if (matchedUser) { $scope.chatHistory.push(matchedUser); } else { $scope.chatHistory.push({ email: Utils.unescapeEmailAddress(ch[i]), name: 'User Offline' }) } }; }); $scope.redir = function(user) { $state.go('chat-detail', { otherUser: user }); } $rootScope.$on('newChatHistory', function($event) { $scope.$broadcast('AddNewChatHistory'); }); $scope.$broadcast('AddNewChatHistory'); }) }); }])

注意看

matchedUser 对象。在用户有聊天历史但是用户在离线状态的时候,这个值将会设为一个离线值,之前也说过。

在这里,我们一直监听从

run 方法里面广播的 newChatHistory 事件。最后,当当前登录用户点击用户名的时候,我们将应用重定向到 chat-detail 视图。接下来是应用里最活跃的页面,这个页面用来与其他用户交互。他的控制器代码太多,没法一次贴出来。因此,我将控制器分离成不同的逻辑块,逐个解释。首先,我们添加了控制器定义和他的依赖:

.controller('ChatDetailCtrl', ['$scope', 'Loader','$ionicPlatform', '$stateParams', 'UserFactory', 'FBFactory', '$ionicScrollDelegate', '$cordovaImagePicker', 'Utils','$timeout', '$ionicActionSheet', '$cordovaCapture', '$cordovaGeolocation', '$ionicModal',function($scope, Loader, $ionicPlatform, $stateParams,UserFactory, FBFactory, $ionicScrollDelegate, $cordovaImagePicker, Utils, $timeout, $ionicActionSheet, $cordovaCapture,$cordovaGeolocation, $ionicModal) { $ionicPlatform.ready(function() { Loader.show('Establishing Connection...'); // controller code here.. }); }])

好多依赖库哇!接下来获取

chatToUser 然后将他添加到 scope。完成之后,我们连接到动态 Firebase 终端。以下代码(将会在结束前)将会添加到 ChatDetailCtrl 中:

$scope.chatToUser = $stateParams.otherUser; $scope.chatToUser = JSON.parse($scope.chatToUser); $scope.user = UserFactory.getUser(); $scope.messages = FBFactory.chatRef($scope.user.email, $scope.chatToUser.email); $scope.messages.$loaded().then(function() { Loader.hide(); $ionicScrollDelegate.scrollBottom(true); });

我们使用

$ionicScrollDelegate 服务来滚动视图面板到最后一条聊天信息。接下来,新建一个方法用作向 Firebase 添加新的聊天信息:

function postMessage(msg, type, map) { var d = new Date(); d = d.toLocaleTimeString().replace(/:\d+ /, ' '); map = map || null; $scope.messages.$add({ content: msg, time: d, type: type, from: $scope.user.email, map: map }); $scope.chatMsg = ''; $ionicScrollDelegate.scrollBottom(true); }

当用户输入文本点后点击 的时候,我们调用

Send sendMessage 方法:

$scope.sendMessage = function() { if (!$scope.chatMsg) return; var msg = '<p>' + $scope.user.cachedUserProfile.name + ' says : <br/>' + $scope.chatMsg + '</p>'; var type = 'text'; postMessage(msg, type); }

使用以下 Action Sheet(动作表单)服务,来展示一系列的列表,例如:

Share Picture 分享图片,Take Picture 拍照,Share My Location 分享位置信息:

$scope.showActionSheet = function() { var hideSheet = $ionicActionSheet.show({ buttons: [{ text: 'Share Picture' }, { text: 'Take Picture' }, { text: 'Share My Location' }], cancelText: 'Cancel', cancel: function() { // add cancel code.. Loader.hide(); }, buttonClicked: function(index) { // Clicked on Share Picture if (index === 0) { Loader.show('Processing...'); var options = { maximumImagesCount: 1 }; $cordovaImagePicker.getPictures(options) .then(function(results) { if (results.length > 0) { var imageData = results[0]; Utils.getBase64ImageFromInput( imageData, function(err, base64Img) { //Process the image string. postMessage('<p>' + $scope.user.cachedUserProfile.name + ' posted : <br/><img class="chat-img" src="' + base64Img + '">', 'img'); Loader.hide(); }); } }, function(error) { // error getting photos console.log('error', error); Loader.hide(); }); } // Clicked on Take Picture else if (index === 1) { Loader.show('Processing...'); var options = { limit: 1 }; $cordovaCapture.captureImage(options).then(function(imageData) { Utils.getBase64ImageFromInput(imageData[0].fullPath, function(err, base64Img) { //Process the image string. postMessage('<p>' + $scope.user.cachedUserProfile.name + ' posted : <br/><img class="chat-img" src="'+ base64Img + '">', 'img'); Loader.hide(); }); }, function(err) { console.log(err); Loader.hide(); }); } // clicked on Share my location else if (index === 2) { $ionicModal.fromTemplateUrl('templates/map-modal.html', { scope: $scope, animation: 'slide-in-up' }).then(function(modal) { $scope.modal = modal; $scope.modal.show(); $timeout(function() { $scope.centerOnMe(); }, 2000); }); } return true; } }); }

当选中 Action Sheet 中的选项的时候,如果:

  • index = 0:我们调用

$cordovaImagePicker 服务让用户选择图片。当用户选中图片后,我们调用 Utils.getBase64ImageFromInput 方法将图片转变成 base64 的字符串。 然后我们使用 postMessage 方法将他发送给 Firebase。

  • index = 1:我们调用

$cordovaCapture 服务的 captureImage 方法来照相,转成 base64 字符串,然后发送给 Firebase。

  • index = 2:我们调用 Ionic Modal 带有地图,指向用户当前位置。

为了在弹出框中使用地图,我们需要在 scope 上定义一些方法:

$scope.mapCreated = function(map) { $scope.map = map; }; $scope.closeModal = function() { $scope.modal.hide(); }; $scope.centerOnMe = function() { if (!$scope.map) { return; } Loader.show('Getting current location...'); var posOptions = { timeout: 10000, enableHighAccuracy: false }; $cordovaGeolocation.getCurrentPosition(posOptions). then(function(pos) { $scope.user.pos = { lat: pos.coords.latitude, lon: pos.coords.longitude }; $scope.map.setCenter(new google.maps.LatLng($scope.user.pos.lat, $scope.user.pos.lon)); $scope.map.__setMarker($scope.map, $scope.user.pos.lat, $scope.user.pos.lon); Loader.hide(); }, function(error) { alert('Unable to get location, please enable GPS to continue'); Loader.hide(); $scope.modal.hide(); }); }; $scope.selectLocation = function() { var pos = $scope.user.pos; var map = { lat: pos.lat, lon: pos.lon }; var type = 'geo'; postMessage('<p>' + $scope.user.cachedUserProfile.name + ' shared : <br/>', type, map); $scope.modal.hide(); }

地图创建的时候将会调用

mapCreated 方法。closeModal 将用来关闭弹出框。centerOnMe 方法在地图初始化完成之后自动调用。这个方法使用$cordovaGeolocation.getCurrentPosition 来获取用户房钱位置。 一旦位置获取成功,他会用一个标记来替换那个点。如果没法通过$cordovaGeolocation.getCurrentPosition 来获取用户坐标,我们就会询问用户激活 GPS。当功能正常运行的时候,就能更好的理解了。最后,

AccountCtrl ,连接到 Tab3 。这个控制器有管理用户登出的方法:

.controller('AccountCtrl', ['$scope', 'FBFactory', 'UserFactory','$state', function($scope, FBFactory, UserFactory, $state) { $scope.logout = function() { FBFactory.auth().$unauth(); UserFactory.cleanUser(); UserFactory.cleanOLUsers(); // remove presence var onlineUsers = UserFactory.getOLUsers(); if (onlineUsers && onlineUsers.$getRecord) { var presenceId = UserFactory.getPresenceId(); var user = onlineUsers.$getRecord(); onlineUsers.$remove(user); } UserFactory.cleanPresenceId(); $state.go('main'); } } ]);

在此,你也可以设置一些个人偏好,例如当打开应用的时候显示通知,播放声音等等。

设置模板

现在我们完成了所有的 Javascript 代码,我们接下来就是实现每个视图的模板来。所有视图都是逻辑实现,简单易懂。 第一个是

main.html ,www/templates/main.html 的全部内容:

<ion-view view-title="IONIC CHAT APP" cache-view="false"> <ion-content> <ion-slide-box does-continue="true" auto-play="true" showpager="false"> <ion-slide> <label class="t-r">Share Photos seamlessly between family & Friends</label> <img src="http://placeimg.com/640/480/tech/grayscale" /> </ion-slide> <ion-slide> <label class="c-c">Simple One click login to start the fun!!</label> <img src="http://placeimg.com/640/480/people/sepia" /> </ion-slide> <ion-slide> <label class="b-r">Notify people where you are with one click location sender</label> <img src="http://placeimg.com/640/480/tech/sepia"/> </ion-slide> </ion-slide-box> <div class="text-center padding"> <h3>The Super chat app, lets you connect with your friends, share images, audio, video, geo-location and ofcourse texts!</h3> <button class="button button-dark" ngclick="loginWithGoogle()"> Login With Google </button> </div> </ion-content> </ion-view>

接下来是

www/templates/tabs.html 文件。使用以下内容替换他的全部内容:

<ion-tabs class="tabs-striped tabs-top tabs-background-positive tabs-color-light"> <!-- Dashboard Tab --> <ion-tab title="IONIC CHAT APP" icon-off="ion-ios-pulse" iconon="ion-ios-pulse-strong" href="#/tab/dash"> <ion-nav-view name="tab-dash"></ion-nav-view> </ion-tab> <!-- Chats Tab --> <ion-tab title="IONIC CHAT APP" icon-off="ion-ios-chatboxesoutline" icon-on="ion-ios-chatboxes" href="#/tab/chats"> <ion-nav-view name="tab-chats"></ion-nav-view> </ion-tab> <!-- Account Tab --> <ion-tab title="IONIC CHAT APP" icon-off="ion-ios-gear-outline" icon-on="ion-ios-gear" href="#/tab/account"> <ion-nav-view name="tab-account"></ion-nav-view> </ion-tab> </ion-tabs>

接下来是

www/templates/tab-dash.html :

<ion-view view-title="IONIC CHAT APP"> <ion-content> <ion-list> <ion-item ng-show="users.length == 1"> <h3 class="text-center padding">Looks like no one is online</h3> </ion-item> <ion-item class="item-avatar item-icon-right" ngrepeat="user in users | filter:search:user" ng-if="user.email != currUser.email" ng-click="redir('{{user}}')"> <img ng-src="{{user.picture}}"> <h2>{{user.name}}</h2> <p>{{user.email}}</p> <i class="icon ion-chevron-right iconaccessory"></i> </ion-item> </ion-list> </ion-content> </ion-view>

接着

www/templates/tab-chats.html :

<ion-view view-title="IONIC CHAT APP"> <ion-content> <ion-list> <ion-item ng-show="chatHistory.length == 0"> <h3 class="text-center padding">Looks like there is no chat history</h3> </ion-item> <ion-item class="item-icon-right item-icon-left" ngclass="{'item-avatar' : user.picture}" ng-repeat="user in chatHistory | filter:search:user" ng-if="user.email != currUser.email" ng-click="redir('{{user}}')"> <img ng-src="{{user.picture}}" ngshow="user.picture"> <h2>{{user.name}}</h2> <p>{{user.email}}</p> <i class="icon ion-chevron-right iconaccessory"></i> </ion-item> </ion-list> </ion-content> </ion-view>

第三个标签页

www/templates/tab-account.html :

<ion-view view-title="IONIC CHAT APP"> <ion-content has-header="true"> <ion-list> <!-- Uncomment below if you would like to add preferences to the app --> <!-- <ion-item> <ion-toggle ng-change="updatePreference()" ngmodel="preference.notification" toggle-class="toggle-positive">Show Notifications</ion-toggle> </ion-item> --> <ion-item> <button class="button button-dark button-block" ng-click="logout()"> Logout </button> </ion-item> </ion-list> </ion-content> </ion-view>

所以行为发生的地方

www/templates/chat-detail.html :

<ion-view view-title="{{chatToUser.name}}"> <ion-pane> <ion-content class="has-header padding"> <div class="button-bar"> <a class="button button-calm" uisref="tab.dash">Online Users</a> <a class="button button-calm" uisref="tab.chats">Chat History</a> </div> <br> <ion-list> <ion-item ng-show="messages.length == 0"> <h3 class="text-center">No messages yet!</h3> </ion-item> <ion-item class="item-avatar" ng-repeat="message in messages" ng-class="{left : message.from == user.email, right : message.from != user.email}"> <img ng-src="{{user.cachedUserProfile.picture}}" ng-if="message.from == user.email"> <img ng-src="{{chatToUser.picture}}" ngif="message.from != user.email"> <p ng-bind-html="message.content"></p> <map inline="true" class="inline-map" lat="{{message.map.lat}}" lon="{{message.map.lon}}" ngif="message.map.lat && message.map.lon"> </ion-item> <div class="padding-bottom"></div> </ion-list> </ion-content> <ion-footer-bar class="bar-footer"> <input class="footerInput" type="text" placeholder="Enter Message" ng-model="chatMsg"> <button class="button button-dark icon-left ionpaper-airplane" ng-click="sendMessage();"></button> <button class="button button-dark icon-left ion-more" ng-click="showActionSheet();"></button> </ion-footer-bar> </ion-pane> </ion-view>

注意在这里我们使用了 map 指令,内联属性设为

true

当用户选择分享位置信息的时候,我们需要一个地图展示。现在我们就来创建这个。在

www/templates 中新建一个文件名为 map-modal.html ,更新如下:

<ion-modal-view> <ion-content scroll="false"> <map on-create="mapCreated(map)"></map> </ion-content> <ion-footer-bar class="bar-stable"> <a ng-click="selectLocation()" class="button button-icon icon ion-checkmark">Share</a> <a ng-click="closeModal()" class="button button-icon icon ion-close">Cancel</a> </ion-footer-bar> </ion-modal-view>

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。
列表为空,暂无数据
    我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
    原文