Angularにも触れてみる 1
はじめに
前回はReactを使ってブログ記事を表示するページを作りかけてみたわけですが、表示するための記事を投稿するためのページが欲しくなりました。
投稿するページは自分だけが利用できればそれでよく、正直DBにそのままデータを突っ込んでも。。。と思ったりもしましたが、それは流石に不便かな、と。
そこでふと、以前聞いたRebuild.fmでReactについて話していた回を思い出しました。
管理画面のようなページを、短時間で作成するには現時点ではAngularの方がやりやすい、というような話題があったように思うのですが、今回の投稿ページはかなりそれに近いのでは?と思ったので、Angularに挑戦してみることにしました。
…というのが表向きの理由です。
裏の理由としては、たまたまKindleストアで見つけたコレの表紙が気になっていたからですw
という感じで、投稿ページはAngularを、閲覧ページはReactを使って作ってみようと思います(挫折するまで)。
準備
- プロジェクトは前回のものにページを追加することにします。
- Javascript、CSSは閲覧ページとは別のものを使用することにします(ごちゃごちゃになりそうなので)。
- Angularは公式ページからver.1.3.15のminified版をダウンロードします。
ファイルの追加(ソースコードは後述)
- views ディレクトリに「newpost.slim」を新規作成します。
- public > js ディレクトリに「addnewpost.js」を新規作成します。
- ダウンロードした「angular.min.js」をpublic > js ディレクトリに置きます。
「angular.min.js」の変更
以前Backboneでもありましたが、Chromeで「angular.min.js」を開くとmin.mapが見つからないと404エラーになります。
そのため、最終行のコメントアウトを変更してやります。
//# sourceMappingURL=angular.min.js.map ↓ // # sourceMappingURL=angular.min.js.map
SlimでAngularを使う
newpost.slim
DOCTYPE html lang='ja' ng-app='NewPostApp' head meta charset='utf-8' title Add New Post body header a.header_title href='/' Vaguely body main#newpost ng-controller='NewPostCtrl' form ng-submit='addPost()' input#new_title type='text' placeholder='タイトルを入力' ng-model='postTitle' textarea#new_article placeholder='本文を入力' ng-model='postArticle' input#new_category type='text' ng-model='postCategory' div ng-bind='postCategory' input#new_btn_submit type='submit' value='投稿' input#new_cancel type='reset' value='キャンセル' script src='/js/angular.min.js' script src='/js/addnewpost.js'
「ng-XXX」の部分がAngular特有の部分です。
処理の内容は以下のような感じです。
- ng-app: Angularを使用するための宣言。今回は「NewPostApp」を指定していますが、空のままでも使用できます。
- ng-controller: MVCの中心となる、コントローラをHTML要素(今回はmainタグ)に追加します。
- ng-submit: submitボタンを押した時に、AngularのSubmitイベントを発火させます。
- ng-model: HTML要素にモデルを紐付けます。これによって「$scope.postTitle」の形でTextareaなどの値を取得したり、 「ng-bind」で該当のモデルを紐付けているHTML要素にリアルタイムに値を出力することができます。
基本的にはHTMLと同じように書くことができますが、いくつか注意点があります。
- まずページ内でAngularを使うことを宣言する「ng-app」。今回は「NewPostApp」を指定していますが、ここを空にしたい場合、「< html lang='ja' ng-app >」のように、「html lang='ja' ng-app」と指定するとエラーになります。
そのため、「html lang='ja' ng-app=''」としてやる必要があります。
- ng-modelを設定したテキストボックスなどへの入力内容を、ページ上にリアルタイムで反映させるデータバインディングを使いたい場合、HTMLのように「div {{postCategory}}」とするとエラーになります。
回避するためには文字列として出力する、ng-bindを使うといった方法があるようですが、見た目的に一番何をやっているかがわかりやすそうなのでng-bindを使いました。
div ng-bind='category'
ちなみに文字列として出力する場合は、「div '{{category}}'」のようにしてもエラー回避はできますが、出力先に「''」が表示されますw
Javascript
ようやくメインにたどり着きました。
現在以下のように書いています。
addnewpost.js
(function () { // Angularモジュールを作成する. var newPostApp = angular.module('NewPostApp', []); // コントローラの作成.scope、httpを引数とする. newPostApp.controller('NewPostCtrl',[ '$scope', '$http', function($scope ,$http) { $scope.addPost = function() { // Postメソッドでデータを送信する(タイトル、記事本文をJSONとして送信). $http.post('/create', {title: $scope.postTitle, article: $scope.postArticle}). success(function(data){ console.log('success'); }). error(function(data, status, headers, config) { console.log('fail'); }); // タイトルのテキストボックスを空にする. $scope.postTitle = ''; }; } ]); }).call(this);
- Submitボタンが押された時に「$scope.addPost」が呼ばれ、Postメソッドを投げます。これを後述のSinatra側のコントローラで受け取ります。
- HTMLの要素やデータにアクセスするには、引数として渡されている「$scope」を使用します。
- 「$scope」を使うと、Textareaなどのデータを受け取るだけでなく、新しい値を代入することもできます。
- データを送受信するためには同じく引数として渡されている「$http」を使用します。
- jQueryを使ったAjaxと同じように、データ送信に成功すればsuccessが、失敗すればerrorが呼ばれます。
Sinatraのコントローラで受け取る
app.rb
require 'json' require 'rss/maker' require 'sass' require 'sinatra' require 'sinatra/base' require 'sinatra/reloader' require 'slim' 〜省略〜 get '/newpost' do # 投稿ページの表示. slim :newpost end post '/create' do # JSON形式でデータを受け取る. jsnParams = ActiveSupport::JSON.decode(request.body.read) print jsnParams['title'] print jsnParams['article'] end 〜省略〜
Postメソッドで投げたJSONデータは、「request.body.read」で受け取ることができます。
そのまま、または「request.body.read.to_json」とすれば、「jsnParams[:title]」や「jsnParams['title']」のような形で受け取れるかな?と思いきやうまくいかず...。
(エラーとなったり、「title」という文字列が返ったりしました)
「ActiveSupport::JSON.decode」で変換し、「jsnParams['title']」と指定することでJavascript側で設定していた値が取得出来ました。
本当のところはまず投稿ページでログイン認証が必要だったり、フォームデータのサニタイジングやDBへのデータ追加などもありますが、これは次回。
感想など
今回触ったところだけでいうと、Angularは既存のHTMLのイベントやAjaxを置き換える形となっており、なんとなくとっつきやすいような印象を受けました。
もちろん作成上のルールに従う形で書いたり、今回未使用のfactoryなどを使用してみるとまた違って見えるかもしれませんが。
Reactと比較してどちらが優れているか?ということではなく、お互い得意分野があり、作りたいものに合わせて選択できるようになれば良いかな、と思っています。
どちらもまだHello worldレベルではありますが、色々と試しながら身につけていけたらと思います。
Angular.js
- Tutorial - AngularJS
- Learning AngularJS
- [備忘録]angularjsでcontrollerを動的に追加する - Qiita
- SlimでAngularJSを扱ってエラーが出たときの対処法 - 大学生からはじめる Web 開発
- AngularJS 1.2 日本語リファレンス - js STUDIO
- AngularJSのTutorialのstep-5よりAjaxの書き方・テストの仕方 - Qiita
- ゼロから学ぶ、AngularJS入門(1) - フラップイズム
- #3 Angular.js - mozaic.fm
- 108 JSJ AngularJS with Igor Minar - Javascript Jabber - DevChat.tv
- AngularJS - angular.module の落とし穴 - Qiita