Falcor+Reactフルスタック（Material-UI）

[Meteor and Reactによるリアクティブシステム「ラーメン野郎を追いかけろ！ ＠ Twitter」を作ってみた](http://qiita.com/sand/items/02219ec5e42cc406ee5c "Meteor and Reactによるリアクティブシステム「ラーメン野郎を追いかけろ！ ＠ Twitter」を作ってみた")

[Falcor+Reactフルスタック（開発環境）](http://qiita.com/sand/items/06571033f99b55fffcb2 "Falcor+Reactのフルスタック（開発環境）")
[Falcor+Reactフルスタック（react-router）](http://qiita.com/sand/items/0a42ada13712abe87711 "Falcor+Reactのフルスタック（react-router）")
[Falcor+Reactフルスタック（views層とcomponents層）](http://qiita.com/sand/items/a969dd786b78554cf9fd "Falcor+Reactのフルスタック（views層とcomponents層）")
[Falcor+Reactフルスタック（formsy-react）](http://qiita.com/sand/items/4466b970f251445e13a4 "Falcor+Reactのフルスタック（formsy-react）")
[Falcor+Reactフルスタック（エラー処理）](http://qiita.com/sand/items/1ef8a19b15ac71c2fb99 "Falcor+Reactのフルスタック（エラー処理）")
[Falcor+Reactフルスタック（Material-UI）](http://qiita.com/sand/items/73d4aea4ae2697bfba2b "Falcor+Reactのフルスタック（Material-UI）")


　今回はMaterial-UIについてです。小池都知事は都民ファーストですが、最近のJavaScriptアプリはモバイルファーストだそうです。ちなみに小沢一郎は「生活が第一」でした。
　従来は、TwitterのBootstrapなどが多くの人に採用されて、モバイルでもPCでもきれいにデザインされたWebアプリが作られてきました。最近はGoogleがMaterial Design Liteというものを公開して、Bootstrapの置き換えに使われてきつつあるようです。それぞれのメリットだけを簡単に言えば、Bootstrapは実績と膨大なリソースがあり、Materialはモダンなデザインが売りなようです。しかし私はBootstrapもMaterialも生で使うには理解が難しいほど複雑で敬遠していました（コピペ文化なんでしょうけど）。しかしReactではどちらも使い易くcomponent化されたライブラリがあり、複雑さが隠蔽され気持ちよく使えるようになっています。その１つがMaterial-UIです。Reactプログラマは、これを使えば複雑で難解な「デザイン」（しかもモバイルファーストです）から解放されるといううわけです。積極的に使いたいものです。
http://www.material-ui.com/


　今回のサンプルプログラムで新しいMaterial-UIのCard componentを導入してみたいと思います。まずはトップのホームページを一見してから、この続きを読まれることをお勧めします。
http://www.mypress.jp:3021/


　前にも述べましたが、トップcomponentのroutes.jsでテーマを設定しています。短いソースなので再度リストします。テーマさえ設定すれば、後は好きな場所で、好きなMaterial-UI componentを使うことが可能になります。


```js:src/routes.js
import React  from 'react';
import {render} from 'react-dom'
import {BrowserRouter} from 'react-router-dom'

//import darkBaseTheme from 'material-ui/styles/baseThemes/darkBaseTheme';
import lightBaseTheme from 'material-ui/styles/baseThemes/lightBaseTheme'; <==テーマをimport
import MuiThemeProvider from 'material-ui/styles/MuiThemeProvider';
import getMuiTheme from 'material-ui/styles/getMuiTheme';
import AppBar from 'material-ui/AppBar';

import App from './App';
import PageNotFound from './components/PageNotFound'

const target = document.getElementById('appRoot');
const node =(
  <MuiThemeProvider muiTheme={getMuiTheme(lightBaseTheme)}> <==テーマでアプリ全体をラッピング
      <BrowserRouter>
        <App />
      </BrowserRouter>
  </MuiThemeProvider>
);
render(node, target);
```


　次に、ホームページを改良します。ホームページには以下のような記事(articles)を掲載することとしましょう。この記事表示のデザインにCardを使うつもりです。


```js:initData.js
[
  {
    name: '山田太郎',
    avatar: 'avatar1.png',
    title: '花火大会',
    content: '今日は隅田川の花火大会でした。とても美しかったです。人が大勢いました。（本当は雨で辛かったけど）'
  },
  {
    name: '鈴木花子',
    avatar: 'avatar2.png',
    title: 'プールで遊んだ',
    content: '東武動物公園のプールに行きました。大きなプールでいっぱいありました。とても楽しかったです。（天気悪くて寒かったけど）'
  },
  {
    name: '魔法使いサリー',
    avatar: 'avatar3.png',
    title: '魔法を使えるとしたら',
    content: 'やはり、夏なので天気をよくする魔法を使いたいです。魔法で温暖化や氷河期の到来も防ぎたいです。'
  }
]
```

　この記事をコマンドラインからMongoDBにimportします

```
mongoimport --db qiita9db --collection articles --jsonArray initData.js --host=127.0.0.1
```

　プログラムの修正です。まずはarticlesコレクションを追加しましたのでMongooseのスキーマを追加します。

```js:server/configMongoose.jsに追加
const articleSchema = {
  name:String,
  avatar:String,
  title:String,
  content:String
};

const Article = mongoose.model('Article', articleSchema, 'articles');
```

　クライアント側のホームページからarticlesを取得しますので、サーバ側のfalcor routerに以下の２つのパスを追加します。


```js:server/routesSession.jsに追加
  {
    route: 'articles.length',
    get: () => Article.count({}, (err, count) => count)
      .then ((articlesCountInDB) => {
        return {
          path: ['articles', 'length'],
          value: articlesCountInDB
        };
      })
  },

  {
    route: 'articles[{integers}]["id","name","avatar","title","content"]',
    get: (pathSet) => {
      const articlesIndex = pathSet[1];
      return Article.find({}, (err, articlesDocs) => articlesDocs)
        .then ((articlesArrayFromDB) => {
          let results = [];

          articlesIndex.forEach((index) => {
            const singleArticleObject = articlesArrayFromDB[index].toObject();
            const falcorSingleArticleResult = {
              path: ['articles', index],
              value: singleArticleObject
            };

            results.push(falcorSingleArticleResult);
          });

          return results;
        });

    }
  },
```

　この２つのパスにクライアント側のホームページからアクセスしてarticlesを取得します。falcor的に言ううとVirtual JSONにアクセスします。以下のようになります。


```js:src/views/HomeApp.js
import React from 'react';
import Falcor from 'falcor';
import falcorModel from '../falcorModel.js';
import ArticleCard from '../components/ArticleCard';

export default class HomeApp extends React.Component {
  constructor(props) {
    super(props);
    this.state = {
      articles: null
    };
  }

  componentWillMount() {
    this._fetch(); 
  }

  async _fetch() {
    const articlesLength = await falcorModel
      .getValue('articles.length')
      .then((length) => length);
 
    const articles = await falcorModel
      .get(['articles', {from: 0, to: articlesLength-1}, ['_id','name','avatar','title', 'content']])
      .then((articles) => articles.json.articles);

      this.setState({articles: articles});
  }

  render () {
    let articlesJSX = [];
    for (let idx in this.state.articles) {
      const article = this.state.articles[idx];
      let currentArticleJSX = (
          <div key={idx}>
            <ArticleCard name={article.name} avatar={article.avatar} title={article.title} content={article.content} />
          </div>
      );
      articlesJSX.push(currentArticleJSX);
    }

    return (
      <div>
        <h1>私のホームページへようこそ !!!</h1>
        <div style={{height: '100%', width: '75%', margin: 'auto'}}>
          {articlesJSX}
        </div>
      </div>
    );
  }
}
```

　HomeApp.jsの修正には２つのポイントがあります。１つ目はfalcorを使ってarticlesを取得していること。２つ目はArticleCardという下層のcomponentを呼び出して、取得したarticlesを描画していること、です。前々回で述べた言葉で言えば、HomeApp.jsはViews層に属し、下層のComponents層のArticleCardを呼び出している。ArticleCardではMaterial-UIのCard componentを利用して具体的な描画を定義している、ということです。

　Material-UIのCard componentは、関係ある複数のコンテンツをまとめて表示するためのものです。このサンプルでは記事タイトル、内容、アバター画像、名前、背景画像などをカードの枠組のなかに表示しています。ArticleCard componentを確認してください。CSSの記述を除けば短いものです。


```js:src/components/ArticleCard.js
import React from 'react';
import { Card, CardHeader, CardMedia, CardTitle, CardText } from 'material-ui/Card';
import { Paper } from 'material-ui';

class ArticleCard extends React.Component {
  constructor(props) {
    super(props);
  }

  render() {
    let {name,avatar,title,content} = this.props;
    let avatar1 = "/"+avatar;

    let paperStyle = {
      padding: 10,
      width: '100%',
      height: 300
    };

    let leftDivStyle = {
      width: '30%',
      float: 'left'
      }

    let rightDivStyle = {
      width: '60%',
      float: 'left',
      padding: '10px 10px 10px 10px'
    }

    return (
      <Paper style={paperStyle}>

        <CardHeader title={name} subtitle="サブタイトル（名前）" avatar={avatar1}/>

        <div style={leftDivStyle}>
          <Card >
            <CardMedia overlay={<CardTitle title={title} subtitle="サブタイトル（画像）" />}>
              <img src="/placeholder.jpg" height="140" />
            </CardMedia>
          </Card>
        </div>

        <div style={rightDivStyle}>
          <div dangerouslySetInnerHTML={{__html: content}} />
        </div>

      </Paper>);
  }
};
export default ArticleCard;
```


　以上で今回の説明は終わりますが、Falcorシリーズはひとまず今回で終わりたいと思います。

　次回は Meteor + React について少し書きたいと思います。