JavaScriptでPythonのrange関数のようにリスト作成

Pythonのrange関数のように開始値と終わり値を指定してリストを作成する関数を毎回書きたくないのでモジュール化。

Pythonのrange関数

range([start, ]stop[, step])

>>> range(3,10)
[3, 4, 5, 6, 7, 8, 9]
>>> range(3,10,2)
[3, 5, 7, 9]

これをJavaScriptでもやりたい

JavaScriptでほぼ等価の関数

var jsrange = function (start, end, step) {
  var array = new Array();
  var step = step === undefined ? 1 : step;
  var counter = start;
  var index = 0;

  while (counter < end) {
    array.push(counter);
    counter += step;
  }

  return array;

}

module.exports = jsrange;

インストール

% npm install jsranger

使用例

var jsranger = require('jsranger');

var L = jsranger(2, 20);
console.log(L);
  // should display the following
  // [ 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19 ]

var l = jsranger(2, 20, 2);
console.log(l);
  // should display the following
  // [ 2, 4, 6, 8, 10, 12, 14, 16, 18 ]

課題

数字以外の者が引数に来た時のエラーなど、引数のチェックを全くしてない

Reactでcheckboxの状態をステートで管理する

React (Facebook): managed state of controlled checkboxesのパクリと自分の理解のためのメモ

index.htmlの準備

<!DOCTYPE html>
<html>
  <head>
  ¦ <meta charset="UTF-8" />
  ¦ <script src="https://cdnjs.cloudflare.com/ajax/libs/react/0.14.3/react.min.js"></script>
  ¦ <script src="https://cdnjs.cloudflare.com/ajax/libs/react/0.14.3/react-dom.min.js"></script>
  ¦ <script src="https://cdnjs.cloudflare.com/ajax/libs/babel-core/5.8.23/browser.min.js"></script>
  </head>
  <body>
  ¦ <div id="content"></div>
    <script type="text/babel">
    // この中にReact書く
    </script>
  </body>
</html>

ReactクラスとState定義

2つあるinputのcheckboxがそれぞれチェックされているかどうかの状態を持つためのstateを準備。ついでにReactDOMレンダーも定義。

var CheckboxDemo = React.createClass({
  getInitialState: function () {
    return {
      data: [
        {id: 1, selected: false },
        {id: 2, selected: false }
      ]
    };
  },
  // この下にFormを書く
});
ReactDOM.render(
  <CheckboxDemo />,
  document.getElementById('content')
);

Form作成

Formの要素としてchecksを定義する。mapの中でCheckboxDemoオブジェクトにthisでアクセスできるようにbindしておく

render: function () {
  var checks = this.state.data.map(function (d) {
    return (
      <div>
        <input type="checkbox" checked={d.selected} onChange={this.__changeSelection.bind(this, d.id)} />
          {d.id}
          <br />
      </div>
    );
  }.bind(this));
  <form>
    {checks}
  </form>
}

まだ理解できてないところとして__changeSelection.bind(this, d.id)のthisはCheckboxDemoなのかdなのか

changeSelection定義

チェックボックスが押された時の__changeSelection関数を定義。チェックボックスが押されたらidを__changeSelectionを渡して呼び出して__changeSelectionの中でdataの中を全部チェックして、渡したidとdataのidが一致したらselectedの真偽値を変更してdataを丸ごと変更かける

__changeSelection: function (id) {
  var nextState = this.state.data.map(function (d) {
    return {
      id: d.id,
      selected: (d.id === id ? !d.selected: d.selected)
    };
  });

  console.log(nextState); // 確認用
  this.setState( {data: nextState });
},

全部チェックを実装

form内を修正

<form>
  <input type="checkbox" ref="globalSelector" onChange={this.__changeAllChecks } />CheckAll
  <br />
  {checks}
</form>

changeAllChecksの定義

globalSelectorのチェックボックスがチェックされているかどうかをgetDOMNodeでとってきてchecksに入れて、checksをdataの全てのselectedに反映する

__changeAllChecks: function () {
  var chekcs = this.refs.globalSelector.getDOMNode().checked;
  var nextState = this.state.data.map(function (d) {
    return {id: d.id, selected: chekcs };
  });

  console.log('change all checks -> ', nextState)
  this.setState( { data: nextState })
}

全体コード

