notebook

都内でWEB系エンジニアやってます。

rxjsのメモ(検索boxの入力値で必要な部分だけ抜き出す)

rxjs使おうとすると毎度ドキュメント見ながら使えそうなオペレータ調べてやるのがつらいですね

後の自分用のメモにサンプル実装を残します

今回は「検索ボックスなどの入力で必要な部分だけ取り出す」です

  • 実行環境
angular: 5.1.2
rxjs: 5.5.6

やりたいこと

インクリメンタルサーチで検索かけてくれるボックスなど文字列が変わるたび(keyup)にイベントが発生する場合

keyupイベントでinputに入力された値を取ると下記のような感じになります

h
ho
hog
hoge #この部分だけ取ってきたい

最後の部分だけを取ってきたいというのが今回の内容となります

途中経過ではなく検索対象としての文言のみ取得したいって感じですね

まずそれぞれ使うオペレータを見ていきます

interval

一定間隔でストリームに値を流すやつですね

Observable | RxJS API Document

http://reactivex.io/rxjs/class/es6/Observable.js~Observable.html#static-method-intervalreactivex.io

withLatestFrom

二つのストリームの一番最新のものを取ってくる感じですね

Observable | RxJS API Document

http://reactivex.io/rxjs/class/es6/Observable.js~Observable.html#instance-method-withLatestFromreactivex.io

pairwise

ひとつ前の値とセットにして次のストリームへ流すやつですね

Observable | RxJS API Document

http://reactivex.io/rxjs/class/es6/Observable.js~Observable.html#instance-method-pairwisereactivex.io

filter

フィルターです、これは特に説明はいらないですね

Observable | RxJS API Document

http://reactivex.io/rxjs/class/es6/Observable.js~Observable.html#instance-method-filterreactivex.io

distinctUntilChanged

流れてくるストリームに対してdistinctをかけるやつですね

オブジェクトのデータが渡ってくる場合はcallbackに何を同一と判断するかの式を渡してあげる必要があります

Observable | RxJS API Document

http://reactivex.io/rxjs/class/es6/Observable.js~Observable.html#instance-method-distinctUntilChangedreactivex.io

検索機能などで入力された文字列を取得する

ここまで見てきたオペレータを使ってサンプルを実装してみます

入力が完了したという条件を仮に「5秒以上inputに変化がない」とすると

まず下記二つのストリームを用意します

  • 5秒ごとに値を流すストリーム
  • keyupイベントを流すストリーム

5秒ごとに値を流しwithlatestfromでkeyupストリームの最後の値を取得してきます

さらにストリームの前後のデータが欲しいのでpairwiseで前後のデータをくっつけてストリームに流します

filterでkeyupイベントのデータに変化が無い(5秒)状態になったものだけをフィルタします

この場合だとintervalが5秒に設定しているため入力が落ち着いたあともずっと値が流れてしまうが欲しくない

最後にdistinctUntilChangedで変化がないけど5秒毎に流れてくる同じ値をカットします

コードにするとこんな感じ

  • component.ts
export interface IInputText {
  value: string;
}
@Component({
  selector: 'app-rxjs',
  templateUrl: './rxjs.component.html',
  styleUrls: ['./rxjs.component.scss']
})
export class RxjsComponent implements OnInit {

  private inputTexts$: Subject<IInputText>;
  private interval$: Observable<any>;

  constructor(
  ) { }

  ngOnInit(){
    this.inputTexts$ = new Subject<IInputText>();
    this.interval$ = interval(5000);

    this.interval$.pipe(
      withLatestFrom(this.inputTexts$),
      pairwise(),
      filter(this.silinceValue),
      distinctUntilChanged(this.distinct)
    ).subscribe(p => {
      console.log(p);
    });
  }

  silinceValue(v){
    // 入力が落ち着いたかどうかをチェック
    // このフィルタを通る = 入力がintervalの分だけないということ
    return v[0][1].value == v[1][1].value;
  }

  // 落ち着いてからずっと同じものが流れないようにする
  distinct(a, b) {
    return a[1][1].value === b[1][1].value;
  }

  onInputEvent(e) {
    this.inputTexts$.next({value: e.target.value});
  }
  • component.html
div>
  <h5>入力されたテキストを(入力途中の値を除く)何とかして取り出す</h5>
  <input (keyup)="onInputEvent($event)">
</div>

書いててめっちゃわかりずらい。。。。

整形とかしてないのでいろいろ微妙な部分もありますが目的の動作は達成できました。

図にするとこんな感じの流れですね

f:id:swfz:20180617013527p:plain

まとめ

なんとなくわかるしドキュメント見ながら使えるようにはなってきたけどやっぱり難しいです

感覚的にはパズルみたいでオペレータを調べて組み合わせて目的の挙動にできるとおぉぉぉ!!!!ってなりますねw

もっとたくさんケースをこなして魔術師になりたいものです