Vue.js の基本 (3)

前回、テキストボックス、ラジオボタン、セレクトボックスについて、UI 要素と JavaScript のデータがひもづく-バインディングされる様を確認しました。

UI 要素には他にも、複数のデータとひもづくものがあります。チェックボックス、複数選択セレクトボックスなどです。

古典的な仕組みでは、name 属性に同じ値を持たせた複数の input を用意してサブミットすることで、サーバサイドではそれらを配列として扱うことが多いかと思います。例えばこうです。

<input type="checkbox" name="gender[]" value="female" />
<input type="checkbox" name="gender[]" value="male" />
<input type="checkbox" name="gender[]" value="other" />

こうすると、PHP では

$selected = $_POST['gender']; // $selected には選択したものが配列でセットされる

となります。

ちなみに、ここからHTTPレスポンスを返すとき、チェック状態を復元する必要があることが多いですが、PHP のみの機能では、それを実現するのは結構面倒です。 データにしたがって適切に checked 属性をつけた HTML コードを生成しなければならないからです。
これはこのシチュエーションに限った話ではなく、最初に表示するとき登録されているデータにもとづいてチェック状態をセットするとか、セレクトボックスの選択状態、テキストボックスの初期値など、データにもとづいて UI 要素を表示するためには必要なことになります。

これが Vue.js では、このようになります。

JavaScript の方は、data に genders という配列があります。

HTML では、それぞれのチェックボックスに v-model 属性を使用して genders がバインドされています。

span は、genders の中身を表示する部分です。詳細は割愛しますが、genders の中身をループして表示しています。

これを動かしてみると、チェックボックスの状態によって、genders の内容がリアクティブに変化していることがわかります。チェックされたチェックボックスvalue 値が、バインドされた genders の配列の要素となっています。

これにより、表示する際のチェックボックスの復元も簡単に実現できます。単に、チェックをつけたい値が配列に設定されていればよいのです。
最初に male をチェック状態にしたいならば、

data: {
  genders: ['male']
}

のようにすればよいだけです。




Vue.js の基本 (2)

Vue.js を使用する場合の入力要素とのつなぎこみを見ていきたいと思います。

表示する場合は、ビューモデルデータを {{ }} という記法を用いて表示させることができました。
ユーザが入力した内容をビューモデルデータに反映させるためには、v-model という特殊な属性を使用します。

百聞は一見にしかず、コードを見てみます。

JavaScript の方は、data に text. pulldown, radio という3つの項目があるだけです。何も処理はないため今回は methods はありません。

HTML を見てみると、<input type="text" v-model="text" /> というコードがあります。
この v-model="text" が、「input に入力された内容は data の text に入れるよ」という意味になります。
data の text の値が変更されれば、テキストボックスの内容も変更されます。テキストボックスからデータ、データからテキストボックスへ、双方向にひもづけされる*1ということです。

テキストボックス以外でも同じようになります。上記の例ではプルダウンとラジオボタンですが、テキストボックスと同じように data とひもづけられていることが確認できるかと思います。

プルダウンやラジオボタンでは、data の方の値を設定しておくことによって、初期値を設定することができます。
例えば radio: '' となっているところを radio: 'b' とすれば、表示された段階で b のラジオボタンが選択されている状態になります。




*1:Vue ではこれをバインディングといいます。この場合は双方向バインディングになります。

Vue.js の基本

Vue.js は、公式ページにおいて

Vue はユーザーインターフェイスを構築するためのプログレッシブフレームワークです。

と説明されている通り、ユーザインタフェース - HTML(DOM) とその上で制御することに特化したフレームワークです。
提供される機能がユーザインタフェース周りに限定されているため、非常に「軽い」ところが気に入っています。

Vue の中心となるのが Vue オブジェクトです。
これは、ビュー(view)のデータをモデル化したオブジェクトです。オブジェクトですから、データと振る舞いを持ちます。

これは、「増やす」ボタンをクリックすると、隣にある数字をインクリメントするアプリケーションです。

