Step5: メモの作成・削除ボタンを実装しよう!

このステップではMeteorコレクション追加・削除の仕方を学びます。

メモの新規作成

前のステップではWebインスペクタからメモデータを追加しました。今度はメモの新規作成ボタンを追加してみましょう。

AppLayoutとAppContainerを以下のように書き換えましょう。

imports/ui/layouts/AppLayout.js

import React from 'react';
import Header from '../components/Header';
import MemoList from '../components/MemoList';

export default class AppLayout extends React.Component {
  constructor(props) {
    super(props);
    this.onClick = this.onClick.bind(this);
  }

  onClick() {
    const { memos, createMemo } = this.props;
    createMemo(`New memo ${memos.length + 1}`);
  }

  render() {
    const { memos } = this.props;
    return (
      <div className="container">
        <Header />
        <button className="add-button" onClick={this.onClick}>Add</button>
        <MemoList memos={memos} />
      </div>
    );
  }
}

AppLayout.propTypes = {
  memos: React.PropTypes.array.isRequired,
  createMemo: React.PropTypes.func.isRequired,
};

imports/ui/containers/AppContainer.js

import AppLayout from '../layouts/AppLayout';
import { Memos } from '../../api/memos/memos';
import { createContainer } from 'meteor/react-meteor-data';

const createMemo = content => {
  Memos.insert({content});
};

export default createContainer(() => {
  return {
    createMemo,
    memos: Memos.find().fetch(),
  };
}, AppLayout);

ポイント

  • Memosコレクションのデータを管理しているAppContainerでメモの新規作成を行うcreateMemo()を定義し、子コンポーネントのAppLayoutに渡す
  • AppLayoutはpropsを参照して、新規追加ボタンのイベント(onClick)を検知し、createMemo()を呼び出す

コードを書き換えたら、保存してWebブラウザを見てみましょう。

Addボタンが追加されていますので、何度か押してみてください。

Addボタンを押すたびに、新しいMemoデータが追加されます。

メモの削除

続いて、メモの削除機能を実装します。

まずAppContainerでremoveMemo() を定義し、AppLayoutに渡します。

imports/ui/containers/AppContainer.js

import AppLayout from '../layouts/AppLayout';
import { Memos } from '../../api/memos/memos';
import { createContainer } from 'meteor/react-meteor-data';

const createMemo = content => {
  Memos.insert({content});
};

const removeMemo = memoId => {
  Memos.remove({_id: memoId});
};

export default createContainer(() => {
  return {
    memos: Memos.find().fetch(),
    createMemo,
    removeMemo,
  };
}, AppLayout);

続いて、AppLayout, MemoList経由でMemoItemにremoveMemo()を渡します。

imports/ui/layouts/AppLayout.js

import React from 'react';
import Header from '../components/Header';
import MemoList from '../components/MemoList';

export default class AppLayout extends React.Component {
  constructor(props) {
    super(props);
    this.onClick = this.onClick.bind(this);
  }

  onClick() {
    const { memos, createMemo } = this.props;
    createMemo(`New memo ${memos.length + 1}`);
  }

  render() {
    const { memos, removeMemo } = this.props;
    return (
      <div className="container">
        <Header />
        <button className="add-button" onClick={this.onClick}>Add</button>
        <MemoList memos={memos} removeMemo={removeMemo} />
      </div>
    );
  }
}

AppLayout.propTypes = {
  memos: React.PropTypes.array.isRequired,
  createMemo: React.PropTypes.func.isRequired,
  removeMemo: React.PropTypes.func.isRequired,
};

imports/ui/components/MemoList.js

import React from 'react';
import MemoItem from './MemoItem';

export default class MemoList extends React.Component {
  render() {
    const { memos, removeMemo } = this.props;
    return (
      <div className="memo-list">
        {memos.map(memo => (
          <MemoItem key={memo._id} memo={memo} removeMemo={removeMemo}/>
        ))}
      </div>
    );
  }
}

MemoList.propTypes = {
  memos: React.PropTypes.array.isRequired,
  removeMemo: React.PropTypes.func.isRequired,
};

そしてMemoItemコンポーネントに削除ボタンを追加し、削除ボタンのonClickremoveMemo()を設定します。

imports/ui/components/MemoItem.js

import React from 'react';

export default class MemoItem extends React.Component {
  constructor(props) {
    super(props);
    this.onClick = this.onClick.bind(this);
  }

  onClick(event) {
    event.preventDefault();
    const { memo, removeMemo } = this.props;
    removeMemo(memo._id);
  }

  render() {
    const { memo } = this.props;
    return (
      <div className="memo-item">
        <a href="#" onClick={this.onClick} className="remove-button">
          &times;
        </a>
        <textarea className="textarea" defaultValue={memo.content} />
      </div>
    );
  }
}

MemoItem.propTypes = {
  memo: React.PropTypes.object.isRequired,
  removeMemo: React.PropTypes.func.isRequired,
};

コードを書き換えたらブラウザで削除ボタンを試してみましょう!

メモの並び替え(作成日降順)

現在はメモを追加すると末尾に追加されます。少しわかりにくいですね。

新しいメモが作成日降順で表示されるように、並び替えましょう。

Memosコレクションのinsert時に作成日時を表すcreatedAtフィールドを自動で追加するように変更します。

imports/api/memos/memos.js

import { Mongo } from 'meteor/mongo';

class MemosCollection extends Mongo.Collection {
  insert(doc, callback) {
    doc.createdAt = doc.createdAt || new Date();
    const result = super.insert(doc, callback);
    return result;
  }
}

export const Memos = new MemosCollection('Memos');

// for debug
if (Meteor.isDevelopment) {
  global.collections = global.collections || {};
  global.collections.Memos = Memos;
}

ポイント

  • Mongo.Collection を extends したES2015 classを作成する
  • 必要に応じて insert, update, remove をオーバーライドする

これでMemosコレクション追加時にcreatedAtフィールドが自動的に追加されるようになりました。

あとは、createdAt降順でメモを表示するようにAppContainerのfind()メソッドにオプションを設定します。

imports/ui/containers/AppContainer.js

import AppLayout from '../layouts/AppLayout';
import { Memos } from '../../api/memos/memos';
import { createContainer } from 'meteor/react-meteor-data';

const createMemo = content => {
  Memos.insert({content});
};

const removeMemo = memoId => {
  Memos.remove({_id: memoId});
};

export default createContainer(() => {
  return {
    memos: Memos.find({}, {sort: {createdAt: -1}}).fetch(),
    createMemo,
    removeMemo,
  };
}, AppLayout);

コードを書き換えたらWebブラウザで動作確認します。

メモが作成日降順で表示されるようになりました。

次のステップに進みましょう。

次へ