ITの隊長のブログ

ITの隊長のブログです。Rubyを使って仕事しています。最近も色々やっているお(^ω^ = ^ω^)

【Angular2】caused by: Error trying to diff '[object Object]'

ngForで、配列をイテレートしようとしたらエラーが。

ぐぐってみると

stackoverflow.com

Your extractData (and possibly also your HTTP API) is returning an object {} - ngFor requires an array [] to iterate.

Oh...

あ、てか、よく考えたら当たり前か。Objectはできないのね。みんなはちゃんと配列をわたしましょう。

【Angular2】caused by: Expression has changed after it was checked.

以前から何度か苦しめられているエラー

github.com

This is not a bug, it's a feature of dev mode working as intended. Calling enableProdMode( ) - see updated plunk when bootstrapping the app prevents the exception from being thrown.

That said, it's being thrown for good reason: In short, after every round of change detection, dev mode immediately performs a second round to verify that no bindings have changed since the end of the first, as this would indicate that changes are being caused by change detection itself.

In your plunk, you have the binding [attr.spinner]=isLoading, and isLoading changes as a result of your call to this.load(), which occurs when ngAfterViewInit is fired, which happens as a part of the initial change detection turn. That in itself isn't problematic though - the problem is that this.load() changes a binding but does not trigger a new round of change detection, which means that this binding will not be updated until some future round of change detection.

どういうこっちゃ・・・?

とりあえず、ngAfterViewInitで、値を変えていることがダメっぽい。よくわからずに使うとダメってことね。

angular.io

lifecycleの一番最後にある、ngOnDestroy。こいつで値を変更するとうまくいきましたー。

理由は知らん。

【CakePHP3.x】query builderを使った複数条件のwhere

便利になったんだろうけど、最初の壁は乗り越えづらい。

ちとハマったので、昔みたいに軽くメモ。

cakephp3.xはquery builderが一新されている。色々方法があるとは思うが、情報探すの大変。

一番はリファレンスを見ることがだが、基礎なので、応用を考えなければいけない。

「これって、こうだから、こんなこともいけるんじゃね?」みたいな発想(?)と試してみるべし。

話戻るけど、下記条件のqueryを実行したかった。

既存のデータから、とある日付 and とあるユーザー and (between 開始時刻 ユーザーが入力した情報1 and ユーザーが入力した情報2 or between 終了時刻 ユーザーが入力した情報1 and ユーザーが入力した情報2)

みたいなquery(わかりづらいな・・・)

1つの条件ならwhereを使えばすぐできそうだが、複雑になるとどうやるかわからなかった。

とりあえず、リファンレスに書いてあることを、試し試しでやっていたらできたのでメモ。@

<?php
// ...
    public static function existsDatetime($date, $userId, $start, $end) {
        $tableName = TableRegistry::get('table_name');
        $query = $tableName->find('all')
            ->where(function ($exp) use ($date, $start, $end, $userId) {
                // (between or between)を作っている
                $orConditions = $exp->or_(function ($or) use ($start, $end) {
                    return $or->between('start_time', $start, $end)
                        ->between('end_time', $start, $end);
                });
                // orは`$exp->or_()`で作らないといけないが
                // andはメソッドチェーンすればandとなる
                return $exp->add($orConditions)
                    ->eq('my_date', $date)
                    ->eq('user_id', $userId);
            });
        $count = $query->count();
        return !(boolean)$count;
    }
// ...

↑のコードはバリデーションクラスで使ったので、バリデーションのこともまたいつか書こうと思う。

無名関数を使えば、色々な表現方法を使ってqueryを生成できそうだ。便利。

これでできた。( ´ー`)フゥー...

【Angular2】Formを動的に扱うコードを書いてみた

ここまで来るのに時間かかったよ!!!

github.com

コードは全部こっちにあります。コミットログを追ったら情報わかるかも。もしわからないなら質問ください。ただ私も初心者に近いので答えられる範囲ですが(^^;

まずやりたいこととして、jQueryみたいにdomを動的に作ってviewに流し込みしたり、イベントを用意してバリデーションとかしたかった。

AngularJS1.xでは、そういうことを実装したことがなかったので、どうやってやればいいんだろ?って悩んでいたのね。

んで、色々試行錯誤したり、チュートリアルを試してみたりなどやっていくと。

情報少なすぎ、チュートリアル少なすぎワロタ。壁にぶち当たり。泣いた(´;ω;`)ブワッ

このままjQueryに戻って実装してやろうかなと思った。