<!DOCTYPE html>
<html>
  <head>
    <meta charset="UTF-8" />
    <script src="https://cdnjs.cloudflare.com/ajax/libs/react/0.14.3/react.min.js"></script>
    <script src="https://cdnjs.cloudflare.com/ajax/libs/react/0.14.3/react-dom.min.js"></script>
    <script src="https://cdnjs.cloudflare.com/ajax/libs/babel-core/5.8.23/browser.min.js"></script>
  </head>
  <body>
    <div id="content"></div>
    <script type="text/babel">

    var CheckboxDemo = React.createClass({
      getInitialState: function () {
        return {
          data: [
            {id: 1, selected: false },
            {id: 2, selected: false }
          ]
        };
      },

      render: function () {
        var checks = this.state.data.map(function (d) {
          return (
            <div>
              <input type="checkbox" checked={d.selected} onChange={this.__changeSelection.bind(this, d.id)} />
                {d.id}
                <br />
            </div>
          );
        }.bind(this));
        return (
          <form>
            <input type="checkbox" ref="globalSelector" onChange={this.__changeAllChecks } />CheckAll
            <br />
            {checks}
          </form>
        )
      },

      __changeSelection: function (id) {
        var nextState = this.state.data.map(function (d) {
          return {
            id: d.id,
            selected: (d.id === id ? !d.selected: d.selected)
          };
        });

        console.log(nextState);
        this.setState( {data: nextState });
      },

      __changeAllChecks: function () {
        var chekcs = this.refs.globalSelector.getDOMNode().checked;
        var nextState = this.state.data.map(function (d) {
          return {id: d.id, selected: chekcs };
        });

        console.log('change all checks -> ', nextState)
        this.setState( { data: nextState })
      }

    });



    ReactDOM.render(
      <CheckboxDemo />,
      document.getElementById('content')
    );
    </script>
  </body>
</html>

ReactをGulpでbabelifyするとUnexpected tokenでこけたら

React書いてGulpでビルドしようとしたらUnexpected tokenエラーが出るのでググると babel-preset-react 入れないとダメだった。

こけた後に出来るだけ最小構成にしてReactの書き方がまずかったかgulpfile.jsの書き方がまずかったのか確認した内容の備忘録。

エラー内容

% gulp
[03:57:42] Using gulpfile ~/react-gulp/gulpfile.js
[03:57:42] Starting 'default'...
events.js:142
      throw er; // Unhandled 'error' event
      ^

SyntaxError: ~/react-gulp/src/app.js: Unexpected token (6:2)
  4 |
  5 | ReactDOM.render(
> 6 |   <Child />,
    |   ^
  7 |   document.getElementById('container')
  8 | );
  9 |
    at Parser.pp.raise (~/react-gulp/node_modules/babylon/lib/parser/location.js:22:13)
    at Parser.pp.unexpected (~/react-gulp/node_modules/babylon/lib/parser/util.js:91:8)
    at Parser.pp.parseExprAtom (~/react-gulp/node_modules/babylon/lib/parser/expression.js:510:12)
    at Parser.pp.parseExprSubscripts (~/react-gulp/node_modules/babylon/lib/parser/expression.js:265:19)
    at Parser.pp.parseMaybeUnary (~/react-gulp/node_modules/babylon/lib/parser/expression.js:245:19)
    at Parser.pp.parseExprOps (~/react-gulp/node_modules/babylon/lib/parser/expression.js:176:19)
    at Parser.pp.parseMaybeConditional (~/react-gulp/node_modules/babylon/lib/parser/expression.js:158:19)
    at Parser.pp.parseMaybeAssign (~/react-gulp/node_modules/babylon/lib/parser/expression.js:121:19)
    at Parser.pp.parseExprListItem (~/react-gulp/node_modules/babylon/lib/parser/expression.js:988:16)
    at Parser.pp.parseCallExpressionArguments (~/react-gulp/node_modules/babylon/lib/parser/expression.js:341:20)

プロジェクト構成

.
├── gulpfile.js
├── index.html
├── node_modules
├── package.json
└── src
    ├── app.js
    └── child.react.js

node_modules

入れたものリスト

"devDependencies": {
  "babelify": "^7.2.0",
  "browserify": "^12.0.1",
  "gulp": "^3.9.0",
  "vinyl-source-stream": "^1.1.0"
},
"dependencies": {
  "react": "^0.14.3",
  "react-dom": "^0.14.3"
}

gulpfile.js

var gulp = require('gulp');
var browserify = require('browserify');
var babelify = require('babelify');
var source = require('vinyl-source-stream');

gulp.task('default', function () {
  return browserify('./src/app.js')
    .transform(babelify)
    .bundle()
    .pipe(source('bundle.js'))
    .pipe(gulp.dest('./'));
});

index.html

<!DOCTYPE html>
<html>
  <body>
    <div id="container"></div>
    <script src="bundle.js"></script>
  </body>
