LoginSignup
1
0

More than 5 years have passed since last update.

powerform + validatexを使ってmithril + redux の todo にバリデーションを追加したメモ

Last updated at Posted at 2017-10-31

todoにvalidationを追加する。

概要

前回作った、mithril todo mvcを参考にしたmithril todo reduxにバリデーションを追加する。

herokuに出来たものを公開した。 redux-todo-mithril

環境設定

docker/webpack/Dockerfile
FROM node:8.8.1

# コンテナ上の作業ディレクトリ作成
WORKDIR /app

# 後で確認出来るようにpackage.jsonを作成
RUN npm init -y

# typescript
RUN npm i -D typescript

# tslint
RUN npm i -D tslint
RUN npm i -D tslint-config-airbnb

# typedoc
RUN npm i -D typedoc 

# ビルドツール
RUN npm i -D webpack

# 開発用サーバ
RUN npm i -D webpack-dev-server

# es6用トランスパイラ
RUN npm i -D babel-loader
RUN npm i -D babel-core
RUN npm i -D babel-cli
RUN npm i -D babel-preset-es2015
RUN npm i -D babel-preset-env
RUN npm i -D babel-plugin-transform-react-jsx
# async
RUN npm i -D babel-preset-es2017

# webpack用typescript loader
RUN npm i -D ts-loader

# jsViewライブラリmithril
RUN npm i -S mithril

# フレームワーク
RUN npm i -S redux
RUN npm i -S redux-actions
RUN npm i -S redux-saga
RUN npm i -S redux-logger

RUN npm i -S babel-polyfill

# ルーティング
RUN npm i -S page

# バリデーション
RUN npm i -S validatex
RUN npm i -S powerform

RUN sed -i -e "s/\(\"scripts\": {\)/\1\n    \"tslint\": \"tslint -p 'tsconfig.json' --type-check\",/g" /app/package.json
RUN sed -i -e "s/\(\"scripts\": {\)/\1\n    \"tsc\": \"tsc -p tsconfig.json \",/g" /app/package.json
RUN sed -i -e "s/\(\"scripts\": {\)/\1\n    \"babel\": \"babel\",/g" /app/package.json
RUN sed -i -e "s/\(\"scripts\": {\)/\1\n    \"typedoc\": \"typedoc\",/g" /app/package.json
RUN sed -i -e "s/\(\"scripts\": {\)/\1\n    \"webpack\": \"webpack\",/g" /app/package.json
RUN sed -i -e "s/\(\"scripts\": {\)/\1\n    \"dev-server\": \"webpack-dev-server\", /g" /app/package.json

ソース

src/containers/AddTodo.tsx
import * as m from 'mithril';
import { ClassComponent, Vnode } from 'mithril'; // tslint:disable-line: no-duplicate-imports
import * as powerform from 'powerform';
import { required } from 'validatex';
import { addTodo } from '../actions/todos';
import { connect } from '../mithril-redux';
interface IAttr {}
function mapDispatchToProps(dispatch) {
  return {
    onClick(text: string) {
      dispatch(addTodo(text));
    },
  };
}
interface IDispatch {
  onClick(text: string): void;
}
class AddTodoComponent implements  ClassComponent<IAttr> {
  private value: string;
  private form = powerform({
    todo: [required(true)],
  },                       true);
  public view(vnode) {
    const { onClick } = vnode.attrs.props;
    return (
      <div>
        <input
          className="toggle"
          oninput={m.withAttr('value', value => this.value = value)}
          value={this.value}
        />
        <button
          onclick={
            () => {
              this.form.todo(this.value);
              if (!this.form.isValid()) { return; }
              const val = this.value;
              this.value = '';
              onClick(val); // dispatchのタイミングで画面が更新される。
            }
          }
        >
          Add Todo
        </button>
      </div>
    );
  }
}
export default connect(null, mapDispatchToProps)(AddTodoComponent);
src/containers/EditTodo.tsx
import * as m from 'mithril';
import { ClassComponent, Vnode, VnodeDOM } from 'mithril'; // tslint:disable-line: no-duplicate-imports max-line-length
import { doneEditingTodo, editingTodo } from '../actions/todos';
import { connect } from '../mithril-redux';
import TodoState from '../models/TodoState';

interface IOwnProps {
  id: number;
  text: string;
  editing: boolean;
}

interface IProps extends IOwnProps {
  onDoubleClick: () => void;
  onBlur: (text: string) => void;
}

interface IAttr {
  props: IProps;
}

const mapStateToProps = (store, { text, editing }: IOwnProps) => {
  return { text, editing };
};
const mapDispatchToProps = (dispatch, { id }: IOwnProps) => {
  return {
    onDoubleClick() {
      dispatch(editingTodo(id));
    },
    onBlur(text: string) {
      dispatch(doneEditingTodo(id, text));
    },
  };
};

class EditTodoComponent implements  ClassComponent<IAttr> {
  private value: string;

  public view(vnode: Vnode<IAttr, this>) {
    const { onDoubleClick, onBlur, text, editing } = vnode.attrs.props;
    this.value = text;
    const cancelEditing = () => {
      this.value = text;
      onBlur(text);
    };
    const doneEditing = () => {
      const val = this.value;
      if (val === '') {
        return cancelEditing();
      }
      this.value = '';
      onBlur(val);
    };
    return (
      <div>
        <label ondblclick={onDoubleClick}>
          {text}
        </label>
        <input
          className="edit"
          value={this.value}
          onupdate={
            (node: VnodeDOM<{}, this>) => {
              if (editing) {
                const element = node.dom as HTMLElement;
                element.focus();
              }
            }
          }
          oninput={m.withAttr('value', value => this.value = value)}
          onblur={doneEditing}
          onkeyup={
            (e: KeyboardEvent) => {
              if (e.key === 'Enter') {
                doneEditing();
              } else if (e.key === 'Escape') {
                cancelEditing();
              }
            }
          }
          />
      </div>
    );
  }
}
export default connect(mapStateToProps, mapDispatchToProps)(EditTodoComponent);

この時点のソース

参考

Reduxでのクライアントサイドvalidationをどこでやるべきか?
reselectを用いてReact Reduxにvalidationの仕組みを実装する 1/2
powerform
React + ReduxのプロジェクトでRedux Formを使ったので使い方のまとめと注意点
Modern JavaScript概観、そしてElectronへ
Herokuで静的ページを公開する

1
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
1
0