しかしダメだ・・・! お前はまた4000行以上のコードを書きたいのか。。。!メンテしたいのか・・・!と、思い踏みとどまった。

思いだけでは解決しなかった。我慢ならなかったので、ぐぐって本を購入しました。 www.ng-book.com

洋書なので、もちろんよめませんでした(´;ω;`)ブワッ

んで、最近賢くなったと聞いたGoogle翻訳を使ってみました。

すると、これがすごく有能。使いながら読むとコードを理解しながら英語の勉強にもなった。Googleすげえぇええええ!!!

で、読んだ内容はFormの基礎。簡単にまとめる。

Angular2のFormは、入力フォームにFormControlを使い、FormGroupでグルーピングする。

使い方ざっくり

  • moduleで、FormsModuleimportしてね。
  • FormControlはこうやって使う => <input type="text" name="name" ngModel>
  • FormGroupは、<form #f="ngForm" (ngSubmit)="onSubmit(f.value)">...</form>
    • onSubmitのメソッドを用意したら、FormGroupにセットされているFormControlの値にアクセスすることができる

#f="ngForm"とかngModelとかなにこれ?と思うかもしれないが、どうやらFormsModuleを呼ぶと暗黙的に値がセットされるらしい。

初期値渡したいの。どうすればいい?

  1. FormBuilderを使った方法があります。

当初はこれが細かい設定の方法だと思っていましたが、どうやらこのクラスもFormGroupFormControlのためのシンタックスシュガーっぽい。

本当に細かくしたいならnew FormControl()とかnew FormGroup()のように、インスタンス化から自分で手続きしなけいけない。

さて、FormBuilderの話に戻る。こちらも使い方ざっくり

  • さっきのFormsModuleに、ReactiveFormsModuleも追加でimport
  • 使いたいComponentで、FormBuilderをDIする
  • FormBuilderのメソッドに、groupがあるので、KeyValueでFormControlになる値を渡してあげる。
private myForm: FormGroup;
constructor(formBuilder: FormBuilder) {
  this.myForm = formBuilder.group({
    'myName': ['初期値だお']
  })
}
  • view側では、<form [formGroup]="myForm">...</form>にすること。このmyFormComponentのメンバー名と同じしないとダメよ!
  • また、<input>では、属性に[formControl]="myForm.controls['myName']"を使う。

これでおk。初期値が入るはず。

このFormBuilderは、ValidateやCustom Validateでも利用できるので、こっちの方を使ったほうがいいかも知れないです。

Validateは?

はい。書きます。

バリデーションは、FormControlの第二引数にセットすればおk.FormBuilderを使っているのなら、こう書けば良い。

import { FormGroup, FormControl, FormBuilder, Validators} from '@angular/forms';
// ...
private myForm: FormGroup;
constructor(formBuilder: FormBuilder) {
  this.myForm = formBuilder.group({
    'myName': ['初期値だお', Validator.required] // <- 第2引数に追加
  })
}

こうすると、requried(必須)のバリデーションが動いてくれる。

また、エラーも表示したいよね? view側でこう書いて。

<div *ngIf="myForm.controls['myName'].hasError('required')"
  class="ui error message">myName is required</div>

複数バリデートしたい場合は? 絶対あるよねー。

第2引数に配列で渡せばいいでしょ?って思うかもしれないがそれはできない。

なので、Validator.compose()というメソッドを使って値を合成(?)して渡す。

private myForm: FormGroup;
constructor(formBuilder: FormBuilder) {
  this.myForm = formBuilder.group({
    'myName': ['初期値だお', Validator.compose([
      Validator.required,     // 必須
      Validators.minLength(5) // 5文字以上
    ])]
  })
}

view側もさっきと同様に追加してね。これでよし。

全部書こうと思ったけど、疲れたからまた今度追記する。Githubのログを追ったら一応わかるはずだけど。

残り

  • Custom Validator
  • Watch Value Changes
  • 本には乗っていなかった自分で試したやつ
    • Enter keyを無効にしたい
    • Datepickerとの組み合わせ
    • 動的にFormを追加

本の感想

すごくわかりやすいよ!!

英語なので、ストレス溜まっていたけど、Google翻訳のおかげで今ならすっと入ってきます。英語が綺麗なのもあるのかな。

まだFormの章しか読んでいないので、ちゃんとオススメはできませんが、基礎はしっかり、後半はチャットアプリデモの作り方みたいな章もあるのですごく楽しみです。

ちと高めですが、勉強したい人はぜひー。 www.ng-book.com