</html>

src/app.js

var React = require('react');
var ReactDOM = require('react-dom');
var Child = require('./child.react.js');

ReactDOM.render(
  <Child />,
  document.getElementById('container')
);

src/child.react.js

var React = require('react');

var Child = React.createClass({
  render: function () {
    return (
      <div id="child">
        <h2>Child</h2>
      </div>
    );
  }
});

module.exports = Child;

解決策

これで gulp を実行すると冒頭のエラーが出るので冒頭に書いているように babel-preset-react をインストール(npm install --save-dev babel-preset-react)してgulpfile.jsを修正すればビルド成功する。

インストール後のdevDependencies(package.js)

"devDependencies": {
  "babel-preset-react": "^6.3.13",
  "babelify": "^7.2.0",
  "browserify": "^12.0.1",
  "gulp": "^3.9.0",
  "vinyl-source-stream": "^1.1.0"
}

修正後のgulpfile.js

var gulp = require('gulp');
var browserify = require('browserify');
var babelify = require('babelify');
var source = require('vinyl-source-stream');

gulp.task('default', function () {
  return browserify('./src/app.js')
    .transform(babelify,{presets: ["react"]})
    .bundle()
    .pipe(source('bundle.js'))
    .pipe(gulp.dest('./'));
});

ビルド結果

これでgulpを実行するとちゃんとbundle.jsができあがってブラウザで<h2>Child</h2>のレンダリング結果が表示される。

% gulp
[04:11:17] Using gulpfile ~/react-gulp/gulpfile.js
[04:11:17] Starting 'default'...
[04:11:20] Finished 'default' after 3.67 s

ビルド後の構成

.
├── bundle.js
├── gulpfile.js
├── index.html
├── node_modules
├── package.json
└── src
    ├── app.js
    └── child.react.js

参考

Immutable.jsチュートリアル用環境構築

Immutable.jsチュートリアル用環境構築

Immutable.jsのチュートリアルをCLIですすめるために行った環境構築手順です。
1. Nodeのインストール

割愛…。nodebrewを使ってnodeを入れています。
2. チュートリアル用フォルダ作成

mkdir ./immutableTutorial
3. package.json生成

npm init
4. Immutable.jsのインストール

npm install –save-dev immutable
以上!
実行方法

とりあえず、entry.jsなどのJSファイル作ってチュートリアルのサンプルコードかいて、node entry.jsとやれば結果が返されます。(下のサンプルコードを実行した場合、2と50が改行されて表示されます)
// entry.js
var Immutable = require(‘immutable’);

var map1 = Immutable.Map({a:1, b:2, c:3});
var map2 = map1.set(‘b’, 50);

console.log(map1.get(‘b’));
console.log(map2.get(‘b’));
引き続きチュートリアルをやるために

The case for Immutability

assertモジュールを使うところが出てきたので、assertモジュールを入れます
npm install –save-dev node
で、チュートリアルのコードをこう書き換えてnode entry.jsを実行します。
var assert = require(‘assert’);
var Immutable = require(‘immutable’);

var map1 = Immutable.Map({a:1, b:2, c:3});
var map2 = map1.set(‘b’, 2);

try {
assert(map1 === map2);
console.log(‘true’);
} catch(e) {
console.log(‘false’);
console.log(e);
}

webpackのチュートリアルエラー回避

webpackのチュートリアルをやってるとcss-loaderのところでwebpackを実行すると
% webpack ./entry.js bundle.js –module-bind “css=style!.css”
zsh: event not found: .css
とエラーが出るんだけど、GitHubにも出てるようにダブルクォテーションをシングルクォーテーションにすれば解決する。
% webpack ./entry.js bundle.js –module-bind ‘css=style!css’
Hash: aa51824889799250cc68
Version: webpack 1.12.9
Time: 3593ms
Asset Size Chunks Chunk Names
bundle.js 11.8 kB 0 [emitted] main
[0] ./entry.js 64 bytes {0} [built]
[5] ./content.js 44 bytes {0} [built]
+ 4 hidden modules

ステップバイステップReact.jsで作るTodoアプリ

復習のためReact.jsでTodoアプリのViewを書いてみました。

このTodoアプリを通して次のことを勉強出来ました。

– stateとpropsの使い方
– bindを使ってコールバック関数内のオブジェクト指定

まだわかっていないのがコンポーネント分割の単位なので、見通しが立ったらまたブログ書きます。

では、書いていきます。

## ファイル構造

