8.5.1 安装插件 1
用到的名词:
- authentication 授权,认证
安装所需的 Cordova 插件
我们现在安装所需插件,运行如下命令:
ionic plugin add https://github.com/wymsee/cordova-imagePicker.git ionic plugin add cordova-plugin-file ionic plugin add cordova-plugin-geolocation ionic plugin add cordova-plugin-inappbrowser ionic plugin add cordova-plugin-media-capture
获取 Google API key
由于我们的 app 用来 Google OAuth,我们需要一个 Client ID。可通过以下步骤获取一个 Client ID:
- 导航至:
https://console.developers.google.com
- 点击 ,输入项目名
Create project
- 创建完成后打开项目
- 在左边的菜单中点击 然后点击 。填写必需信息。产品名是强制的。
APIs and authConsent
- 点击左边的菜单中的 ,然后点击
APIs and authCredentials
- 点击 OAuth 部分下面的
Create new Client ID
- 选中如下:
- 应用类型为网页应用(web application)
- 授权(Authorized)JavaScript origins 为
- 授权(Authorized)重定向 URI 为
- 一旦上述信息填写完成,点击 你就可以看到网页应用的 Client ID
Creat Client ID
有了 Client ID 之后,你就需要在我们的 Firebase Forge 中更新。导航到 Firebase 应用页(查看实时数据更新的地方)。在页面左边,可以找到一个菜单选项“ ;点击之。 当右边页面刷新之后,点击 Google 标签页,填入之前生成的 Google Client ID 和 Secret。
Login and Auth
以上步骤对认证工作非常重要
如果你想创建一个 API key 来访问 Google 地图 API,可以使用以下步骤:
- 点击左边菜单的 ,然后点击
APIs and authCredentials
- 点击 下面的
Public API AccessCreate new Key
- 从弹出框里面选择浏览 key
- 将 文本域留空
Accept request from these HTPP referrers
- 点击 。这样将生成 API key,这一用这个 API key 来替换
Create index.html 中的那个 key
- 点击左边菜单中的 ,然后点击
APIs and authAPIs
- 在搜索框里面搜索 ,点击搜索到的连接
Google Maps JavaScript API v3
- 点击 按钮激活 API
Enable API
现在,我们有了 Client ID,我们将设置一些常量。 在
www/js/app.js**中, run 方法之后 config*方法之前,添加以下 3 个常量:
.constant('FBURL', 'https://ionic-chat-app.firebaseio.com/') .constant('GOOGLEKEY', '1002599169952-4uchnlc7ahm6ng4696p9tgr1adhsiqv5.apps.googleusercontent.com') .constant('GOOGLEAUTHSCOPE', ['email'])
将 FBURL 替换为你的 Firebase 应用 URL。将 Google key 替换为我们早先生成的 Client ID。作为 OAuth 请求的一部分,我们需要向 Google 发送一个 scope, 这样我们就可以取回我们需要的信息。我们的应用只需要用户的基本信息;此外,我们用邮件作为一个 scope。
设置路由以及路由认证
由于我们建立的是一个标签页模板的应用,因为我们就基本有了所有所需的路由。我们将对已有的路由做一些变更并给他们添加认证。 这样,当授权失败的时候,视图就不会展示。我们将使用路由的
resolve 属性来实现。
继续深入之前,我建议先浏览一下 AngularJS 状态路由器的
resolve 属性。这个属性负责在加载控制器之前解析所有指定的 promise。 这个在我们加载控制器之间验证用户授权状态非常有用。更多信息参考: https://github.com/angularui/ui-router/wiki#resolve
Firebase 的
$firebaseAuth 函数上来两个方法:
$waitForAuth :他返回一个将用做解析当前授权状态的 promise。这个仅用在主页路由上。
$requireAuth :他返回一个与当前授权状态一切解析的 promise;否则,将拒绝这个 promise。这个将用在每个需要授权认证的路由。
我们将平衡借助这两个方法来控制未授权和授权的用户可以看到什么不可以看到什么。我们将为现有的路由多加一个路由名为
main 。这个路由将作为默认路由且作为我们应用的主页。 我们在每个路由上添加一个 resolve 属性用来解析 Firebase Auth。 同时,修改 chat-detail 路由使他摆脱 chats 路由的子路由的身份。更新后的路由部分代码如下:
$stateProvider.state('main', { url: '/', templateUrl: 'templates/main.html', controller: 'MainCtrl', cache: false, resolve: { 'currentAuth': ['FBFactory', 'Loader', function(FBFactory, Loader) { Loader.show('Checking Auth..'); return FBFactory.auth().$waitForAuth(); }] } }) .state('tab', { url: "/tab", abstract: true, cache: false, templateUrl: "templates/tabs.html" }) .state('tab.dash', { url: '/dash', cache: false, views: { 'tab-dash': { templateUrl: 'templates/tab-dash.html', controller: 'DashCtrl' } }, resolve: { 'currentAuth': ['FBFactory', function(FBFactory) { return FBFactory.auth().$requireAuth(); }] } }) .state('tab.chats', { url: '/chats', cache: false, views: { 'tab-chats': { templateUrl: 'templates/tab-chats.html', controller: 'ChatsCtrl' } }, resolve: { 'currentAuth': ['FBFactory', function(FBFactory) { return FBFactory.auth().$requireAuth(); }] } }) .state('tab.account', { url: '/account', cache: false, views: { 'tab-account': { templateUrl: 'templates/tab-account.html', controller: 'AccountCtrl' } }, resolve: { 'currentAuth': ['FBFactory', function(FBFactory) { return FBFactory.auth().$requireAuth(); }] } }) .state('chat-detail', { url: '/chats/:otherUser', templateUrl: 'templates/chat-detail.html', controller: 'ChatDetailCtrl', cache: false, resolve: { 'currentAuth': ['FBFactory', 'Loader', function(FBFactory, Loader) { Loader.show('Checking Auth..'); return FBFactory.auth().$requireAuth(); }] } }); $urlRouterProvider.otherwise('/');
在如上代码中使用工厂的时候我们会设置
FBFactory 和 Loader 。
注意,我们将 otherwise 的路由更新为'/'
此时,当
$requireAuth 拒绝 promise 的时候,以为这用户去到了一个需要认证的页面,但是用户并没有认证。 因为,我们需要在这个时候添加一个监听器,将用户重定向到登录页。当 Firebase Auth 拒绝了 promise 的时候,他发出了一个 stateChangeError 事件。 我们将在 run 方法中监听这个事件,然后将用户重定向到主页。在
$ionicPlatform.ready 方法中,添加以下 stateChangeError 事件到 run 方法:
$rootScope.$on('$stateChangeError', function(event, toState,toParams, fromState, fromParams, error) { if (error === 'AUTH_REQUIRED') { $state.go('main'); } });
记住给
run 方法注入$rootScope 和$state 依赖。
接下来,我们将使用
$ionicConfigProvider 来设置一些默认值。给
config 方法添加$ionicConfigProvider 作为依赖。在开始初始化路由之前,将以下代码片段添加到 config 方法内:
$ionicConfigProvider.backButton.previousTitleText(false); $ionicConfigProvider.views.transition('platform'); $ionicConfigProvider.navBar.alignTitle('center');
上面的配置也不是必需的。只是给你展示如何在一个实时 app 中使用
$ionicConfigProvider
设置服务/工厂
现在我们设置好了主体应用,我们现在就来看看需要用到的工厂了。工厂都将添加到
www/js/services.js 中。可以先打开这个文件,清空其中所有内容。首先,添加
chatapp.services 模组和一个与 localStorage 交互的工厂:
angular.module('chatapp.services', []) .factory('LocalStorage', [function() { return { set: function(key, value) { return localStorage.setItem(key, JSON.stringify(value)); }, get: function(key) { return JSON.parse(localStorage.getItem(key)); }, remove: function(key) { return localStorage.removeItem(key); }, }; }])
接下来,添加一个工厂用来管理 Ionic 加载服务:
.factory('Loader', ['$ionicLoading', '$timeout',function($ionicLoading, $timeout) { return { show: function(text) { //console.log('show', text); $ionicLoading.show({ content: (text || 'Loading...'), noBackdrop: true }); }, hide: function() { //console.log('hide'); $ionicLoading.hide(); }, toggle: function(text, timeout) { var that = this; that.show(text); $timeout(function() { that.hide(); }, timeout || 3000); } }; } ])
也要创建一个与 Firebase 交互的工厂:
.factory('FBFactory', ['$firebaseAuth', '$firebaseArray', 'FBURL','Utils',function($firebaseAuth, $firebaseArray, FBURL, Utils) { return { auth: function() { var FBRef = new Firebase(FBURL); return $firebaseAuth(FBRef); }, olUsers: function() { var olUsersRef = new Firebase(FBURL + 'onlineUsers'); return $firebaseArray(olUsersRef); }, chatBase: function() { var chatRef = new Firebase(FBURL + 'chats'); return $firebaseArray(chatRef); }, chatRef: function(loggedInUser, OtherUser) { var chatRef = new Firebase(FBURL + 'chats/chat_' + Utils.getHash(OtherUser, loggedInUser)); return $firebaseArray(chatRef); } }; } ])
上面的代码中,
olUsersRef 将 Firebase 的引用指向 , https://ionicchat-app.firebaseio.com/onlineUsers chatBase 指向 , https://ionic-chatapp.firebaseio.com/chats chatRef 指向在两个用户之间动态创建的终端。
我们也会创建一个用户工厂用来存储用户信息,在线用户和已有 ID。已有 ID 是在 ID。 这个已有 ID 将在用户离线时,
https://ionicchat-app.firebaseio.com/onlineUsers 中创建的入口 object onlineUsers 集合删除对象之用:
.factory('UserFactory', ['LocalStorage', function(LocalStorage) { var userKey = 'user', presenceKey = 'presence', olUsersKey = 'onlineusers'; return { onlineUsers: {}, setUser: function(user) { return LocalStorage.set(userKey, user); }, getUser: function() { return LocalStorage.get(userKey); }, cleanUser: function() { return LocalStorage.remove(userKey); }, setOLUsers: function(users) { // >> we need to store users as pure object. // else we lose the $ method of FB. // >> sometime, onlineUsers becomes null while // navigating between tabs, so we save a copy in LS LocalStorage.set(olUsersKey, users); return this.onlineUsers = users; }, getOLUsers: function() { if (this.onlineUsers && this.onlineUsers.length > 0) { return this.onlineUsers } else { return LocalStorage.get(olUsersKey); } }, cleanOLUsers: function() { LocalStorage.remove(olUsersKey); return onlineUsers = null; }, setPresenceId: function(presenceId) { return LocalStorage.set(presenceKey, presenceId); }, getPresenceId: function() { return LocalStorage.get(presenceKey); }, cleanPresenceId: function() { return LocalStorage.remove(presenceKey); }, }; }])
最后,添加一些工具方法:
.factory('Utils', [function() { return { escapeEmailAddress: function(email) { if (!email) return false // Replace '.' (not allowed in a Firebase key) with ',' email = email.toLowerCase(); email = email.replace(/\./g, ','); return email.trim(); }, unescapeEmailAddress: function(email) { if (!email) return false email = email.toLowerCase(); email = email.replace(/,/g, '.'); return email.trim(); }, getHash: function(chatToUser, loggedInUser) { var hash = ''; if (chatToUser > loggedInUser) { hash = this.escapeEmailAddress(chatToUser) + '_' + this.escapeEmailAddress(loggedInUser); } else { hash = this.escapeEmailAddress(loggedInUser) + '_' + this.escapeEmailAddress(chatToUser); } return hash; }, getBase64ImageFromInput: function(input, callback) { window.resolveLocalFileSystemURL(input, function(fileEntry) { fileEntry.file(function(file) { var reader = new FileReader(); reader.onloadend = function(evt) { callback(null, evt.target.result); }; reader.readAsDataURL(file); }, function() { callback('failed', null); }); }, function() { callback('failed', null); }); } }; }])
getHash 方法接收两个邮件地址返回一个哈希。这个方法用作构建动态终端。 getBase64ImageFromInput 用作将图片转换成 base64 编码的字符串,以存放到 Firebase。做完这些之后,我们完成了工厂的设置。
设置地图指令
由于我们需要给用户提供当前位置分享的功能呢,我们需要一个地图指令来将坐标以一个可呈现的方式显示出来。 我们从地图模块( 在
https://github.com/driftyco/ionicstarter-maps/blob/master/js/directives.js)中借用*map*指令,按需修改。
www/js 文件夹内新建一个文件名为 directives.js 。更新其内容如下:
angular.module('chatapp.directives', []) .directive('map', function() { return { restrict: 'E', scope: { onCreate: '&' }, link: function($scope, $element, $attr) { function initialize() { var lat = $attr.lat || 43.07493; var lon = $attr.lon || -89.381388; var myLatlng = new google.maps.LatLng(lat, lon); var mapOptions = { center: myLatlng, zoom: 16, mapTypeId: google.maps.MapTypeId.ROADMAP }; if ($attr.inline) { mapOptions.disableDefaultUI = true; mapOptions.disableDoubleClickZoom = true; mapOptions.draggable = true; mapOptions.mapMaker = true; mapOptions.mapTypeControl = false; mapOptions.panControl = false; mapOptions.rotateControl = false; } var map = new google.maps.Map($element[0], mapOptions); // custom function to manage markers map.__setMarker = function(map, lat, lon) { var marker = new google.maps.Marker({ map: map, position: new google.maps.LatLng(lat, lon) }); } $scope.onCreate({ map: map }); map.__setMarker(map, lat, lon); } if (document.readyState === 'complete') { initialize(); } else { google.maps.event.addDomListener(window, 'load', initialize); } } } });
我所做的修改是:调整 map 指令以在聊天信息中的内联地图上使用内联属性能够正常运行。我也添加了展示标记的支持。
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论