今宵も我が家の回線は遅い

開発とダーツとにんげんかんさつが好き

JavaScriptフレームワーク、Meteorのチュートリアルを試してみる その1

Meteorを試して使ってみる

Meteorとは

2012年に登場したNode.jsをベースとしたフルスタックフレームワークです。 Webアプリケーションが爆速で開発できるとのことでチュートリアル的なことを今回やってみたいと思います。

今回は公式サイトのチュートリアル「simple-todos with React」をやってみたいと思います。

Meteor公式サイト - Creating an app

準備

今回の環境は以下の通りです。

Meteorのインストール

バージョンによってインストール方法が異なるようなので下手に記事を漁るより 公式サイト - Meteorに従って進めていきましょう。 といっても簡単で以下のコマンドをターミナル上で実行します

curl https://install.meteor.com/ | sh

プロジェクトの作成

インストールが終わったら、次はプロジェクトの作成を行います。

meteor create simple-todos

一度、動かしてみる

ここまできたらとりあえず動かしてみましょう

cd simple-todos

// meteorコマンドでサーバーを起動
meteor

サーバが起動したら確認します。 http://localhost:3000/で確認できるかと思います。 f:id:msk6252:20170821103107p:plain

こんな感じの画面が出れば成功です。 ボタンを押すとカウントしてくれるカウンターアプリが作られています。

React用の準備

React.jsを用いて書いていくのでそちらの準備を行っていきます。 reactとreact-domをインストールします

meteor npm install --save react react-dom

Todoアプリを作成していく

準備が整ったのでTodoアプリを書いていきましょう まずは、client/main.htmlを書き換えていきます。

<head>
  <title>Todo List</title>
</head>
 
<body>
  <div id="render-target"></div>
</body>

これだけでOKです。

次にclient/main.jsを書き換えます。 一度、client/main.jsを削除し、再度client/main.jsを作成しましょう。 中身のコードは以下の通りです。

import React from 'react';
import { Meteor } from 'meteor/meteor';
import { render } from 'react-dom';
 
import App from '../imports/ui/App.jsx';
 
Meteor.startup(() => {
  render(<App />, document.getElementById('render-target'));
});

コンポーネントを作成

Reactを学んでいない方は少しわからないかもしれないですが、コンポーネントというものを作成していきます。簡単にいうと部品ごとにコードを分割し、コンポーネントにすることで可読性や生産性の向上を図ります。

まずは、コンポーネントディレクトリを作ります

mkdir imports
mkdir imports/ui

まずは大元であるAppコンポーネントを作成していきます。

imports/ui/App.jsxです。

import React, { Component } from 'react';
 
import Task from './Task.jsx';
 
// App component - represents the whole app
export default class App extends Component {
  getTasks() {
    return [
      { _id: 1, text: 'This is task 1' },
      { _id: 2, text: 'This is task 2' },
      { _id: 3, text: 'This is task 3' },
    ];
  }
 
  renderTasks() {
    return this.getTasks().map((task) => (
      <Task key={task._id} task={task} />
    ));
  }
 
  render() {
    return (
      <div className="container">
        <header>
          <h1>Todo List</h1>
        </header>
 
        <ul>
          {this.renderTasks()}
        </ul>
      </div>
    );
  }
}

次は、タスク1つ1つに関するコンポーネントが必要なので作っていきます imports/ui/Task.jsxです

import React, { Component, PropTypes } from 'react';
 
// Task component - represents a single todo item
export default class Task extends Component {
  render() {
    return (
      <li>{this.props.task.text}</li>
    );
  }
}
 
Task.propTypes = {
  // This component gets the task to display through a React prop.
  // We can use propTypes to indicate it is required
  task: PropTypes.object.isRequired,
};

CSSも追加する

ここまででも動きますがせっかくなのでCSSでスタイリングしていきたいと思います。 client/main.cssに記述していきます。