HTML は非常にシンプルで、id 属性が "app" の div の中に、button が1つあり、その隣に {{ count }} という見慣れない記述があります。

JavaScript のコードでは、Vue オブジェクトを生成しています。このとき、オプション el に、ひもづける要素を指定します。el:'#app' なので、HTML で記述した div タグを指していることになります。

他にも data と methods という2つのオプションがあります。この2つのオプションこそが、オブジェクトのデータと振る舞いです。

Vue では、いくつか特殊な記法を用いることになります。

  • @~ イベントをハンドリングします。@click="..." と書けば、クリックしたときの処理(JavaScript)を記述することができます。また、オブジェクトのメソッドを指定することができます。@click="inc" であれば、クリックされたときに inc メソッドを呼び出すということになります。

  • {{ ~ }} オブジェクトのデータを表示します。{{ count }} であれば、オブジェクトの count の値が表示されます。

  • v-model, v-for などの特殊な属性 別記事にて説明

inc メソッドがいかにもオブジェクト的な処理になっていることがおわかりいただけるかと思います。inc メソッド内では、その count をどこに表示するかとか、何がきっかけに呼ばれているのかだとかを意識せず、単に自分のデータである count をインクリメントしています。

ポイント:

  • Vue は Vue オブジェクトが中心となり、それは HTML の任意の要素と結びつく

  • データを表示するときは {{ ~ }} を使う

  • イベントをハンドリングするときは @~ を使う




Vue.js でリアクティブな動作を見る (2)

こちらが前回の例

ですが1つ書き忘れていたことがありました。

ラベルの表示内容はオブジェクト { name: '' } の name の変化に反応して変わるわけですが、
このオブジェクトのプロパティ name の値もまた、input タグの入力内容に反応して自動的に内容が書き換わります。

つまり、
input タグに入力する
 → それに反応してオブジェクトのnameが書き換わる
  → それに反応してラベルの表示内容が書き換わる
というわけです。
Vue.js では、この動作についての処理を書く必要はありません。処理を書くのではなく、v-model 属性や {{ name }} のような Vue.js 固有の書き方(しかしトランスパイラ等を必要としません!)でこの動作を宣言的に定義することになります。

これまでは、
input タグに入力する
・その input タグの内容を $('#name').val() などを使用して取得する
・取得した値を $('#name-label').text などを使用してセットする
という JavaScript の処理を書かなければなりませんでした。

この極めてシンプルな例では、Vue.js を使用する場合としない場合とであまり差異を感じられないかもしれません。しかし、Web アプリケーションのフロント部分の基本的な機能構成は、 ・サーバサイドデータを画面要素に表示 ・エンドユーザの画面要素への入力 ・エンドユーザの入力内容のサーバへの送信 からなり、通常の Web アプリケーションでは画面要素が1つ2つなどということはありませんから、画面要素とモデルデータとのつなぎ込みは膨大な量になります。

Vue.js を使用することで、その画面要素とモデルデータをつなぎ込む処理から解放されることになります。
もちろん、「どの要素がどのデータに対応する」ということは宣言的に書かなければなりません。しかしそれでも、コードの見通しは非常に良くなると感じます。




Vue.js でリアクティブな動作を見る

テキストボックスの内容を、別のラベルの中に転記したいとします。
jQuery を使った典型的なコードでは、以下のようになります。


これは、

  • テキストボックスへの入力が発生したら
  • テキストボックスの要素を取得してそのvalue値を取得し
  • ラベルの要素を取得してその内容にセット

しています。当たり前ですが、自分でテキストボックスの内容をラベルに移しているわけです。

一方、Vue.js を使用すると、以下のようになります。


こちらのバージョンでは、自分でテキストボックスの値を取得してラベルに設定する、ということを行っていません。
Vue オブジェクトのコンストラクタに渡しているオブジェクト { name:'' } の name の値が変化することにより、それに反応して(reactive)ラベルの内容が書き換わっています。

これまでは、

  • DOM <-> データ のやり取り
  • データ <-> サーバサイドのやり取り

