カテゴリー: javascript

AngularJSでCalendarを表示する

カスタマイズできるのカレンダーが必要になったのでざっくり実装。

AngularJSでカレンダーを描画してみる。
合わせて、モーダルウィンドウ上でもカレンダーを表示してみる。

デモはこちら

サンプルソースコードはこちら

Mo3g4u/AngularJSCalendarSample

angularjscalendarsample

バックエンドに問い合わせて、月のカレンダーを受け取り
描画しているだけ。

バックエンドが返すデータに予約枠数とか、リンクなどを
含めれば予約カレンダーみたいなものができる想定。

AngularJSでデータ構造をjsonで画面に表示したい

久しぶりに触ってまったく思い出せなかったのでメモ。

$logとかでコンソールログに出すほどじゃないけど、データが
今どうなっているか確認したい場合は以下のようにすれば画面に表示される。


<pre>
  {{data | json}}
</pre>

表示したいデータが「data」で「json」フィルタに通す。

/API Reference/ng/filter components in ng/json

AngularJSでinput要素にrequiredを設定していると半角スペースを入れてもエラーになる件

AngularJSでinput要素にrequiredを設定していると半角スペースを入れてもエラーになる。

半角スペースでも入っていたらエラーを出したくない。

調べてみるとこれはどうもng-modelの仕業のようです。

ng-modelはデフォルトでtrimを行うらしく、この動作のせいで
半角スペースを入れただけではエラーになってしまうみたい。

普段の挙動はこれでいいんだろけど、今回は半角スペース入れる
だけでもvalidationを通してしまいたかったので勝手にtrimしない
ように「ng-trim=”false”」を指定。

↓これを・・・

<input ng-model="name" type="text" required />

↓こうしました。

<input ng-model="name" ng-trim="false" type="text" required />

参考サイト
AngularJS ng-required allowing only whitespace

Ajaxzip3のGithub移行版へ移行

ajaxzip3のGithub移行版へ移行

以前はURLがhttpとhttpsで別だったのでajaxzip3を一本化するために↓のようなことをしてました・・・。

<script src=”//ajaxzip3.googlecode.com/svn/trunk/ajaxzip3/ajaxzip3.js” charset=”UTF-8″></script>
<script type=”text/javascript”>AjaxZip3.JSONDATA=”//ajaxzip3.googlecode.com/svn/trunk/ajaxzip3/zipdata”;</script>

でも最近動かなくなってました。。

なのでついでにGithub版に移行です。

Github移行したのを機にhttpとhttpsの一本化されてて素晴らしい!

<script src="https://ajaxzip3.github.io/ajaxzip3.js" charset="UTF-8"></script>

Google Codeのサービス廃止が決定しているので早めの移行が必要です。

ajaxzip3/ajaxzip3.github.io
https://github.com/ajaxzip3/ajaxzip3.github.io

jQuery UIでリストのソート機能を実装したときにFirefoxとかでinputのフォーカスがおかしくなる件

AngularJsのディレクティブでリストのソート機能を
実装していて困ったのでメモ。

ディレクティブでは実際には「jQuery UI」の「sortable」を
利用していた。

各々のバージョンは以下
jQuery v2.1.3
jQuery UI v1.11.4
AngularJS v1.3.15

実装したコードは以下。

app.directive('mySortable',function(){
    return {
        link:function(scope,el,attrs){
            el.sortable({
                revert: true,
                axis: 'y',
                cancel: "",
                handle: ".sortable-handle"
            });
            
            el.disableSelection().delegate('input,textarea','click',function(ev){
                ev.target.focus();
            });
            
            el.on( "sortdeactivate", function( event, ui ) {
                //ソートした時の挙動
                ・・・
            });
            
        }
    }
})

上記の状態だと「Firefox」と「IE」でソートは出来るのだけどソートする対象内に
あるinputタグにフォーカスが当たるけど全選択(Ctrl+a)とかができないし、
入力値がある場合にもなぜか必ず入力エリアの左側にカーソルが入ってしまう
現象が発生した。

