LoginSignup
1
0

More than 1 year has passed since last update.

NativeScript-Vueのコンポネート親子関係(props、$emit)【コピペできるコード付き】

Posted at

注意

本記事の公開日は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ファイルに以下のように書き換えてください。
(実際に動かさない方はここをスキップしてください)

ParentPage.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>
ButtonOfChangingTitle.vue
<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

ButtonOfChangingTitle.vue
<script>
    export default {
        props: {
            text: String
        },
    };
</script>

props:{}の中で、受け取りたいデータのプロパティ名を指定します。
propsのプロパティの定義についてはVue.js公式で詳しく紹介されています。
今回はtextというString型を受け取ります。

HTML

ButtonOfChangingTitle.vue
<template>
    <Button :text="text" class="custom-button" />
</template>

ボタンの文字列に、propsで受け取るtextにバインディングします。
その結果、ボタンには親が指定した文字列が表示されます。
見やすくためにcss classを入れました。挙動には影響がありません。

親コンポネート

子コンポネートが用意できたので、今度を親コンポネートの方を実装します。

JavaScript
ParentPage.vue
<script>
    // 使いたいコンポネートをまずimport
    import ButtonCT from "./elements/ButtonOfChangingTitle";
    export default {
        // そしてコンポネートとして登録する
        components: {
            ButtonCT
        },

        data() {
            return {
                title: "Title"
            };
        },
    };
</script>

さて、ここは一旦データ渡しから離れて、コンポネートの登録をします。
まず、スクリプトをimportします。絶対パスも相対パスも使えます。
そして、componentsの中に入れて、登録します。
そうすると、HTML部分で通常のコンポネートと同じ使い方ができます。

下のdata()には、タイトルにバインディングするためのtitleを定義します。
デフォルトの値は"Title"です。

HTML
ParentPage.vue
    <!-- タイトル表示 -->
    <Label :text="title" row="0" class="title" />

まず、<Label />text="Title":text="title"に書き換えてください。
:を付けたことによって、文字列の"Title"ではなく、動的に変数のtitleを表示することに変わります。
この変数titleは、ついさっきdata()に設定したtitleのことです。

ParentPage.vue
    <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と紐付けます。

GridLayoutcolはUI表示用。挙動に影響ありません)

子から親のデータ渡し($emit)

今度はボタンがタップされた時に、
親に自身の文字列(=propsで受け取った文字列)を渡して、親が表示しているタイトルを変更させます。
$emitを使うことによって、子が親のメソッドを呼ぶことが可能になり、その際引数にデータを渡すこともできます。

親コンポネート

今度は親コンポネートの方から実装していきます。

JavaScript
ParentPage.vue
<script>
    export default {
        // componentsを省略

        // data()を省略

        methods: {
            changeText(result) {
                this.title = result;
            }
        }
    };
</script>

methods:{}を追加し、中にchangeTextメソッドを作成します。
引数のresultを変数titleに代入するメソッドです。
titleは先ほど作成したdata()の中で定義されています)

HTML
ParentPage.vue
    <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

ButtonOfChangingTitle.vue
<script>
    export default {
        // propsを省略

        methods: {
            changeText() {
                this.$emit("changeText", this.text);
            }
        }
    };
</script>

methods:{}を追加し、親コンポーネントと同様、中にchangeTextメソッドを作成します。
これは親コンポーネントのchangeTextメソッドを実行するためのメソッドです。
this.$emit()の第一引数に呼ぶ関数、第二引数に親に渡す文字列(今回の場合はpropsで定義したtext)を設定します。

完成形

全部合わせるとこんな感じになります。

ParentPage.vue
<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>
ButtonOfChangingTitle.vue
<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


  1. $navigateToで画面遷移時のprops渡しならありますが。 

  2. 今考えてみれば、パニックによる凡ミスだと思います。 

1
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
1
0