“`
MyApp
├── index.html
└── react-0.13.3
├── README.md
├── build
│   ├── JSXTransformer.js
│   ├── react-with-addons.js
│   ├── react-with-addons.min.js
│   ├── react.js
│   └── react.min.js
“`

## HTML雛形作成
React.jsでReactコンポーネントを作る前にHTMLの雛形を作ります。

“`html:index.html








“`と“`“`の間に書く。

##必要なコンポーネント
コードを書き始める前に必要なコンポーネントを考えます。

今回は3つのReactコンポーネントを作ります。

– TodoApp ・・・アプリ全体
– TodoCreator ・・・Todoを新規で追加するため
– TodoList ・・・ Todoリストを描画するため

##TodoAppコンポーネントを描画

“タグの間の一番最後でrenderメソッドで描画するコンポーネントとロードするdivのidを指定します。

“`jsx
React.render(
,
document.getElementById(‘myApp’)
);
“`

##TodoAppコンポーネント

###renderメソッドを定義

“`jsx
var TodoApp = React.createClass({
render: function(){
return (


);
}
});
“`

TodoCreatorコンポーネントとTodoListコンポーネントを返すTodoAppコンポーネントを定義しています。

###Todoリストの初期値の定義

Todoリストの初期値をTodoAppクラス内で定義します。
テストの為に、sampleというキーと0(未完了/false)の値をセットしています。(後でこの中身を消して空のJSONオブジェクトとして定義します。)

“`jsx
getInitialState: function() {
return {
todos = [{item:”sample”, status:0}
}
},
“`

###TodoListコンポーネントの定義
Todoリストを表示するためのTodoListコンポーネントを定義します。

“`jsx
var TodoList = React.createClass({
render: function() {
return (

    {
    this.props.todos.map(function(todo, i){
    if (todo.status == 0) {return

  • {todo.item}
  • }
    else {return

  • {todo.item}
  • }
    })
    }

);
}
});
“`

次にTodoAppコンポーネントからgetInitialStateで定義したtodosのステートを渡すために““を次のように書き換えます。

“`
// // 変更前

“`

これでtodosの状態をTodoListコンポーネントに渡し、TodoList側でpropsを通じてtodosの中身を参照することになります。

###TodoCreatorコンポーネントの定義
Todoの内容を入力し、Addボタンを押してtodosリストに追加するためのコンポーネントを作ります。

“`jsx
var TodoCreator = React.createClass({

_onAdd: function(){
var newTodo = this.refs.inputText.getDOMNode().value;
this.props.onAdd(newTodo);
},

render: function(){
return (


);
}

});

“`

Addボタンが押されたら_onAddメソッドを呼び出します。次に_onAddメソッド内ではgetDOMNodeを使ってtextボックス内の値を取得して、newTodo変数に格納し、propsを通じてnewTodoを引数に親であるTodoAppコンポーネントのonAddメソッドを実行します。

####TodoAppのonAddメソッド
TodoAppコンポーネントにonAddメソッドを追加します。

“`jsx
onAdd: function(newTodo){
this.setState({
todos : this.state.todos.concat({item:newTodo, status:0})
});
},
“`

onAddメソッドの内部でsetStateを実行todosの状態を更新します。

こうすることで、todosのステートが更新され、TodoListの表示が更新されます。

##TodoCreatorコンポーネントの修正
Addボタンを押したときに、入力した内容がちゃんと消えるようにするために加筆します。

“`jsx
var TodoCreator = React.createClass({

getInitialState: function(){
return {
value: “”
}
},

_onAdd: function(){
var newTodo = this.refs.inputText.getDOMNode().value;
this.props.onAdd(newTodo);
this.setState({value: “”});
},

_onChange: function(e){
this.setState({
value: e.target.value
});
},

render: function(){
return (


);
}
});
“`

valueという変数をgetInitialStateで定義しておき、inputボックスの中身が変化したら_onChangeメソッドでvalue変数を更新します。Addボタンが押されたら、親コンポーネントのtodosに更新作業を行った後に、value変数の中身をNullに戻しています。

##完了Todoの更新
Todoの完了を示すチェックボックを表示し、完了したものは取り消し線で表示するようにTodoListコンポーネントを修正します。

###Todoの表示部分の変更
“`jsx
render: function() {
return (

    {
    this.props.todos.map(function(todo,i){
    if (todo.status == 0) {
    return (


  • {todo.item}
  • )
    } else {
    return

  • {todo.item}
  • }
    },this)
    }

);
}
“`

###Todoステータスの更新
チェックボックスがクリックされると、まずTodoListコンポーネントの_onDeleteが呼ばれ_onDelete内の“`this.props.onDelete“`で親であるTodoAppコンポーネントのonDeleteメソッドが実行されています

