AngularJSã¨Firebaseã§ãªã¢ã«ã¿ã¤ã ãã£ããã¢ããªä½ã£ã¦ã¿ãã
å
æã社å
ã®æå¿ã§åã
好ããªãã¼ããæ±ºãã¦ï¼æ¥ããããåå¼·ããã¨ããå¬ããéå¬ããã®ã§ãããåã¯ã¯ã©ã¤ã¢ã³ããµã¤ããã¬ã¼ã ã¯ã¼ã¯ã触ã£ã¦ã¿ããã¼ã¨ãããã¨ã§AngularJSãåãã¦è§¦ã£ã¦ã¿ã¾ããã以ä¸ã®å
容ã¯1æ¥ã§ãµãããåå¼·ããªããæ¸ããå
容ãªã®ã§ééã£ã¦ããã©ãã©ããææãã ããã
â»å ´æã¯ã ã¼ãã³ã°ã¹ã¯ã¯ããããã®ãªãã£ã¹ãåãã¾ãããé ç°ãããããã¨ããããã¾ããï¼ä¼ç¤¾ã¯こちら
AngularJSã¨ã¯
Google主å°ã§éçºãã¦ããã¯ã©ã¤ã¢ã³ããµã¤ãMVCãã¬ã¼ã ã¯ã¼ã¯ã§ããä»ã«ãåæ§ã®ãã®ã¯Backbone.jsã¨ãKnockout.jsã¨ããã£ãããã¾ããæè¿ãªãªã¼ã¹ãããnoteã¨ãããµã¼ãã¹ã¯AngularJSã使ã£ã¦ããã¿ããã§ããã
åèï¼noteをAngularJSで構築した話
ãããªãã¨ãã§ãã
以ä¸ã®ã³ã¼ãã¯AngularJSã®ãµã¤ãã«æ²è¼ããã¦ãããµã³ãã«ã§ãã
<!doctype html> <html ng-app> <head> <script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.3.0-beta.10/angular.min.js"></script> </head> <body> <div> <label>Name:</label> <input type="text" ng-model="yourName" placeholder="Enter a name here"> <hr> <h1>Hello {{yourName}}!</h1> </div> </body> </html>
ã¿ã°ã®ä¸ã«è¦æ £ããªãng-XXãããã¨æãã¾ãããããã¯AngularJSã¨HTMLã®ã¨ã¬ã¡ã³ããé¢é£ã¥ãã¦ããã³ã¼ãã§ããã¡ãªã¿ã«ãã¬ãã£ãã¯ã¹ã®ngã¯Angularã®A "ng" ularããåã£ã¦ãããããã§ãã
ãµã³ãã«ã¯ng-model="yourName"ã¨ããã³ã¼ãã§yourNameã¨ããã¢ãã«ãå®ç¾©ãã¦ããã¹ãããã¯ã¹ã«å²ãå½ã¦ã¦ãã¾ããã§ãä¸ã®h1ã® {{yourName}} ã§å®ç¾©ããã¢ãã«åãè¨å®ããã¦ãã¾ãããããAngularJSããã¤ã³ãããé¨åã«ãªãã¾ãã
ãã®HTMLããã©ã¦ã¶ã§è¡¨ç¤ºããã¨{{yourName}}ã¯è¡¨ç¤ºããã¾ããã代ããã«ããã¹ãããã¯ã¹ã«ä½ãå
¥åããã¨{{yourName}}ã®é¨åã«å
¥åããå¤ãèªåã§è¡¨ç¤ºãããããã«ãªãã¾ãã
jQueryã§åããããªãã¨ãããå ´åãããã¹ãããã¯ã¹ã«å¯¾ãã¦keyupã¤ãã³ãã§ããã¹ãããã¯ã¹ã®å¤ãåå¾âh1ã®ããã¹ãã«ã»ããããã¨ããå¦çãããªã²ã¼ãããã¿ãããªã³ã¼ããæ¸ãã®ã§Mappingã ãã§çä»ãã®ã§ããã°ããªã楽ã§ããã
ãã£ãããã¦ã¿ã
ãã£ããã¨è¨ãã°Node.js/socket.ioãªããã§ãæè»½ã«ä½ãäºãã§ããæä»£ã«ãªã£ãããã§ãããAngularJSã§ãFirebaseã¨ãããµã¼ãã¹ã¨çµã¿åããã¦ãã£ãããä½ããããã¾ãã
github-pagesã«ç«ã¦ã¦ã¾ãã
http://rei-m.github.io/angular_chat/
â»èªç±ã«æç¨¿ãã¦ããã£ã¦æ§ãã¾ãããå人æ å ±ã®æç¨¿ã¯è¦ã£ãããåé¤ãã¾ãã
ã§ã¯ãå°ãã ãã³ã¼ãã解説ãã¦ããã¾ãã
ã¾ã index.htmlã§ãã
<!doctype html> <html ng-app="chatApp"> <head> <meta charset="UTF-8"> <script src="https://cdnjs.cloudflare.com/ajax/libs/angular.js/1.2.6/angular.min.js"></script> <script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.3.0-beta.7/angular-route.min.js"></script> <script src="https://cdn.firebase.com/js/client/1.0.11/firebase.js"></script> <script src="https://cdn.firebase.com/libs/angularfire/0.7.1/angularfire.min.js"></script> <script src="js/index.js?11"></script> <link rel="stylesheet" href="css/style.css" /> </head> <body> <header id="pg_head"> <h1>ãã£ããã£ã½ãä½ããä½ã£ã¦ã¿ã</h1> </header> <div ng-view></div> <fotter> </fotter> </body> </html>
headã§AngularJSã®ã¢ã¸ã¥ã¼ã«ã¨Firebaseã®ã¢ã¸ã¥ã¼ã«ãèªã¿è¾¼ãã§ãã¾ããFirebaseã¨ããã®ã¯Websocketã¨ãã¼ã¿ã¹ãã¢ãæä¾ãã¦ãããPaasã£ã½ããµã¼ãã¹ã§ããFirebaseã¯AngularJSã¨é£æºããããã®ã¢ã¸ã¥ã¼ã«ãæä¾ãã¦ããã®ã§ããã使ãã¾ãã
å¾ã¯ä»¥ä¸ã®ã³ã¼ããhtmlã®ng-app屿§ã¯AngularJSã®ã¢ããªã§ãããã¨ãå®ç¾©ãã¦ãã¦ãèªåã§AngularJSã®èµ·å対象ã¨ãªãã¾ããdivã®æ¹ã¯AngularJSããã¤ã³ãããviewé¨åã§ãã
<html ng-app="chatApp">
<div ng-view></div>
次ã«Viewã2æä½ã£ã¦ãã¾ãããã®å
容ãURLã«å¿ãã¦å
ã®ng-viewã«ãã¤ã³ãããã¾ãã
ã¾ãã¯chatã§è¡¨ç¤ºããååãå
¥åããviewã
view/regist.html
<div id="registUserDiv"> ååãå ¥åãã¦ãã¿ã³ãæ¼ãã¦ã<br> <input type="text" ng-model="name" placeholder="ããªã¾ã" value=""><br> <button ng-click="regist()">ãã£ãããéå§ãã</button><br> </div>
ng-model="name"ãnameã¢ãã«ãng-click="regist()"ãregist颿°ã¨ããããAngularJSãMappingãã¦ãã¾ãã
次ã«chatæ¬ä½ã表示ããviewã§ãã
view/chat.html
<div id="messagesDiv"> <table id="ct_messageTable" border="0" cellspacing="0" > <tr ng-repeat="msg in messages" message-list> <td class="ct_left"> {{msg.from}}<br> <span class="ct_date">({{msg.date}})</span> </td> <td class="ct_right"> {{msg.body}} </td> </tr> </table> </div> <hr> <span>{{inputName}}ãã</span><br> <input type="text" ng-model="msg" ng-keydown="addMessage($event)" placeholder="ã¡ãã»ã¼ã¸ãå ¥ãã¦Enterãæ¼ãã¦ããç»åã®URLãè²¼ããã" style="width:800px;">
{{}}ã§å²ã¾ãã¦ããã¨ããã¯AngularJSããã¤ã³ãããã¨ããã§ããSmartyãJadeãªã©ã®ãã³ãã¬ã¼ãã¨ã³ã¸ã³ã¨åããããªè¦ãç®ã§ããã
æ°ããåºã¦ããã®ã¯ãã®é¨åã®ng-repeat屿§ã§ãã
<tr ng-repeat="msg in messages" message-list> <td class="ct_left"> {{msg.from}}<br> <span class="ct_date">({{msg.date}})</span> </td> <td class="ct_right"> {{msg.body}} </td> </tr>
msg in messageã¨ããå¤ãå ¥ã£ã¦ãã¾ããããã£ããæ å ±ãå ¥ã£ã¦ããmessageé åããï¼ä»¶ãã¤msgã«åãåºãã¦ä¸ã®tdã¿ã°ã«è¡¨ç¤ºãã¦ããã¨ãããããªå¦çãAngularJSã«å½ä»¤ãã¦ãããã¨ã«ãªãã¾ããfor in ã«ã¼ãã§å é ããã¾ãããããªã¤ã¡ã¼ã¸ã§ããã
ã§ãæå¾ã«AngularJSã®ã³ã³ããã¼ã©ã¨ãªãindex.jsã§ããã¡ã¨é·ãã®ã§å ¨ã¦ã®ã³ã¼ãã¯è§£èª¬ãã¾ããããããã¾ãã«è¨ãã¨åé ã§URLã«å¿ãã¦ã³ã³ããã¼ã©ã¨ng-viewã«è¡¨ç¤ºããviewãæå®ãã¦ãå¾åã§åã³ã³ããã¼ã©ãå®ç¾©ãã¦åæå¦çãng-clickãªã©ã®ã¤ãã³ãå¦çãå®è£ ãã¦è¡ãã¨ããå½¢ã§ãã
index.js
angular // ã¢ã¸ã¥ã¼ã«èªã¿è¾¼ã¿ .module('chatApp', ['firebase', 'ngRoute']) // ããããã£ã»ãã .value('fbURL', 'https://fiery-fire-8368.firebaseio.com/chat/') // factoryã»ãã .factory('Schema', function($firebase, fbURL) { return $firebase(new Firebase(fbURL)); }) // Rooting .config(function($routeProvider) { $routeProvider .when('/', { controller:'registUserCtl', templateUrl:'view/regist.html' }) .when('/chat/:userName', { controller:'chatCtl', templateUrl:'view/chat.html' }) .otherwise({ redirectTo:'/' }); }) // ã¦ã¼ã¶ã¼ã®ååãç»é²ããã³ã³ããã¼ã« .controller('registUserCtl', function($scope, $location) { // ãã£ããéå§ãã¿ã³æ¼ä¸ $scope.regist = function() { if($scope.name){ // ãã£ããç»é¢ã¸ $location.path('/chat/'+$scope.name); }else{ alert('æªå ¥åããï¼'); } }; }) // Viewãã¤ã³ãæã®ã¤ãã³ãã«ä»å ¥ .directive("messageList",function(){ return function(scope, element, attrs){ var _msg = scope.msg; var _re = /^(http|ftp):\/\/.+.(jpg|gif|png)$/; var _chkBody = _msg.body.toLowerCase(); if(_re.test(_chkBody)){ element[0].innerHTML = '<td class="ct_left">' + _msg.from + '<br><span class="ct_date">(' + _msg.date + ')</span></td><td class="ct_right"><img src="' + _msg.body + '"></td>'; } }; }) // ãã£ãããè¡ãã³ã³ããã¼ã« .controller('chatCtl', function($scope, $location, $routeParams, Schema) { // ã¦ã¼ã¶ã¼åãæªå ¥åã®å ´åã¯ãªãã¤ã¬ã¯ã if(!$routeParams.userName){ $location.redirectTo('/'); } // åæå $scope.msg = ''; // 渡ã£ã¦ããã¦ã¼ã¶ã¼åããã¤ã³ã $scope.inputName = $routeParams.userName; // ç»é²æ¸ã¿ã®ãã£ããæ å ±ããã¤ã³ã $scope.messages = Schema; // ã¡ãã»ã¼ã¸å ¥å $scope.addMessage = function(e) { // Enter以å¤ã¯ãªã¿ã¼ã³ if (e.keyCode != 13) return; // ä½ãããå ¥åããã¦ãããç»é² if($scope.msg !== ''){ var _date = new Date(), _year = _date.getFullYear(), _month = _date.getMonth() + 1, _day = _date.getDate(), _hour = _date.getHours(), _minute = _date.getMinutes(), _second = _date.getSeconds(); $scope.messages.$add({ from: $scope.inputName, body: $scope.msg, date: _year + '-' + _month + '-' + _day + ' ' + _hour + ':' + _minute + ':' + _second }); // å ¥åãçµãã£ããåæå $scope.msg = ''; } }; });
ãã®é¨åã¯hipchatã£ã½ãç»åã®URLãæç¨¿ãããããªã³ã¯ãããªãã¦ç»åã表示ããããªã¼ã¨ããã¨ããã§è¶³ãã¦ã¿ã¾ããããã£ããæç¨¿æã«æç¨¿å 容ãhttpããå§ã¾ã£ã¦æ¡å¼µåãjpg/gif/pngã ã£ãããã¤ã³ãããå å®¹ãæ¸ãæããã¨ãããããªãã¨ããã¦ãã¾ãã
.directive("messageList",function(){ return function(scope, element, attrs){ var _msg = scope.msg; var _re = /^(http|ftp):\/\/.+.(jpg|gif|png)$/; var _chkBody = _msg.body.toLowerCase(); if(_re.test(_chkBody)){ element[0].innerHTML = '<td class="ct_left">' + _msg.from + '<br><span class="ct_date">(' + _msg.date + ')</span></td><td class="ct_right"><img src="' + _msg.body + '"></td>'; } }; })
â»èªåã®ç°å¢ã§è©¦ãã¦ã¿ããï¼ã¨ããæ¹ã¯ãã®ã¾ã¾ä½¿ã£ã¦ãããã®ã§ãããããã ã¨åã®Firebaseã¢ã«ã¦ã³ãã«ãã¼ã¿ãç´ã¥ãã®ã§Firebaseã®ã¢ã«ã¦ã³ããç»é²ããå¾ã以ä¸ã®ã³ã¼ãã®URLãèªåã®ãã®ã§æ¸ãæãã¦ä¸ããã
// ããããã£ã»ãã .value('fbURL', 'https://fiery-fire-8368.firebaseio.com/chat/')
ææ
Node.jsã§ãã£ããã¢ããªã試ãã¦ã¿ãäºãããã¾ãããä»åã®ããæ¹ã¯ããªããæè»½ã§ããããã ã¹ãã¬ã¼ã¸ããFirebaseã®URLãã¯ã©ã¤ã¢ã³ãå´ã«ããè¦ããªã®ã§ä½¿ãéã¯æ³¨æãã¦ä¸ãããWeb Socketé¨åã¯Nodeã¨AngularJSã§ä½ã£ã¦ãµã¼ããµã¤ãã§Firebaseãä¿åå ã¨ãã¦ä½¿ãã®ãå®ç¨çãªæ°ããã¾ããï¼ã§ããã®ããªï¼ï¼AngularJSèªä½ãä»äºã§ä½¿ã£ã¦ã¿ããã®ã§ããããããããä»äºã§è§¦ã£ã¦ãããããã¯ãã¯IE8以ä¸ããµãã¼ããã¨ãã®ã§åãã®ããããこちらãèªãã æããIE8ã§ãåãããã§ããæããã¦ã©ããªããã¨ããããããã£ã¬ã³ã¸ãã¦ã¿ããã¯ããã¾ããã
ä»åã®ã³ã¼ãã¯GitHubã«ç½®ãã¦ããã®ã§èªç±ã«éãã§ä¸ããã
https://github.com/rei-m/angular_chat
ã§ã¯ã