結局以下↓の箇所を

el.disableSelection().delegate('input,textarea','click',function(ev){
    ev.target.focus();
});

【jQuery】sortable()を設定したタグ内のtextarea, inputタグに入力出来ない」このページの内容通りに以下↓のように変更。

el.bind('click.sortable mousedown.sortable',function(ev){
    ev.target.focus();
});

直った。。

jQuery UI の.disableSelection()がdeprecated(非推奨)になってる件

↑これとかの影響なのかな・・・。

■参考URL

【jQuery】sortable()を設定したタグ内のtextarea, inputタグに入力出来ない

Ticket #4429 (closed bug: notabug) Can’t select text in inputs within sortables

jQuery UI の.disableSelection()がdeprecated(非推奨)になってる件

WindowsのVagrant+CentOSで共有フォルダでnpm関係でエラーが出る件[未解決]

WindowsのVagrant+CentOSで共有フォルダでnpm関係でエラーが出る件[未解決]

Windows7上のVagrantでCentOS6.5を動かしている状態で、
共有フォルダ内で「yo angular」したら
以下のようなエラーが大量に出た。

npm ERR! code EPERM
npm ERR! errno -1
npm ERR!   code: 'EPERM',
npm ERR! Please try running this command again as root/Administrator.
npm ERR! tar.unpack untar error 

「/home/vagrant」以下とかだと問題無く動くのにどうやら
共有の「/vagrant」以下で発生する問題っぽい。

まだ解決していないけど、手がかりっぽいサイトをまとめておく。

■Working with npm and symlinks through Vagrant on Windows
http://perrymitchell.net/article/npm-symlinks-through-vagrant-windows/

■running a Vagrant box on Windows fails due to path length issues
https://github.com/npm/npm/wiki/Troubleshooting#upgrading-on-windows

■Vagrant + シェルスクリプトでPHP開発環境をプロビジョニングしてみたメモ
http://k-holy.hatenablog.com/entry/2013/09/05/084237

■provisioning, run: “always” doesn’t
https://groups.google.com/forum/#!topic/vagrant-up/BF_L7-A1iCU

■VirtualBox(Ubuntu)で共有フォルダするときの注意。
http://rubellum.hatenablog.com/entry/20110508/1304835867

■WindowsのVMでnpm install時にモジュールのパスが255文字を超えてしまう場合
http://qiita.com/aooni_kun/items/7d3ded7b1a51886f21dc

■Windowsでのファイルパス長の制限でnpmのパッケージが使えない
http://ja.stackoverflow.com/questions/1990/windows%E3%81%A7%E3%81%AE%E3%83%95%E3%82%A1%E3%82%A4%E3%83%AB%E3%83%91%E3%82%B9%E9%95%B7%E3%81%AE%E5%88%B6%E9%99%90%E3%81%A7npm%E3%81%AE%E3%83%91%E3%83%83%E3%82%B1%E3%83%BC%E3%82%B8%E3%81%8C%E4%BD%BF%E3%81%88%E3%81%AA%E3%81%84

■Node npm windows file paths are too long to install packages
http://stackoverflow.com/questions/26155135/node-npm-windows-file-paths-are-too-long-to-install-packages

う~ん。。共有フォルダで直に作業するのは微妙かも。。

CentOS6環境にyeomanでAngularJSの雛形を作成するまで

CentOS6環境にyeomanでAngularJSの雛形を作成するまで

AngularJSを開発するときに最初の準備が
面倒なのでyeomanでAngularJSの雛形を簡単
に準備するメモ。

以下の手順ではすんなり入っているようにみえますが、
nodeとかnpmとか久しぶりなのとVagrantのメモリが少なかった
とか色々あってかなり苦戦しました。
最終的に必要だった手順だけ載せます。

インストールはVagrantに作ったCentOS6.5。
ネットワークはpublic_networkでとりあえずDHCP。
インストール時のIPは192.168.1.222。
メモリは2GBに設定。

■nodejs,npmをepelからインストール