まず、TodoListコンポーネントの_onDeleteの定義から

“`jsx
_onDelete: function(i){
this.props.onDelete(i);
}
“`

次にTodoAppコンポーネントのonDeleteメソッドを定義します。

“`jsx
onDelete: function(i){
var targetTodo = this.state.todos[i];
targetTodo.status = 1;
this.setState({
todos: this.state.todos
});
},
“`

また、propsで渡すためにTodoAppコンポーネントからTodoListへonDeleteを渡すためにTodoListのロード部分を変更します。

“`jsx

“`

これでindex.htmlファイルをブラウザで表示すると下図のTodo画面が出来上がります。

## 全体コード

“`html








“`

検温記録WEBアプリ「れこねつ」をリニューアル

検温記録WEBアプリ「[れこねつ](http://reconetsu.22labs.net/#!/)」をリニューアルしました。

とはいえ、サービス本番稼働前の実験なので、いつ停止してもおかしくないんですが。

*今のところ本番リリース時にはデータも移行する予定ではあります。*

以前にリリースした[Sencha Touch製](http://apps.22labs.net/RecoNetsu-ST/)はデータがローカルストレージ版だったのでスマホのキャッシュをクリアしたら消えていましたが、今回はデータの永続化をサーバ側でしているのでPC、スマートフォン、タブレットどれでも入力と確認ができます。

2週間自分で使ってみて「とりあえず動く」という程度のできです。

ソースは[https://github.com/uchikoshi22/reconetsu](https://github.com/uchikoshi22/reconetsu)にて公開しています。

gulp-uglifyの1.2.0はダメな子

gulp-uglifyを使って次のようなタスクを作って“`gulp build“`を実行すると次のようなエラーが出る。

## gulpタスクの定義

“`
# js:gulpfile.js
var gulp = require(‘gulp’);
var concat = require(‘gulp-concat’);
var uglify = require(‘gulp-uglify’);
var react = require(‘gulp-react’);

var path = {
HTML : ‘src/index.html’,
ALL : [‘src/js/*.js’, ‘src/js/**/*.js’, ‘src/index.html’],
JS : [‘src/js/*.js’, ‘src/js/**/*.js’],
MINIFIED_OUT : ‘build.min.js’,
DEST_SRC : ‘dist/src’,
DEST_BUILD : ‘dist/build’,
DEST : ‘dist’
}

gulp.task(‘build’, function(){
gulp.src(path.JS)
.pipe(react())
.pipe(concat(path.MINIFIED_OUT))
.pipe(uglify(path.MINIFIED_OUT))
.pipe(gulp.dest(path.DEST_BUILD));
});
“`

## エラー内容
“`
react-gulp/node_modules/gulp-uglify/node_modules/deap/lib/deap.js:56
Object.keys(b).forEach(function(p) {
^
TypeError: Object.keys called on non-object
at Function.keys (native)
“`

ググると[github](https://github.com/terinjokes/gulp-uglify/issues/98)で、

「gulp-uglifyの1.2.0はダメな子だから1.3を使わんね。」

「いやいや、1.3.0どこにあると?1.1.0に落としたらいいちゃないと?」

ってことなんで、おとなしく1.1.0に落とします。

## gulp-uglify@1.2.0のアンインストール

###消します
“`
npm uninstall gulp-uglify
“`

###1.1.0入れます
“`
npm install –save-dev gulp-uglify@1.1.0
“`

##タスク再実行
“`
gulp build
[13:38:57] Using gulpfile ~/react-gulp/gulpfile.js
[13:38:57] Starting ‘build’…
[13:38:57] Finished ‘build’ after 14 ms
“`

成功!

[Qiitaにも投稿](http://qiita.com/uchikoshi22/items/c8edb851cc8045aa394b)

検温記録WEBアプリを作りました

3歳の娘が頻繁に熱を出すので検温結果を一時的に記録するための簡単なWEBアプリを作りました。

[レコネツ – 検温記録アプリ](http://apps.22labs.net/RecoNetsu-ST)

http://apps.22labs.net/RecoNetsu-ST/

データ保存はlocalstorageを使ってるのでブラウザのキャッシュをクリアしてしまうとほとんどの端末では検温結果履歴も消えてしまします。

使ったフレームワークはSencha Touchで、ほとんどは前回作ったパスワード生成アプリの焼き直しです。

今回新たに試したのはdatetimeスピナプラグインとリスト内のグループ(日付)表示です。

画面は今のところ登録画面と履歴画面の2つだけ用意しています。