/* CSS declarations go here */
body {
  font-family: sans-serif;
  background-color: #315481;
  background-image: linear-gradient(to bottom, #315481, #918e82 100%);
  background-attachment: fixed;
 
  position: absolute;
  top: 0;
  bottom: 0;
  left: 0;
  right: 0;
 
  padding: 0;
  margin: 0;
 
  font-size: 14px;
}
 
.container {
  max-width: 600px;
  margin: 0 auto;
  min-height: 100%;
  background: white;
}
 
header {
  background: #d2edf4;
  background-image: linear-gradient(to bottom, #d0edf5, #e1e5f0 100%);
  padding: 20px 15px 15px 15px;
  position: relative;
}
 
#login-buttons {
  display: block;
}
 
h1 {
  font-size: 1.5em;
  margin: 0;
  margin-bottom: 10px;
  display: inline-block;
  margin-right: 1em;
}
 
form {
  margin-top: 10px;
  margin-bottom: -10px;
  position: relative;
}
 
.new-task input {
  box-sizing: border-box;
  padding: 10px 0;
  background: transparent;
  border: none;
  width: 100%;
  padding-right: 80px;
  font-size: 1em;
}
 
.new-task input:focus{
  outline: 0;
}
 
ul {
  margin: 0;
  padding: 0;
  background: white;
}
 
.delete {
  float: right;
  font-weight: bold;
  background: none;
  font-size: 1em;
  border: none;
  position: relative;
}
 
li {
  position: relative;
  list-style: none;
  padding: 15px;
  border-bottom: #eee solid 1px;
}
 
li .text {
  margin-left: 10px;
}
 
li.checked {
  color: #888;
}
 
li.checked .text {
  text-decoration: line-through;
}
 
li.private {
  background: #eee;
  border-color: #ddd;
}
 
header .hide-completed {
  float: right;
}
 
.toggle-private {
  margin-left: 5px;
}
 
@media (max-width: 600px) {
  li {
    padding: 12px 15px;
  }
 
  .search {
    width: 150px;
    clear: both;
  }
 
  .new-task input {
    padding-bottom: 5px;
  }
}

コレクションを使う

Todoリストを作成しても、保存できなければ意味がありません。なのでコレクションを使用し、保存できるようにしましょう。 ディレクトリをつくってやります

mkdir imports/api

imports/apiの中に、tasks.jsファイルを作成します。

import { Mongo } from 'meteor/mongo';
 
export const Tasks = new Mongo.Collection('tasks');

先程作ったファイルをサーバで使うように読み込みます。 server/main.jsに追記していきます。

 import '../imports/api/tasks.js';

コレクションを使うにはモジュールのインストールが必要なので行います。

meteor npm install --save react-addons-pure-render-mixin
meteor add react-meteor-data

コレクションを使うには、先程つくったApp.jsxを編集する必要があります。

import React, { Component } from 'react';

//追記
import { createContainer } from 'meteor/react-meteor-data';

//追記
import { Task } from '../api/tasks.js';

//追記
import Task from './Task.jsx';

class App extends Component {
  renderTasks() {
    return this.getTasks().map((task) => (
      <Task key={task._id} task={task} />
    ));
  }

  render() {
    return (
      <div className="container">
        <header>
          <h1>Todo List</h1>
        </header>

        <ul>
          {this.renderTasks()}
        </ul>
      </div>
    );
  }
}

//追記
App.propTypes ={
    tasks: PropTypes.array.isRequired,
};
//追記
export default createContainer(() => {
  return {
    tasks:Tasks.find({}).fetch(),
  };
},App);

コレクションにデータを入れていきます。 ターミナル2つ使い、1つはmeteorコマンドでサーバを起動 もう1つはmeteor mongoでコンソールを立ち上げます

//1つ目のターミナル
meteor

//2つ目のターミナル
meteor mongo

データをインサートする

// 2つ目のターミナル コレクションのコンソールが起動した状態で
db.tasks.insert({ text: "Hello world!", createdAt: new Date() });

Todoを追加するフォームを記述していきます imports/ui/App.jsx

import React, { Component, PropTypes } from 'react';

// 追記
import ReactDOM from 'react-dom';
import { createContainer } from 'meteor/react-meteor-data';

import { Tasks } from '../api/tasks.js';

import Task from './Task.jsx';

class App extends Component {

// 追記 フォーム入力確定後に実行される関数
  handleSubmit(event){
   event.preventDefault();

   const text = ReactDOM.findDOMNode(this.refs.textInput).value.trim();
   Tasks.insert({
    text,
    createdAt: new Date(),
   });

   ReactDOM.findDOMNode(this.refs.textInput).value = '';
  }

  renderTasks() {
    return this.props.tasks.map((task) => (
      <Task key={task._id} task={task} />
    ));
  }

  render() {
    return (
      <div className="container">
        <header>
          <h1>Todo List</h1>

// フォームの追記
          <form className="new-task" onSubmit={this.handleSubmit.bind(this)} >
             <input
               type="text"
               ref="textInput"
               placeholder="Type to add new tasks"
             />
          </form>
        </header>

        <ul>
          {this.renderTasks()}
        </ul>
      </div>
    );
  }
}

App.propTypes ={
    tasks: PropTypes.array.isRequired,
};

export default createContainer(() => {
  return {
    // 書き換え
    tasks:Tasks.find({}, {sort: { createdAt: -1} }).fetch(),
  };
},App);

実行してみる

まだすべて実装が終わっていませんが、実行してみましょう。 ちなみにmeteorコマンドでサーバを起動した状態でコードを修正すると自動でサーバをリスタートしてくれます。また、ブラウザも更新してくれます。

// サーバ起動
meteor

ページにアクセスする際は、http://localhost:3000でいけると思います。 f:id:msk6252:20170822001528p:plain

こんな感じのTodoリストができていたらOKです。 リストの追加や終わったリストへのチェック、項目の削除ができるので動作を確認しておきましょう。

今回のまとめ

今回はここまでにします。

今回は

  • Meteorのインストール
  • Meteorで動かす
  • ToDoアプリを作成する
    • Reactで書くための準備
    • とりあえずのTodoリストを書く
    • コレクションの設定
    • フォームの設置

今後も書いていきます。

思ったよりチュートリアル長い。。。