読者です 読者をやめる 読者になる 読者になる

the industrial

都内で働くITエンジニアの日記

Riot.js を利用してTODOアプリケーション構築のハンズオンを作成しました #riotjs

program

デモ

http://omiend.github.io/riotjs_todo/

はじめに

Riot.jsとは

関連するHTMLとJavaScriptを結合し、再利用可能なコンポーネントとしてまとめたカスタムタグを利用してアプリケーションを構築することができる、超軽量Webフレームワークです。

http://riotjs.com/ja/

カスタム・タグ

Viewと対になるScriptを、一つのタグに定義したものです。

HTML
└── Custom Tag
    ├── Custom Tag
    │   ├── HTML
    │   └── Script
    ├── HTML
    └── Script

コンパイラ

インブラウザ・コンパイルとプリコンパイルから選べます。 このハンズオンでは環境構築を簡素化するために、表示時にコンパイルされるインブラウザ・コンパイルを採用しています。

http://riotjs.com/ja/api/compiler/

私は普段、Webpackでプリコンパイル(&トランスパイル)させて、BundleされたJSを読み込むようにしています。

その際に利用する webpack.config.js 例です。

var path = require('path');
var webpack = require('webpack');

module.exports = {
  entry: {
    index: path.join(__dirname, 'src/app.js')
  },
  output: {
    path: path.join(__dirname, 'public/scripts'),
    filename: 'app.js',
    library: 'Router'
  },
  plugins: [
    new webpack.ProvidePlugin({
      riot: 'riot'
    })
  ],
  module: {
    loaders: [
      { 
        test: /\.tag$/, 
        exclude: /node_modules/, 
        loader: 'riotjs-loader'
      },
      { 
        test: /\.json$/, 
        exclude: /node_module]s/, 
        loader: 'json-loader'
      },
      { 
        test: /\.js$/,
        exclude: /node_modules/, 
        loader: 'babel-loader' 
      }
    ]
  }
};

ちなみに、 package.json はこんな感じ。

{
  "name": "riot_twitter",
  "version": "1.0.0",
  "description": "",
  "dependencies": {},
  "devDependencies": {
    "babel": "^6.5.2",
    "babel-core": "^6.13.2",
    "babel-loader": "^6.2.4",
    "babel-preset-es2015": "^6.13.2",
    "json-loader": "^0.5.4",
    "riot": "^2.5.0",
    "riot-router": "^0.8.0",
    "riotjs-loader": "^3.0.0",
    "webpack": "^1.13.1",
    "webpack-dev-server": "^1.14.1"
  },
  "scripts": {
    "watch": "webpack --progress --color --watch"
  },
  "author": "omiend"
}

ルーティング

ルーティングにはriot.routeを利用します。

下記アクションでURLが変更されると、与えられたcallback関数を実行します。

  • 1.新しいURLが、ブラウザのロケーションバーに入力されたとき
  • 2.ブラウザの戻る/進むボタンが押されたとき
  • 3.route(to)が呼び出されたとき
  • 4.アンカータグがクリックされたとき

当TODOアプリにおいては、URLの変更を検知→URLの状態を受け取り、callback関数にてカスタム・タグをマウントさせています。

// 例えば、‘todo/1000/edit’にアクセスすると
// collection=‘todo’; id=‘1000’; action=’edit’がバインドされる
riot.route(function(collection, id, action){
  riot.mount('route', collection || 'home', {id: id})
})

参考までに、私は普段riot-routerを利用しています。

riot-router

こちらも気になっています。

riot-routehandler

ハンズオン

注意点

独学なので、Riot.jsのお作法に則っていない可能性があります(お作法がアレば、の話ですが)

Global変数をそのまま触っており(良くない)、JavaScriptについては割りと乱暴にお手軽に実装しています。

※observableを利用して、fluxを取り入れるべきだなぁとは感じています。

また、ローカル環境においては、カスタムタグを外部ファイルとして読み込むため

<script type="riot/tag" src="tags/home.tag"></script>

Google Chromeで起動すると下記のエラーが発生し、正常に動作しない可能性があります。

その際は、Google Chrome起動時に--allow-file-access-from-filesを引数を指定して起動するか、Firefoxなどの別のブラウザでお試し下さい。

riot+compiler.min.js:2 XMLHttpRequest cannot load file://hoge-hoge/riotjs_todo/tags/home.tag. Cross origin requests are only supported for protocol schemes: http, data, chrome, chrome-extension, https, chrome-extension-resource.