# rpm -ivh http://ftp-srv2.kddilabs.jp/Linux/distributions/fedora/epel/6/x86_64/epel-release-6-8.noarch.rpm
# yum -y install nodejs npm --enablerepo=epel
# node --version
v0.10.33
# npm -v
1.3.6

■一応最新版を入れるためにnをいれて安定版を使う

# npm cache clean
# npm install -g n
# n stable
# node -v
v0.10.33
# npm -v
1.3.6

■以前インストールを試したときにエラーが出たのでnpmで一応以下のものも先に入れておく
※あとで面倒なのでグローバルインストール
ここの作業は本来いらないはず・・・?

# npm install -g coffee-script
# npm install -g phantom
# npm install -g grunt-contrib-imagemin
# npm install -g ws

■bower,grunt,gulp,yoをnpmでいれる
※あとで面倒なのでグローバルインストール

# npm install -g bower grunt-cli gulp yo

■AngularJS雛形用のジェネレータをインストール
※あとで面倒なのでグローバルインストール

# npm install -g generator-angular

■yeomanでAngularJSの雛形を作成する
※ここからは一般ユーザでおこなった。

$ mkdir test-angular  <-適当にフォルダを作成
$ cd test-angular/
$ yo angular  <-yoコマンドの実行

     _-----_
    |       |    .--------------------------.
    |--(o)--|    |    Welcome to Yeoman,    |
   `---------´   |   ladies and gentlemen!  |
    ( _´U`_ )    '--------------------------'
    /___A___\
     |  ~  |
   __'.___.'__
 ´   `  |° ´ Y ` 

Out of the box I include Bootstrap and some AngularJS recommended modules.

? Would you like to use Sass (with Compass)? No  <-Sassは自分は使わないのでNo
? Would you like to include Bootstrap? Yes  <-Bootstrapは使うのでYes
? Which modules would you like to include?  <-表示がよくわからないけど使うものを「◉」使わないものを「◯」に変更
 ◯ angular-animate.js
 ◯ angular-aria.js
 ◉ angular-cookies.js
 ◉ angular-resource.js
 ◯ angular-messages.js
 ◉ angular-route.js
 ◉ angular-sanitize.js
❯◯ angular-touch.js

・・・しばらく待つ・・・

Execution Time (2015-03-18 01:23:47 UTC)
loading tasks    6ms  ▇▇▇▇▇▇▇ 3%
wiredep:app    159ms  ▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇ 89%
wiredep:test    12ms  ▇▇▇▇▇▇▇▇▇▇▇▇▇▇ 7%
Total 179ms

$ ll
total 76
drwxrwxr-x  6 vagrant vagrant  4096 Mar 18 01:21 ./
drwxrwxr-x  3 vagrant vagrant  4096 Mar 18 01:19 ../
drwxrwxr-x  6 vagrant vagrant  4096 Mar 18 01:20 app/
drwxrwxr-x 10 vagrant vagrant  4096 Mar 18 01:21 bower_components/
-rw-rw-r--  1 vagrant vagrant   371 Mar 18 01:20 bower.json
-rw-rw-r--  1 vagrant vagrant    38 Mar 18 01:20 .bowerrc
-rw-r--r--  1 vagrant vagrant   415 Feb 10 19:07 .editorconfig
-rw-r--r--  1 vagrant vagrant    11 Feb 10 19:07 .gitattributes
-rw-r--r--  1 vagrant vagrant    52 Feb 10 19:07 .gitignore
-rw-rw-r--  1 vagrant vagrant 10575 Mar 18 01:20 Gruntfile.js
-rw-r--r--  1 vagrant vagrant   376 Feb 10 19:07 .jshintrc
drwxrwxr-x 30 vagrant vagrant  4096 Mar 18 01:21 node_modules/
-rw-rw-r--  1 vagrant vagrant  1154 Mar 18 01:21 package.json
-rw-rw-r--  1 vagrant vagrant   282 Mar 18 01:20 README.md
drwxrwxr-x  3 vagrant vagrant  4096 Mar 18 01:20 test/
-rw-r--r--  1 vagrant vagrant   110 Feb  2 19:59 .travis.yml
-rw-r--r--  1 vagrant vagrant     3 Feb 10 19:07 .yo-rc.json
雛形が完成