を自分で書かなければいなかったわけですが、Vue.js を導入することにより、データの取り扱いに注力することができるようになります。

Selenium WebDriver を使う (C#) (3)

WebDriver を使ってよくやることをまとめます。

ブラウザのウィンドウを最大化する

var url = "<任意のURL>";
driver.Manage().Window.Maximize();


任意のURLに移動する

var url = "<任意のURL>";
driver.Navigate().GoToUrl(url);


「戻る」操作を行う

var url = "<任意のURL>";
driver.Navigate().Back();


JavaScript を実行する

((IJavaScriptExecutor)driver).ExecuteScript("window.focus();");
// スクリプトが値を return すれば ExecuteScript の戻り値として取れる


C# 側で取得した要素を JavaScript に渡す

// element は IWebElement arguments[0] にそのオブジェクトが設定されている
((IJavaScriptExecutor)executor.Driver).ExecuteScript("alert(arguments[0]);", element);


表示されている要素だけ取得する

var elements = driver.FindElements(by).Where(o => o.Displayed).ToList();


alert を OK する

driver.SwitchTo().Alert().Accept();
// キャンセルする場合は driver.SwitchTo().Alert().Dismiss();
// SwitchTo を使えば処理対象を iframe に切り替えることもできる


テキストボックス(input)にテキストを書き込む

var text = "abc";
element.SendKeys(text);
// もともとテキストボックスに入力されている値を削除してから入力する場合は、element.Clear() してから SendKeys する


マウスカーソルをホバーする

new Actions(driver)
    .MoveToElement(element) // element ホバーしたい要素
    .Perform();


セレクトボックスの選択肢を選択する

var select = new SelectElement(element);
select.SelectByValue(value);
// 選択する方法として他に、SelectByText SelectByIndex などがある


セレクトボックスで選択されている値を取得する

var select = new SelectElement(element);
var option = select.SelectedOption;
// 未選択の場合は option が null になるので注意




Selenium WebDriver を使う (C#) (2)

前回 は WebDriver を使ってブラウザを起動する方法を確認しました。
今回は HTML 要素の取得について確認します。

WebDriver で行えることは、「Web ページの操作」です。Web ページの操作の大部分は HTML の要素を操作することです。
つまり HTML の要素を扱うことが WebDriver で行うことのメインになります。

HTML 要素の操作の第一段階として、まずは HTML 要素に相当するオブジェクトを得る必要があります。
そのオブジェクトは、ドライバクラスの FindElement/FindElements メソッドを使用して取得します。

どの HTML 要素を取得するかをどうやって指定するか、その指定の仕方には、HTML タグ名や CSS セレクタXPath などがあり、そのどれを使うかは、FindElementXXX メソッドを使い分けるか、By クラスを使用して使い分けるかのいずれかになります。

// ID属性を指定して取得 FindElementById メソッドを使う
var element = driver.FindElementById("hoge");
// 他にも
// CSSセレクタで取る driver.FindElementByCssSelector とか
// name 属性で取る driver.FindElementByName とか
// XPath で取る FindElementByXPath とかがある

// XPath を指定して取得 By クラスを使う
var element2 = driver.FindElement(By.Id("hoge"));
// 他にも By.CssSelector とか By.Name とか、FindElementByXXX に対応した By メソッドがある
// というよりも FindElementByXXX の方が By を使わないですむ糖衣構文のようなもの

上記の2つは同じことをしていますが、ID 属性を指定して取得する専用のメソッド を使用するか、By オブジェクトによって方法を伝えるか の違いがあります。

いずれの方法を用いても、これにより IWebElement のオブジェクトを取得することができます。これが1つの HTML 要素に対応するオブジェクトです。
このオブジェクトを通じて、HTML 要素を操作します。

// タグ名を取得
var tagName = element1.TagName;

// innerText を取得
var text = element1.Text;

// class 属性の値を取得
var classValue = element1.GetAttribute("class");

// 要素をクリック
element1.Click();