Python(MacOSならば標準搭載)を利用し、SimpleServerを利用する手もあります。

知らなかったPythonのSimpleServer

ローカル環境で動かしましょう

git clone git@github.com:omiend/riotjs_todo.git でもどうぞ
  • index.htmlをブラウザで開く

構成

.
├── css
│   ├── main.css      // ページの整形用に
│   ├── normalize.css // https://necolas.github.io/normalize.css/
│   └── skeleton.css  // わたくしomiendが好きな、軽量CSSフレームワークです( http://getskeleton.com/)
├── images
│   └── riotjs.png    // riotをイメージした画像
├── index.html         // ブラウザが一番初めに読み込む単純なHTMLファイルです
├── scripts
│   └── app.js        // ルーティングと、予め登録されたデータを格納しています
└── tags               // カスタム・タグを配置しているディレクトリです
      ├── add.tag      // TODOデータの追加を行うカスタム・タグです
      ├── commands.tag // TODOデータに対して何らかの処理を行うコマンドを集めたカスタム・タグです
      ├── edit.tag     // TODOデータを更新するためのフォームが定義されたカスタム・タグです
      ├── home.tag     // ホーム画面を表示するための簡単なHTMLが定義されたカスタム・タグです
      ├── navi.tag     // ナビバーの単純なHTMLが定義されたカスタム・タグです
      └── todo.tag     // TODOデータを一覧したりなど、TODOアプリの基盤画面を定義したカスタム・タグです

index.html

ブラウザで一番初めに読み込むファイルです。

内容は単純なHTMLですが、下記の要素が定義されています。

<!-- カスタムタグがマウントされるセレクター 名称はrouteでなくても良いです -->
<route></route>
<!-- Riot.js本体と、コンパイラをCDNで読み込み -->
<script src="https://cdn.jsdelivr.net/riot/2.6/riot+compiler.min.js"></script>
<!-- ルーティングなどの読み込み -->
<script src="scripts/app.js" defer></script>

tag/navi.tag

こちらは本当に単純なHTMLです。

コメントアウトを解除し、画面にナビバーが表示されることを確認してください。 正しく表示されていれば、naviタグが正しくマウントされているということです。

tag/home.tag

ルーティング設定によって、「/」にアクセスされた時ににマウントされるタグです。

同タグ内のScriptにて‘Riot.js’という値を返す関数「echo()」が定義されています。

I Love {this.echo()} !
」にて当該関数を実行し、 ‘Riot.js’を受け取ってレンダリングしています。

単純な例ですが、カスタム・タグの基本がわかると思います。

tag/todo.tag

TODOアプリケーションの基盤的なカスタム・タグです。

まず、riotが提供するeachによってTODOデータをループし、表示しています。

<div class="row" each={todo in store.todos}>

一行ごとにcommandsタグをマウントさせています。

commandsタグはtodoという名前を引数として、ループ中のtodoを受け取っています。

<commands todo={todo}></commands>

tag/commands.tag

todoタグから受け取ったTODOデータは、optsというriotが提供する特別な変数に格納されます。

optsは、Reactで言う所のpropsのイメージです。

<div if={opts.todo.done}><!-- doneがtrueだったらレンダリング -->

commandsタグは、TODOデータに対してアクションを行うボタンを提供するために実装したカスタム・タグです。

editTagをにマウントさせるために呼び出される関数。 フラグを更新する関数。 TODOデータを削除する関数を定義しています。

一つのカスタム・タグが、各行で各TODOデータのみを利用する動きとなっており、コンポーネントの再利用を意識した作りとしています。

tag/add.tag

addタグがマウントされるはtodoタグ内に定義されているので、addタグは子タグと呼べます。

子タグは親タグ(todoタグ)がマウントされた後に初期化されます。

TODO内容を入力し、ADDボタンがクリックされると、addタグに定義されたadd関数が実行され、storeに入力内容を保存します。

実はその後の「 store.trigger('refresh') 」が大切な処理です。

sroreはapp.jsにてobservableのオブジェクトとしており、add実行時にtriggerを実行することによって refresh イベントを発火させています。

riot.observable(store)

storeに対する refresh イベントが発火されると、todoタグに定義された下記のcallback関数が実行され、todoタグが自身を更新し、TODO一覧に追加したTODOデータが表示されるという仕組みになっています。

var self = this // callbackの中でtodoタグを参照するため
store.on('refresh', function(){
  self.update() // todoタグを更新
})

オブザーバブル(Observable)について http://riotjs.com/ja/api/observable/

tag/edit.tag