■Gruntfile.jsの編集
静的サーバを立ち上げてブラウザから確認できるようにhostnameを変更しておく。
今回の場合は192.168.1.222にした。

・・・・
    // The actual grunt server settings
    connect: {
      options: {
        port: 9000,
        // Change this to '0.0.0.0' to access the server from outside.
        hostname: '192.168.1.222',
        livereload: 35729
      },
・・・・

■Gruntサーバ起動

$ grunt serve
Running "serve" task

Running "clean:server" (clean) task
>> 1 path cleaned.

Running "wiredep:app" (wiredep) task

Running "wiredep:test" (wiredep) task

Running "concurrent:server" (concurrent) task

    Running "copy:styles" (copy) task
    Copied 1 file

    Done, without errors.

    Execution Time (2015-03-18 01:39:59 UTC)
    loading tasks   5ms  ▇▇▇▇▇▇▇▇▇▇▇ 23%
    copy:styles    16ms  ▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇ 73%
    Total 22ms

Running "autoprefixer:server" (autoprefixer) task
File .tmp/styles/main.css created.

Running "connect:livereload" (connect) task
Started connect web server on http://192.168.1.222:9000

Running "watch" task
Waiting...

■ブラウザで確認
yeomon-angular

■リリース用ファイル作製

$ grunt build
・・・・
$ ll
total 84
drwxrwxr-x  8 vagrant vagrant  4096 Mar 18 01:42 ./
drwxrwxr-x  3 vagrant vagrant  4096 Mar 18 01:19 ../
drwxrwxr-x  6 vagrant vagrant  4096 Mar 18 01:20 app/
drwxrwxr-x 10 vagrant vagrant  4096 Mar 18 01:21 bower_components/
-rw-rw-r--  1 vagrant vagrant   371 Mar 18 01:20 bower.json
-rw-rw-r--  1 vagrant vagrant    38 Mar 18 01:20 .bowerrc
drwxrwxr-x  7 vagrant vagrant  4096 Mar 18 01:43 dist/  <-distが作られる
-rw-r--r--  1 vagrant vagrant   415 Feb 10 19:07 .editorconfig
-rw-r--r--  1 vagrant vagrant    11 Feb 10 19:07 .gitattributes
-rw-r--r--  1 vagrant vagrant    52 Feb 10 19:07 .gitignore
-rw-rw-r--  1 vagrant vagrant 10579 Mar 18 01:39 Gruntfile.js
-rw-r--r--  1 vagrant vagrant   376 Feb 10 19:07 .jshintrc
drwxrwxr-x 30 vagrant vagrant  4096 Mar 18 01:21 node_modules/
-rw-rw-r--  1 vagrant vagrant  1154 Mar 18 01:21 package.json
-rw-rw-r--  1 vagrant vagrant   282 Mar 18 01:20 README.md
drwxrwxr-x  3 vagrant vagrant  4096 Mar 18 01:20 test/
drwxrwxr-x  4 vagrant vagrant  4096 Mar 18 01:42 .tmp/
-rw-r--r--  1 vagrant vagrant   110 Feb  2 19:59 .travis.yml
-rw-r--r--  1 vagrant vagrant     3 Feb 10 19:07 .yo-rc.json

これで準備が整ったのであとはapp以下で開発をおこなっていく。

■参考URL
・Yeoman を使って Webアプリの雛形を作ってみる
http://akiyoko.hatenablog.jp/entry/2014/07/31/024528

・yeomanを使ったAngularJSプロジェクトの始め方 2014年版
http://blog.kinzal.net/post/104245981055/yeoman-angularjs-2014

・エンカフェ
https://www.engcafe.tv/?tag=yeoman

・yeoman+grunt+bowerでangularjs+bootstrap+sass環境構築
http://ryutamaki.hatenablog.com/entry/2014/05/26/003744

・Nodeの複数バージョン管理のお供にnをどうぞ
http://dekokun.github.io/posts/2014-01-01.html

■個人メモ
front_dev

AngularJSでng-repeartを使って動的に入力フォームを作成した時のバリデーション

AngularJSでng-repeartを使って動的に入力フォームを作成した時に
requiredなどのバリデーションが上手く動かなかったのでそのときの対処方法。

あと、plunker使ってみました。

■通常のエラーチェックの例
http://plnkr.co/edit/AXxoyf2ZU5ti7WQDXW70?p=preview

問題が起きたケースはinputタグなどをng-repeartを使って
複数作成した場合にでした。
たとえば以下のような記述です。
※実際にはモーダル画面の中でinputタグの動的追加みたいなことをしてました。

<form name="mainForm">
	<div ng-repeat="title in titles">
		<input type="text "name="title{{ $index }}" ng-model="titles[$index]" required />
		<span ng-show="mainForm.title{{ $index }}.$error.required">Title required!</span>
	</div>
</form>

ただ自分の場合はこれだと値を空にしてもエラーメッセージが表示されませんでした。
ググってみると

Dynamic validation and name in a form with AngularJS
http://stackoverflow.com/questions/14378401/dynamic-validation-and-name-in-a-form-with-angularjs

上記のようなページが見つかり、上記ページ中の↓のように「ng-form」を
利用することで正常にエラーが表示されるようになりました。
formタグの入れ子の利用ということかな?
これでわざわざnameを変えなくてもバリデーションがかけられます。

<form name="outerForm">
<div ng-repeat="item in items">
   <ng-form name="innerForm">
      <input type="text" name="foo" ng-model="item.foo" />
      <span ng-show="innerForm.foo.$error.required">required</span>
   </ng-form>
</div>
<input type="submit" ng-disabled="outerForm.$invalid" />
</form>

ng-form

formディレクティブの入れ子のエイリアスです。 HTMLはフォーム要素の入れ子を許可していません。 例えば、コントロールのサブグループのみの検証が必要なケースで、入れ子のフォームは便利です。

■ng-repeatを使ったフォームのエラーチェック例
http://plnkr.co/edit/1cccXO6UmCi4K6it78Zj?p=preview

 

AngularJS読書メモ

AngularJS読書メモ

娘に歯が生え始めました!

ディレクティブとかサービスとかどこに何を書けばいいのかよくわかってないので、
本で学習。

コントローラはスコープをセットアップする関数。
コントローラで$scopeのメソッドを定義する際に複雑な
ロジックを記述してしまうのではなく、プレゼンテーション
ロジックはフィルターやディレクティブに、ビジネスロジック
はサービスに分離して記述し、コントローラはあくまでも
スコープのセットアップ処理だけに徹するのがいい。

プレゼンテーションロジックを実装する:ディレクティブ、フィルター
ビジネスロジックや共通処理を実装する:サービス
これらを繋ぎ合わせる:コントローラ、スコープ
その他:DI、モジュール管理機能

読んだからといって出来てるわけではないです。。

AngularJSでDragAndDropとSortableとModalを組み合わせてみた

AngularJSでDragAndDropとSortableとModalを組み合わせてみた

あけましておめでとうございます。2015年もぼちぼちブログにメモしていきます。

前回に引き続き、AngularJS。

リストの並べ替えとドラッグ&ドロップでの
リスト要素追加、リスト要素の削除とリスト要素の内容変更を
モーダルウィンドウで行うを組み合わせてみました。
あと一応タッチデバイスでも動くように「jQuery UI Touch Punch」
も読み込んでます。

まだAngularJSがよくわかってないので書き方がおかしい
かもしれませんが晒してみます。

わかった範囲でコメントも記載しています。

おかしなところがあったら指摘をお願いします。。

demo

■index.html

<!DOCTYPE html>
<html ng-app="myapp">
<head>
	<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
	<title>AngularJs drag and drop and sortable sample</title>
	<link rel="stylesheet" type="text/css" href="https://cdnjs.cloudflare.com/ajax/libs/twitter-bootstrap/3.1.0/css/bootstrap.min.css"/>
	<script src="http://ajax.googleapis.com/ajax/libs/jquery/2.0.0/jquery.min.js"></script>
	<script src="http://ajax.googleapis.com/ajax/libs/jqueryui/1.10.2/jquery-ui.min.js"></script>
	<script src="jquery.ui.touch-punch.min.js"></script>
	<script src="http://ajax.googleapis.com/ajax/libs/angularjs/1.1.3/angular.min.js"></script>
	<script src="http://cdnjs.cloudflare.com/ajax/libs/angular-ui/0.4.0/angular-ui.min.js"></script>
	<script src="https://cdnjs.cloudflare.com/ajax/libs/angular-ui-bootstrap/0.10.0/ui-bootstrap-tpls.min.js"></script>
	<script src="app.js"></script>

</head>
<body ng-controller="myCtrl">
	<div class="container" style="margin-top:20px;">
		<h1>AngularJs drag and drop and sortable sample.</h1>

		<div class="row">

			<div class="col-md-4">
				<div class="panel panel-default">
					<div class="panel-heading">
						drag要素
					</div>
					<div class="list-group" ng-model="items">
						<div my-draggable="#sortable" class="list-group-item" ng-repeat="item in items">{{item.title}}</div>
					</div>
				</div>
			</div>

			<div class="col-md-8">
				<div class="panel panel-default">
					<div class="panel-heading">
						sortable要素
					</div>
					<div class="list-group" ng-model="blocks" my-sortable id="sortable">

						<div class="list-group-item" ng-repeat="block in blocks">
							<div>■{{block.title}}</div>
							<button class="btn btn-primary sortable-handle" >並び替え</button>
							<button class="btn btn-success" my-modal-edit>編集</button>
							<button class="btn btn-danger" my-modal-delete>削除</button>
						</div>

					</div>
				</div>
			</div>

		</div>

		<!-- デバッグ用エリア -->
		<div class="row">
			<div class="col-md-4">
				<pre>
					{{items | json }}
				</pre>
			</div>
			<div class="col-md-8">
				<pre>
					{{blocks | json }}
				</pre>
			</div>
		</div>

	</div>

   <!-- Modal for edit -->
    <script type="text/ng-template" id="T_editForm">
        <div class="modal-header">
            <button type="button" class="close" ng-click="$dismiss()">&times;</button>
            <h3>要素編集モーダル</h3>
        </div>
        <div class="modal-body">
            <form role="form">
                <div class="form-group">
                    <label>title</label>
                    <input ng-model="editData.title" type="text" class="form-control" placeholder="タイトル" />
                </div>
            </form>
        	<div ng-show="error">
        		{{error}}
        	</div>
        </div>
        <div class="modal-footer">
            <button type="button" class="btn btn-success" my-update>編集</button>
        </div>
    </script><!-- Modal for edit-->

   <!-- Modal for delete -->
    <script type="text/ng-template" id="T_deleteForm">
        <div class="modal-header">
            <button type="button" class="close" ng-click="$dismiss()">&times;</button>
            <h3>要素削除モーダル</h3>
        </div>
        <div class="modal-body">
        	<div>
        		タイトル:{{block.title}}を削除します。
        	</div>
        </div>
        <div class="modal-footer">
            <button type="button" class="btn btn-success" ng-click="$close()">削除する</button>
        </div>
    </script><!-- Modal for delete-->

</body>
</html>

■app.js

var app = angular.module('myapp', ['ui.bootstrap']);

app.controller('myCtrl', function($scope, $modal) {
    // drag元データ
    $scope.items = [
    {'title' : 'テキストA'},
    {'title' : 'テキストB'},
    {'title' : 'テキストC'},
    {'title' : 'テキストD'},
    {'title' : 'テキストE'},
    {'title' : 'テキストF'},
    {'title' : 'テキストG'},
    {'title' : 'テキストH'},
    {'title' : 'テキストI'},
    ];

    // sortable対象データ -> 初回は本来はサーバから取得する
    $scope.blocks = [
    {'title' : 'デフォルトA'},
    {'title' : 'デフォルトB'},
    {'title' : 'デフォルトC'},
    {'title' : 'デフォルトD'},
    {'title' : 'デフォルトE'},
    {'title' : 'デフォルトF'},
    {'title' : 'デフォルトG'},
    {'title' : 'デフォルトH'},
    {'title' : 'デフォルトI'},
    {'title' : 'デフォルトJ'},
    ];

    // 編修のモーダルインスタンスを保持する用
    $scope.editModalInstance = null;

    // sort処理
    // $on -> $emitや$broadcastで送信したイベントを受け取るメソッド
    $scope.$on('my-sorted',function(event,val){
      $scope.blocks.splice(val.to, 0, $scope.blocks.splice(val.from, 1)[0]);
    })

    // drag処理
    $scope.$on('my-created',function(event,val){
      $scope.blocks.splice(val.to, 0,{title:'#'+($scope.blocks.length+1)+': '+val.title});
    })

    // delete処理
    $scope.$on('my-delete',function(event,val){
      $scope.blocks.splice(val, 1);
    })

    // blockの更新
    $scope.$on('my-update',function(event,val){
      $scope.blocks.splice(val.index, 1, val.data); // 差し替え
    })

  });

// sortableのディレクティブ
app.directive('mySortable',function(){
  return {
    // scope -> scope,
    // el -> ディレクティブが適用された箇所の要素,
    // attrs -> ディレクティブが適用された要素の属性
    link:function(scope,el,attrs){
      // jQuery UI sortableのオプション
      // http://www.jqref.net/ui/interactions/sortable.php
      el.sortable({
        revert: true,
        axis: 'y',
        cancel: "", // 指定しない -> 参考: http://kamegu.hateblo.jp/entry/jquery-ui/sortable-handle
        handle: ".sortable-handle"
      });
      el.disableSelection();

      // deactivate は、アイテムがドラッグ終了した時に呼び出されます。
      // event - イベントオブジェクト
      // ui - ui オブジェクト
      //    item - ドラッグ開始したアイテムの jQuery オブジェクト
      el.on( "sortdeactivate", function( event, ui ) {
        // scope -> 要素に結び付いている$scopeを取得する dragされてきた場合は$indexはundefined
        var from = angular.element(ui.item).scope().$index;
        //console.log('from -> ' + from);

        // jQueryオブジェクト内で、引数で指定されたエレメントのインデックス番号を返す。インデックスは、ゼロから始まる連番。
        // もし渡されたエレメントがjQueryオブジェクト内に存在しない場合、戻り値には-1が返る。
        var to = el.children().index(ui.item);
        //console.log('to -> ' + to);

        if(to>=0){
          // $apply -> AngularJS管理外のイベントハンドラの中で$applyメソッドを実行すると、強制的に実行できる。
          scope.$apply(function(){
            if(from>=0){
              // fromがあるからソート処理  $emit -> 派生元のスコープに対してイベントを送信するメソッド
              scope.$emit('my-sorted', {from:from,to:to}); // イベント呼び出し
            }else{
              // fromが無いから追加処理
              scope.$emit('my-created', {to:to, title:ui.item.text()}); // イベント呼び出し
              ui.item.remove();
            }
          })
        }
      } );
    }
  }
})

// draggableのディレクティブ
app.directive('myDraggable',function(){
  return {
    link:function(scope,el,attrs){
      // jQuery UI draggableのオプション
      // http://www.jqref.net/ui/interactions/draggable.php
      el.draggable({
        connectToSortable: attrs.myDraggable, // connectToSortable を指定すると、sortable にコネクト(項目を追加)することができます。
        helper: "clone",
        revert: "invalid"
    });
    el.disableSelection(); // jQuery UI マッチした要素集合のテキスト選択を無効にします。
    }
  }
})

// 削除のモーダルを開くディレクティブ
app.directive('myModalDelete', function($modal){
  return {
    scope: false, // falseを指定すると、そのディレクティブを利用している箇所のスコープがディレクティブのスコープとして扱える
    link:function(scope,el,attr){
      el.on("click", function( event ){
        scope.$apply(function(){
          var modalInstance = $modal.open({
            templateUrl: "T_deleteForm",
            scope: scope // 呼び出されたときのスコープをそのまま渡しているのでテンプレート側でblockが使える
          });

          // modalInstanceからのコールバックを受ける
          modalInstance.result.then(function() {
            //console.log("モーダル側でcloseが呼ばれた!");
            scope.$emit('my-delete', scope.$index);
          }, function() {
            //console.log("モーダル側でdismissが呼ばれた!");
          });
        })
      });
    }
  }
})

// 編集のモーダルを開くディレクティブ
app.directive('myModalEdit', function($modal){
  return {
    scope: false, // falseを指定すると、そのディレクティブを利用している箇所のスコープがディレクティブのスコープとして扱える
    link:function(scope,el,attr){
      el.on("click", function( event ){
        scope.$apply(function(){
          scope.editData = angular.copy(scope.block); // リアルタイムに編集してしまわないようにコピーする

          // 外部の操作で閉じられるようにモーダルのインスタンスを親スコープに保存しておく
          scope.$parent.editModalInstance = $modal.open({
            templateUrl: "T_editForm",
            scope: scope // 呼び出されたときのスコープをそのまま渡しているのでテンプレート側でblockが使える
          });

          // modalInstanceからのコールバックを受ける
          scope.editModalInstance.result.then(function() {
            //console.log("モーダル側でcloseが呼ばれた!");
          }, function() {
            //console.log("モーダル側でdismissが呼ばれた!");
          });
        })
      });
    }
  }
})

// 編集でデータの更新
app.directive('myUpdate', function(){
  return {
    scope: false, // falseを指定すると、そのディレクティブを利用している箇所のスコープがディレクティブのスコープとして扱える
    link:function(scope,el,attr){
      el.on("click", function( event ){
        scope.$apply(function(){

          // エラーチェック -> サービスに切り出すほうがいいのかも。
          if(!scope.editData.title){
            scope.error = 'タイトルは必須です。';
            return;
          }

          // データの更新
          scope.$emit('my-update', {index:scope.$index, data:scope.editData});
          scope.editData = null; // 念の為消しておく

          // モーダル閉じる
          scope.editModalInstance.close();
        })
      });
    }
  }
})

参考サイト

■Drag object into sortable list – AngularJS
http://stackoverflow.com/questions/18835619/drag-object-into-sortable-list-angularjs
⇒回答者のデモ
http://plnkr.co/edit/aSOlqR0UwBOXgpQSFKOH?p=preview

■Angular.js入門 (5)ディレクティブ その2
http://qiita.com/_rdtr/items/590ccb7be16dc8ed9be3

■AngularJS 製アプリで jQuery を使いたい
http://dev.classmethod.jp/client-side/javascript/angularjsandjquery/

■積極的に利用したい AngularJS グローバル API
http://dev.classmethod.jp/client-side/javascript/angularjsglobalapis/

■AngularJSではじめるHTML5開発 – Part8 モーダルダイアログによる新規レコード作成フォーム
http://www.nkjmkzk.net/?p=5401

■AngularJSでBootstrapのModalを出す方法メモ
http://otiai10.hatenablog.com/entry/2014/10/30/000537

■AngularJSで HTML5的なアラートウィンドウを表示する
http://www.walbrix.com/jp/blog/2014-01-angularjs-simplemodal.html

■jQuery UI Touch Punch
https://github.com/furf/jquery-ui-touch-punch

■AngularJSで、動的に入力項目数を設定する
http://diaryruru.blog.fc2.com/blog-entry-64.html