やってみたんよ。
この記事は高校生くらいの知識の、プログラミングやってみたいだけで色々情報を取得したけどなかなかやりはじめられていない人くらいを対象としています。
1環境2コピペ3公式サンプルにDELを付けるまでの試行錯誤、で構成されています。
謝辞・mithrilタグ各位。
###1・だって、mithrilは簡単簡単いうて、本当に初心者は誰もやってへんやんか。
環境構築はなんとなくわかる。
nodejsいれてnpmでinstallすることのexpress。
―まぁここは本当はVS2015のテンプレートでなんとかなった。
Express3とnodejs(typescript)のパッケージを選んだら自動的にそこまでしてくれる。
初心者向けの記事を狙ってるからね。一から書かないとね。
その後、npmパッケージの追加(言い方は変わってもinstallのことよ)でmithrilとかmaterial-desighn-liteとかいれた。
こっから、/views/layout.jadeでとりあえずmithril読ませたらええんちゃうかな?と思ったけど、全然ちがった。
npmで追加されるのはサーバー側のほうで、フロントのほうは/publicに置かなあかんやん。
どんだけ考えても/node_module/って書くのは変なような…って五分くらい考えてた。
というわけで置きなおしました。
mithrilだけ勉強したくても、ウェッブで公開するフロントとして使いたいならこういう所も学ばないといけないのね。サーバー・フロント。別物。でも誰も書いてない。本当にみんな使ってるのかな?エア環境では?ってこの辺でおもってた。むずかしいもん。
この辺りは切り取って記事にしたいね。
###2・コピペタイム
Qiitaには良質な記事がたくさんあると僕は思ってる。少なくとも、自分がアカウントを作ったからにはそう思うべきや。何がスタックオーバーフローや。雑魚がオーバーフローしとるだけやんけ。こうやって手間暇かけた記事にこそ面白さがあるんや。仕事ちゃうねんで。
さて、もういいかな?褒めた分好きにやらせてもらうで。
要するにコピペがしたいねん。良質な記事やからこそ。パクリちゃうねん。この上級国民の考えわかるかな。
さてコピペさせてもらったのは以下の記事。
MithrilのチュートリアルをTypeScriptで写経してみる
コピペさせてもらってなんやけど、mithrilの一つのコンポーネントに対する部品意識がないというか、本当は大きく全部をネームスペースでくくるんやないのかな?と思ったけど、まぁこれ。
TypeScript選んだものの、素のJavascriptのfunction(function(){})みたいな構造の意味もわからないままやってるからね。
コピペじゃないと動く気しない。ちょっと調べたら、TypeScriptはJavascriptまんまでも警告でるだけで動くみたいやけどね。まぁええねん。
んでコピペして作ったtest1.jsをindex.jadeから読み込みました。動きました。
このあと
Mithrilでコンポーネントを使い回す
もコピペ…すると動かない!これは簡単。m.mount対象がbodyタグになってるからで、これを適宜新しいdivのidなりに振ればええわけやね。
こういうコピペも混ぜこぜすると少しづつ自分の理解が深まるのが感じるよ。
単一のコンポーネントに二枚のカードコンポーネントを混ぜる、このやり方…すこしQiitaで読んだelmとかいう関数型のやり方がこんなんやったような…。そうか、mithrilは関数言語タイプなんやな、と思った。
今この記事書きながら公式見てたら、めちゃ何回も関数型やって書いてあった。そうか、おまえも気づいたか。
でもmithril公式はプレイヤーを束縛したくないみたいで詳しくは書いてない。
このへんはelmなりreactなりの記事で深めたほうがいいかもしらん。
mithril記事や公式には全然書いてないけど、たぶん最終的にはあの手のイメージの
・上位でコンポーネント全体の進行と他ページ等への遷移を管理するコンポーネント作って
・一番下位にインプットやアウトプットのみの機能を作って
・中位に下位から上がってきたユーザ入力とかをセコセコいじる。時と場合で上位に報告する。
形になるんやと思う。こういう基本的な考えがないとどうしてもtodo以上の規模のものは作れないと思う。
誰も説明せんので、一応やで。
―さて、ではこのコピペからなにをしようか…。
そうや、ここは他コンポーネントを呼び出すボタンを作り最終的にtodoのサンプルにないデリート機能をつけよう!
###3詰まる。
まずカード記事のコピペに対する呼び出しを考えた。
最初はベタで二つのコンポーネントをそのまま表示してたけど、上の考えから行くと多分一発目のmountは一つがいい。次々に必要な機能をmountしていくんやろうな、とイメージ。
で、変更点は面倒やから省くけどカードコピペを大きくくくるvar cardを作って、
(var card = function () {コピペ})
それをtodoのviewの追加ボタンの行の下に追加ボタンのコピペまんまのものを置いてonclickでcardにしてみた。
こんなあほなことしてたらそのうち変数が衝突しそうやけど、まあええやろ。
ここまでは順調だったけど、todoの消すボタン。これがむずい。
まず、追加する度に出てくるテーブルのやつ(trが大枠でtdが個別の枠ってこと?よくわからん)に、Checkboxの行からコピペしてbuttonに変えたものをを後ろに追加。
で、呼び出す関数をtodoVM.del(index)として、todoVMにdel関数を作った。
ここから詰まるのよ。
del関数の中身は最初確か、
todoVM.todoList().splice(index,1)だったかな?
まぁ関数の中身はおいおいとして、これがうまく動かないの。
それが、del関数を設定したボタン「以外」で全部発動しちゃうんよ。
なんか文字をinput boxに書く、それを追加ボタンで追加する。
この一連でいえば、追加ボタンの度に消える。
なんで?
###4解決、そして…。
未だになんでかわからないけどこの解決はやはりqiita。そしてコピペにあった。
Mithril.js 試してみた(4) todo アプリのフロント側まで
このおっさん、少ないコードで高度なことしとる(うまいギャグやな)から避けてたけど、このオリジナルTodoを見てほしい。
要するに全部のソースは追ってないけど、消すボタンがあるから参考にした。ってわけや。
するとやな、Viewのところで
m('button[type=reset]', {onclick: removeTask.bind(null, task)}, '削除')
としてる。
なんやこれ?バインド?.bindで検索するとジェークエリーのメソッドしかでてこん。
ジェークエリーは読ませたつもりないけど、おっちゃんも別につこてないけど使ってるし、俺のヴィスタ(ヴィジュアルスタジオ)も「つこてええで」言うてる。
漫画の主人公が負けかけた時に不意に手が光りだすみたいな感じに思えたね。
(その後javascript .bindで検索するとジェックエに関わらずいろいろでました。)
書いてることはわからんが、これやというのはわかる。パクリや!
.bind(null,index)にしてみた。どうせ後ろが渡すべき引数なんやろ。
これが当たり!!!!!
見事消えた。
ついでにこのおっさんのやってる削除の方法はスプライスつことらん。
mapとかreduceとか、関数砦の三悪人みたいなやつ(ちょっとだけ知ってる)の中のfilterをつことる。
これはおもろそうや。パッと読んで大体理屈はわかる。要するに削除対象以外で配列を作り直すんやな。
こういうのはわかり始めるとすんなり。
それで組んでみま……、こっからまた詰まる。
多分この人は空の配列?tasklist=[]で作ってるけど、わしのコピペプログラムは
todoVM.todoList= m.propや。
どっちがええかは知らんが、このせいでfilterしたやつを返すものをもってく左辺の=がどうしてもうまくいかない。
todoVM.todoList= todoList= todoVM.todoList()[]=
曰く式じゃないだの、それは違うだの…。ヴィスタもはっきり何が違うかいうてくれよ。
んで、いろいろ試したけど要するにtodoList()というファンクション形式??なのが、代入できん原因なわけや。
なんつーか、配列とかそういうんじゃないんやろな。しらんけど。でもすぐ=ファンクションとかするくせに、結構身勝手な仕様じゃないか??むちゃくちゃならルビーくらいアホな仕様にせえよ…グチグチ
とまあ、
逆に言えばtodoList()のファンクションでもう一回新しい配列内容を読ませれば…。と考えたのよ。
そんで考えたのが一旦var a = で作って
そのaをtodoListってみよかと。todoVM.todoList(a)。お、いけた!!!
というわけで削除機能が完成したのでそれを張って終わりたいと思います。
三時間くらいいろいろやってました。ジャワスクリプトって本当に(アルゴリズム以外が)難しい。
namespace todoVM {
//新しいTodoを作成する前の、入力中のTodoの名前を保持するスロット
export var description: MithrilProperty<string>;
//アクティブなTodoのリスト
export var todoList: MithrilProperty<Todo[]>;
//export var done: MithrilProperty<boolean>;
/**
* 初期化
*/
export function init() {
todoVM.todoList = m.prop<Todo[]>([]);
todoVM.description = m.prop<string>("");
//todoVM.done = m.prop<boolean>(false);
}
/**
* Todoの追加をしてdescriptionをクリア
*/
export function add() {
if (description()) {
todoVM.todoList().push(new Todo(description()));
todoVM.description("");
}
}
export function del(x) {
//これがLightSpeedC式。別にaとかいらんで()でくくればいいって?うるせーなほっとけ
//この方式だと引数に配列とればたくさんにチェックして一度に消すようなことが可能やね。
//var a = todoVM.todoList().filter(function (todos) { return todos !== todoVM.todoList()[x] });
// todoVM.todoList(a);
//
//これがスプライス式。それぞれのTodoに削除ボタンつけるならこれでええね。
console.log(todoVM.todoList().splice(x, 1));
//試行錯誤の過程。なんも役に立たなかったプロパティネームとかいう雑魚メソッド
//console.log(Object.getOwnPropertyNames(todoVM.todoList[x]));
//試行錯誤の副産物。こうする( ()[] とかいう変な)ことでなんと対象その値が見れる。というかこうしないと見れない。むずい。
console.log(todoVM.todoList()[x].description());
}
}
/**
* Controller
* コントローラは、モデルの中のどの部分が、現在のページと関連するのかを定義している
* この場合は1つのビュー・モデルですべてを取り仕切っている
*/
function controller() {
todoVM.init();
}
/**
* View
* @returns {any}
*/
var view = () => {
return m("div", { id: "test1"}, [
m("input", { onchange: m.withAttr("value", todoVM.description), value: todoVM.description() }),
m("button", { onclick: todoVM.add }, "追加"),
m("button", { onclick: card }, "card"),
m("table", [
todoVM.todoList().map((task: Todo, index: number) => {
return m("tr", { "id": index }, [
m("td", [
m("input[type=checkbox]", { onclick: m.withAttr("checked", task.done), checked: task.done() })
]),
m("td", { style: { textDecoration: task.done() ? "line-through" : "none" } }, task.description()),
m("td", m("button.mdl-button mdl-js-button mdl-button--raised", { onclick: todoVM.del.bind(null,index) },"del")
),
])
})
])
]);
};