注意
本記事の公開日は2021年となっておりますが、コードや記事のベースを書いたのは2020年です。
(コードはすぐ用意できたのに、記事の方は書いたりやめたり、なんやかんやで一年以上かかってしまった理由で)
もったいないので供養の意味を兼ねて投稿しました。
そのため、記述は Vue 2.x と NativeScript 6.x を前提にしています。
最新バージョンでは通用しない可能性があります。
はじめに
NativeScript-VueはVue.jsとほぼ変わらないでコンポネートを扱うことができます。
しかしながら公式にその記述がない1し、使おうとしてパニックになり使えないまま諦めてしまう2こともあったので、
「NativeScript-Vueでも同じ使い方できるから安心して」と誰かに教えてもらいたかったため、執筆しました。
内容は大したことなくて、Vue.jsに慣れている方ならすでに分かっていると思います。
繰り返しますが、本記事はあくまで、
「NativeScript-VueもVue.jsと同じ使い方できる」のを伝いたいだけです。
対象読者は、以下のどちらに当てはまる方になると思います。
- コンポネートや親子関係の概念が分かっている
- Vue.jsでのprops、$emitは分かるが、NativeScript-Vueはどんな感じかを見たい
- コンポネートや親子関係はよく分かってないけど、概念を読むより、コピペして実際に動かしてみたい
今回は実装重視なので、概念のことは一切触れません。
コードは全公開します。
⇨ コード全文
とりあえず画面
起動画面
非常にシンプルです。
ヘッダーに「Home」、画面上部にタイトルの「Title」と、下部に
一列のボタン「First」「Second」「Third」があります
ボタンタップ
今回のファイル構造
app
├── app.css
├── app.js
└── components
├── ParentPage.vue
└── elements
└── ButtonOfChangingTitle.vue
ParentPage
が唯一のページで、
elements
のディレクトリの下にはButtonOfChangingTitle
という子コンポネートがあります。
流れ的には、ページ(親)からボタン(子)へ文字列を渡します。
ボタンは親からの文字列を表示します。
タップされた時に、受け取った文字列を親に返して、親がそれを自身(ページ)のタイトルにします。
ちょっとバカバカしい設計になっています。
同じデータが一周回ってますからね。
そこは大目に見ていただければ助かります。
事前準備
以上のファイル構造を用意できたら、二つのVueファイルに以下のように書き換えてください。
(実際に動かさない方はここをスキップしてください)
<template>
<Page>
<ActionBar title="Home" />
<Label text="Title" row="0" class="title" />
<GridLayout rows="*, auto">
<!-- この後書き換えるよ -->
</GridLayout>
</Page>
</template>
<script>
export default {
// 後に追加
};
</script>
<style scoped>
.title {
font-size: 40;
horizontal-align: center;
}
</style>
<template>
<!-- この後書き換えるよ -->
</template>
<script>
export default {
// 後に追加
};
</script>
<style scoped>
.custom-button {
background-color: #1a1f54;
color: #FFF;
margin: 20;
}
</style>
Vueファイルのフォーマット、cssクラスと最小限のHTMLを定義しました。
この後のコードを適切な場所を入れると同じ動きになります。
最後には全体コードを置いてあるので、不安のある方を照らし合わせながらお進みください。
親から子のデータ渡し(props)
propsは親が子コンポーネントへのデータの受け渡しに使われます。
propsを通じて、
親からボタンに表示する文字列(=後に返してくる文字列)を渡します。
子コンポネート
まず、子コンポネートButtonOfChangingTitle
でpropsの設定をします。
JavaScript
<script>
export default {
props: {
text: String
},
};
</script>
props:{}の中で、受け取りたいデータのプロパティ名を指定します。
propsのプロパティの定義についてはVue.js公式で詳しく紹介されています。
今回はtext
というString型を受け取ります。
HTML
<template>
<Button :text="text" class="custom-button" />
</template>
ボタンの文字列に、propsで受け取るtext
にバインディングします。
その結果、ボタンには親が指定した文字列が表示されます。
見やすくためにcss classを入れました。挙動には影響がありません。
親コンポネート
子コンポネートが用意できたので、今度を親コンポネートの方を実装します。
JavaScript
<script>
// 使いたいコンポネートをまずimport
import ButtonCT from "./elements/ButtonOfChangingTitle";
export default {
// そしてコンポネートとして登録する
components: {
ButtonCT
},
data() {
return {
title: "Title"
};
},
};
</script>
さて、ここは一旦データ渡しから離れて、コンポネートの登録をします。
まず、スクリプトをimportします。絶対パスも相対パスも使えます。
そして、componentsの中に入れて、登録します。
そうすると、HTML部分で通常のコンポネートと同じ使い方ができます。
下のdata()
には、タイトルにバインディングするためのtitle
を定義します。
デフォルトの値は"Title"
です。
HTML
<!-- タイトル表示 -->
<Label :text="title" row="0" class="title" />
まず、<Label />
のtext="Title"
を:text="title"
に書き換えてください。
:
を付けたことによって、文字列の"Title"ではなく、動的に変数のtitle
を表示することに変わります。
この変数title
は、ついさっきdata()
に設定したtitle
のことです。
<GridLayout row="1" columns="*, *, *">
<!-- 子コンポーネントを読み込む -->
<ButtonCT text="First" col="0" />
<ButtonCT text="Second" col="1" />
<ButtonCT text="Third" col="2" />
</GridLayout>
次に、<!-- この後書き換えるよ -->
の部分をこのように書き換えてください。
JavaScript部分でButtonCT
を定義したので、HTMLで<ButtonCT />
が使えるようになりました。
プロパティtext
で表示する文字列(=後に返してくる文字列)を指定します。
このtext
が子コンポネートのpropsで定義したtext
と紐付けます。
(GridLayout
とcol
はUI表示用。挙動に影響ありません)
子から親のデータ渡し($emit)
今度はボタンがタップされた時に、
親に自身の文字列(=propsで受け取った文字列)を渡して、親が表示しているタイトルを変更させます。
$emitを使うことによって、子が親のメソッドを呼ぶことが可能になり、その際引数にデータを渡すこともできます。
親コンポネート
今度は親コンポネートの方から実装していきます。
JavaScript
<script>
export default {
// componentsを省略
// data()を省略
methods: {
changeText(result) {
this.title = result;
}
}
};
</script>
methods:{}を追加し、中にchangeText
メソッドを作成します。
引数のresult
を変数title
に代入するメソッドです。
(title
は先ほど作成したdata()の中で定義されています)
HTML
<GridLayout row="1" columns="*, *, *">
<ButtonCT text="First" @changeText="changeText" col="0" />
<ButtonCT text="Second" @changeText="changeText" col="1" />
<ButtonCT text="Third" @changeText="changeText" col="2" />
</GridLayout>
先ほど作成した<ButtonCT />
に@changeText="changeText"
を追加します。
changeText
に統一しましたが、別に同じ名前じゃなくても問題ありません。
前半の@changeText
は子コンポーネント内に使う名前で、後半の"changeText"
は親コンポーネント内に使う名前です。
子コンポーネント<ButtonCT />
にchangeText
メソッドを渡したことによって、
子コンポーネントから呼べるようになります。
子コンポネート
親コンポーネントの準備が終わったので、いよいよ子コンポネートで呼んでみます。
JavaScript
<script>
export default {
// propsを省略
methods: {
changeText() {
this.$emit("changeText", this.text);
}
}
};
</script>
methods:{}を追加し、親コンポーネントと同様、中にchangeText
メソッドを作成します。
これは親コンポーネントのchangeText
メソッドを実行するためのメソッドです。
this.$emit()
の第一引数に呼ぶ関数、第二引数に親に渡す文字列(今回の場合はpropsで定義したtext
)を設定します。
完成形
全部合わせるとこんな感じになります。
<template>
<Page>
<ActionBar title="Home" />
<GridLayout rows="*, auto">
<Label :text="title" row="0" class="title" />
<GridLayout row="1" columns="*, *, *">
<ButtonCT text="First" @changeText="changeText" col="0" />
<ButtonCT text="Second" @changeText="changeText" col="1" />
<ButtonCT text="Third" @changeText="changeText" col="2" />
</GridLayout>
</GridLayout>
</Page>
</template>
<script>
import ButtonCT from "./elements/ButtonOfChangingTitle";
export default {
components: {
ButtonCT
},
data() {
return {
title: "Title"
};
},
methods: {
changeText(result) {
this.title = result;
}
}
};
</script>
<style scoped>
.title {
font-size: 40;
horizontal-align: center;
}
</style>
<template>
<Button :text="text" @tap="changeText" class="custom-button" />
</template>
<script>
export default {
props: {
text: String
},
methods: {
changeText() {
this.$emit("changeText", this.text);
}
}
};
</script>
<style scoped>
.custom-button {
background-color: #1a1f54;
color: #FFF;
margin: 20;
}
</style>
さいごに
$refsについて言及していませんでしたが、NativeScriptでの使い方もまた一緒です。
参考資料
emit
https://forum.vuejs.org/t/passing-data-back-to-parent/1201
prop(Typescript)
https://www.nativescript.org/blog/nativescript-vue-with-class-components
-
$navigateToで画面遷移時のprops渡しならありますが。 ↩
-
今考えてみれば、パニックによる凡ミスだと思います。 ↩