これまでの説明以上の目新しいことはしていません。

コメントアウトを解除して、更新画面が表示されることを確認してください。

commandsタグからリクエストパラメータでeditタグを呼び出し・・・

edit() {
  riot.route(`edit/${opts.todo.id}`)
}
riot.route(function(collection, id, action){
  // collection = edit
  // id = commandsタグの${opts.todo.id}で取得されたTODOデータのID
  riot.mount('route', collection || 'home', {id: id})
})

TODOデータのIDを opts を利用して受け取っています。

opts.form = store.todos.filter(function(s){return s.id == opts.id})[0]

参考にさせていただいたモノ

基礎

Router周り

モンスターズ

movies

モンスターズ/地球外生命体(字幕版)

 

遂にAmazon primeに入る決断笑

 

早速いくつか映画を観てるのだけど、何時でも手軽に映画が見放題ってのはかなり危険。観過ぎちゃいそうだから笑

 

前から少し気になっていた、地球に侵略してきたエイリアンが生息するメキシコの危険地帯から脱出すると言う映画。

 

エイリアンはかなりデカイタコの様な生物で、生態は原始的。

 

脱出は脱出なのだけど、この映画の醍醐味は「あまりエイリアンとの戦闘がなく、脱出のために旅をすると言う、ロードムービー要素がある映画」だという事。

 

 

ロードムービーは凄く好きなジャンルで、そこにさらにエイリアン侵略という非現実感が入ってなかなかの名作に仕上がっていると感じた。

 

例えばこの映画に出てくるエイリアンが文明を持ったビームをバンバン撃つような奴らだったとしたら、大した映画では無いんだろうなと。

 

音楽も物静か。

 

ズートピア

movies

ズートピア (吹替版)

 

嫁ちゃんが欲しがり、娘ちゃんも観るかなという事でBlu-rayで購入。かなり人気の一本で、納得。とても面白かった。

 

ストーリーはありがちな気はするけど、仕事に行き詰まった大人が観ると一番響くのかな?CGも凄くて、毛の質感がフワフワ🐏

 

なので、娘ちゃんにはまだお話が難しいみたい。でも、SHAKIRAが演じる?ガゼルのライブシーンがお気に入りで、何度も何度も繰り返し、そしてダンスしながら観てるのが可愛いくて良かった笑

 

映画の内容とは関係無いけど。

 

デッドプール

movies

デッドプール (字幕版)

無責任ヒーローというキャッチコピーは、なんだか違う気がする笑

グロさはちょっとあるけど、スタイリッシュなアクションとそのキャラクターがいい味を出していて、おバカお映画なのにシリアスなラブストーリー要素もあって、なかなかおもしろかったデス。

知らなかったPythonのSimpleServer

program

まず、MacOSにはデフォルトでPythonが入っていること。

そしてコマンド一発でローカルサーバーが立っちゃう事。

pythonの確認

$ which python
/usr/bin/python

例えば、下記の様なディレクトリがあったとして、当該ディレクトリをカレントにして・・・

$ cd hoge
$ ls hoge
index.html

下記コマンドを撃ちこめば、ブラウザでlocalhost:8000にアクセスした際にindex.htmlが表示される

$ python -m SimpleHTTPServer
Serving HTTP on 0.0.0.0 port 8000 ...

ポートはデフォルトで8000なのだけど、オプション指定もできる

$ python -m SimpleHTTPServer 8080
Serving HTTP on 0.0.0.0 port 8080 ...

python -m SimpleHTTPServer はPython2系のコマンドで、Python3系だと python -m http.server らしい

こっちの方がわかりやすい(試してないけど)

ロックンローラ

movies

ロックンローラ [DVD]

夜中にテレビでやっていた映画

 

なんで録画したのかわからないけど、ジェラルド・バトラーが主演だからかな

 

あと、トム・ハーディーが出演してるの知らなくて、なんか得した気分

 

そして、監督がガイ・リッチーだったのもスタッフロールで知ると言う

 

ガイ・リッチーと言えば俺の中ではスナッチ

 

なるほど確かにこの映画はスナッチに似てる気がする

 

つまり、とても面白かった!

 

タイトルから想像してた内容とは違ったけど

 

 

オデッセイ

movies

オデッセイ(字幕版)

 

マットデイモンさん主演

 

事故で火星に取り残され、サバイバルをするハメに

 

幸い植物学者なのと、ジャガイモの種芋があるので、栽培してみたら…

 

ちょっと強引に話を展開させるのがアメリカンだったw

 

面白かった