プログラミング

Vue.jsでWEBアプリを作り、それをNativeScriptでスマホアプリにする

プログラミング
この記事は約44分で読めます。

0.ご覧になる方へ

これは、私がVue.jsとNativeScriptの学習したときの忘備録です。

内容は、初歩的な機能のWEBアプリを作成し、それをAndroidスマホで動作できるようしたもので、実際アプリの動作確認まではしたのですが、途中で終わっています(一旦放棄)

また、私自身、Vue.jsまたはNativeScriptに触ったばかりで、不慣れな点もあり、この項をそのまま参考にされても変な癖がついてしまう恐れがあります。

もし、参考にされるのであれば、こういう事情を汲んだうえで、参考にしてください。

アプリの仕様

今回は、パスワード生成ツールを作ります。

私は今までも、パスワード生成ツールを使っていたのですが、文字が小さいという問題がありました。

そこで、同じような機能をWEBアプリで作成、そしてそれをさらにスマホアプリで作り変えます。

  • ボタン押下で、ランダム文字列が変わる
  • 文字種は、数字・英小文字・英大文字・記号
  • 初期状態では、文字種はすべて選択している。
  • ランダム文字列は、4文字ごと、全16文字固定。
  • 画面遷移なし

まじめに作ろうとしたら、もっと複雑になるのですが、簡単にしたかったので、こういう仕様にしました。

最終的には、こんなAndroidスマホ上で画面になりました。

「パスワード発生」ボタンを押すと、右側のようにパスワード文字列が発生されます。

1.Vue.js編

1-0.Vue.jsで、お世話になった書籍

そもそも私は、Vue.jsとは何か、ほとんどわかっていませんでした。

Vue.jsについては、参考になる入門書が多数出版されており、どれにしようか迷うほどでしたが、私が選んだのはこれでした。

これは、この本の通りに学んでいけば、Vue.jsの最小限のことがわかるという内容で、私もだいぶお世話になりました。

ただ、オリジナルアプリを作る段になると、かえって順を追った内容が邪魔になり、かといっていきなりリファレンスを読んでもよくわからなくなるということで(本による学習と、オリジナルアプリを作成した時期の間にブランクが入ってしまい、Vue.jsのことをだいぶ忘れてしまったこともあるのですが)、今回作ったアプリは、そちらの方で苦労しました。

なお、今回作ったアプリは、この本に書いてあるソースをアレンジして作っています。

また、NativeScriptについては、ほとんど和書がない状態ですが、公式サイトで一応日本語で書いてありました。

1-1.Vue.js(単一ファイルコンポーネント化前)によるもの

実行するとこうなります。

(この時点では、文字種による選択機能は、まだついていません。)

ファイル構成です。

main.htmlの中に親コンポーネント、components/random.jsに子コンポーネントがあります。

main.html
<!DOCTYPE html>
<html lang="ja">
<head>
  <meta charset="utf-8">
  <title>ランダム文字列</title>
  <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/normalize/5.0.0/normalize.min.css">
  <link rel="stylesheet" href="main.css">
</head>
<body>
<div id="app">
  <!-- Vueコンポーネントmine-product(表示処理)による処理 -->
  <!-- (3)子(mine-product)の「child-click」イベントが発生したら、親の「onRandomStr」を呼び出す  -->
  <mine-product v-on:child-click="onRandomStr" v-bind:rnd-str="rndStr" ></mine-product>
</div>
<script src="https://jp.vuejs.org/js/vue.js"></script>
<!-- 子コンポーネントmine-productがある場所の宣言 -->
<script src="components/random.js"></script>
<!-- 親コンポーネントがある場所の宣言 -->
<script src="main.js"></script>
</body>
</html>

ここでは実質上の処理は13行目のみで、あとは宣言関係ですね。

<mine-product>というタグがあるのですが、これはあとで出てくるこちら側で自由に名づけができるカスタムタグです。

components/random.js
Vue.component('mine-product', {
  // テンプレート部分
  template: `
    <div>
      <span class="rndStrEach" v-for="rndStrEach in rndStr">
        {{rndStrEach}}
      </span>
      <!-- (1)「パスワード発生」ボタンの表示。押されたら「clickHandler」を呼ばれる(下のmethods) -->
      <button v-on:click="clickHandler">パスワード発生</button>
    </div>`,
  props: ['rndStr'],
    // メソッド
  methods: {
    clickHandler: function(){
      // (2) 「パスワード発生」ボタンが押されたら、子コンポーネントにカスタムイベント「child-click」を発生させる
      this.$emit('child-click');
    }
  }
});

5~7行目はパスワードの表示部分。

rndStrは後述のmain.jsで宣言された配列で、パスワードが4文字ずつ4個入っています。

このrndStrはpropsオプション(11行目)で渡ってきます。

それをv-forを使って表示しています。

9行目はボタン部分で、押されたら「clickHandler」を発生させます。

そして、clickHandler(14行目)の中の$emitを使って、さらに「child-click」イベントを発生させます。

main.html(再掲)
   <!-- (3)子(mine-product)の「child-click」イベントが発生したら、親の「onRandomStr」を呼び出す  -->
  <mine-product v-on:child-click="onRandomStr" v-bind:rnd-str="rndStr" ></mine-product>

main.htmlの<mine-product>のコメントの通り、「child-click」イベントが発生されているので、ここでmain.jsの「onRandomStr」イベントを発生させます。

なお、v-bindのrnd-strは、ここでしか使っていない属性名です。このv-bindがないと、パスワードは表示されません。

ちなみに、v-bindの書き方は、こんな書き方です。

<要素名 v-bind:属性名=”プロパティ名”>

main.html,random.jsそしてmain.jsの関係は、下図の通りになります。

main.js
var app = new Vue({
  el: '#app',
  data: {
    rndStr : '開始',
    rndStrLen : 0,
    targetStrNum : '0123456789',
    targetStrLower : 'abcdefghijklmnopqrstuvwxyz',
    targetStrSymbol1 : "'",
    targetStrSymbol2 : '!"#$%&()*+,-./:;<>=?@[]^_`{}|~',
  },
  methods: {
    // (4)子から呼び出されるメソッド
    onRandomStr: function() {
      //console.log(this.targetStrNum);
      targetStrUpper = this.targetStrLower.toUpperCase();
      targetStrSymbol = this.targetStrSymbol1 + this.targetStrSymbol2;
      targetStr = this.targetStrNum + this.targetStrLower + targetStrUpper + targetStrSymbol;
      targetLen = targetStr.length;
      // 乱数発生
      let uInt32 = new Uint32Array(1);
      this.rndStr = [];
      this.rndStrLen = 16;
      cntL = 0;
      while ( cntL < this.rndStrLen ) {
        cntA = Math.floor(cntL / 4);
        if (cntL % 4 == 0) {
          this.rndStr[cntA] = '';
        }
        crypto.getRandomValues(uInt32); // 符号なし32bitの数値を入れる
        targetStrSta = Math.floor(uInt32[0]/(2**32)*targetLen)+1;
        this.rndStr[cntA] = this.rndStr[cntA] + targetStr.substr(targetStrSta,1);
        cntL = cntL + 1;
      }
    }
  }
});

こちらの、onRandomStrが呼び出されて、rndStrの配列にパスワードが入ります。

ここで、実際の乱数発生部は、36行目の

        crypto.getRandomValues(uInt32); // 符号なし32bitの数値を入れる

なんですが、乱数発生でMath.random()を使わずに、あえて暗号強度の高いcrypto.getRandomValues()を使ったわけですが、ここでは良かったのですが、NativeScriptに変換したときに問題になるとは、、、

1-2.Vue.js(単一ファイルコンポーネント化後)によるもの

実行するとこうなります。

(まだ、文字種による選択機能は、まだありません。)

インストールと初期設定

ここからVue CLIを使って、作っていきます。

注)Vue CLIのVer.2が入っている環境では、一旦アンインストール(Ver3からパッケージ名が変わっているため)

npm uninstall -g vue-cli

そして、インストールは、

npm install -g @vue/cli

そして起動します

vue ui

すると、こういうような画面が出てきて、Vue CLIが起動されます。

そして、プロジェクトを作っていきます。

randomというプロジェクトを作成します。

そして、空きのプロジェクトとしてビルドしていき、空きのプロジェクトの画面(Hello world的な)を表示させます。

Vue CLIによって、次のファイルが生成されていきます。

この、random/src以下に、前項で作ったソースを単一コンポーネントにしたものを書き込んでいきます。

モジュール化前のアプリの移行

書き込む物のファイル構成です。random/srcの構成が、次のように変わります。

ソースの説明と変更点

App.vue
<template>
  <div id="app">
    <Random/>
  </div>
</template>
<script>
import Random from './components/Random'
export default {
  name: 'App',
  components: {
    Random
  }
}
</script>
<style>
#app {
  font-family: 'Avenir', Helvetica, Arial, sans-serif;
  -webkit-font-smoothing: antialiased;
  -moz-osx-font-smoothing: grayscale;
  text-align: center;
  color: #2c3e50;
  margin-top: 60px;
}
</style>

3行目、8行目と13行目に、Random.vueの読み込み設定があります。

components/Random.vueですが、1-1項で作成したcomponents/random.jsとmain.jsがもとになります。

components/Random.vue
<template>
  <div id="app">
    <span v-for="rndStrEach in rndStr" v-bind:key = "rndStrEach.id">
      <span class="rndStrEach">{{rndStrEach}}</span>
    </span>
    <!-- (1)「パスワード発生」ボタンの表示。押されたら「onRandomStr」を呼ばれる(下のmethods) -->
    <button class="btn" v-on:click="onRandomStr">パスワード発生</button>
  </div>
</template>
<script>
export default {
  name: 'Random',
  mounted: function(){
		document.title = "ランダム文字列";
	},
  data() {
    return {
      rndStr : '開始',
      rndStrLen : 0,
      targetStrNum : '0123456789',
      targetStrLower : 'abcdefghijklmnopqrstuvwxyz',
      targetStrSymbol1 : "'",
      targetStrSymbol2 : '!"#$%&()*+,-./:;<>=?@[]^_`{}|~',
    }
  },
  methods: {
  // (2)子から呼び出されるメソッド
    onRandomStr: function() {
      //console.log(this.targetStrLower);
      let targetStrUpper = this.targetStrLower.toUpperCase();
      let targetStrSymbol = this.targetStrSymbol1 + this.targetStrSymbol2;
      let targetStr = this.targetStrNum + this.targetStrLower + targetStrUpper + targetStrSymbol;
      let targetLen = targetStr.length;
      // 乱数発生
      let uInt32 = new Uint32Array(1);
      this.rndStr = [];
      this.rndStrLen = 16;
      let cntL = 0;
      while ( cntL < this.rndStrLen ) {
        let cntA = Math.floor(cntL / 4);
        if (cntL % 4 == 0) {
          this.rndStr[cntA] = '';
        }
        crypto.getRandomValues(uInt32); // 符号なし32bitの数値を入れる
        let targetStrSta = Math.floor(uInt32[0]/(2**32)*targetLen)+1;
        this.rndStr[cntA] = this.rndStr[cntA] + targetStr.substr(targetStrSta,1);
        cntL = cntL + 1;
      }
    }
  }
}
</script>
<!-- Add "scoped" attribute to limit CSS to this component only -->
<style scoped>
  .capitalize {
      text-transform: capitalize;
  }
  .rndStrEach {
    font-size: 32px;
    padding-right: 16px;
  }
</style>
Random.vueの説明
<templete>ブロック
<template>
  <div id="app">
    <span v-for="rndStrEach in rndStr" v-bind:key = "rndStrEach.id">
      <span class="rndStrEach">{{rndStrEach}}</span>
    </span>
    <!-- (1)「パスワード発生」ボタンの表示。押されたら「onRandomStr」を呼ばれる(下のmethods) -->
    <button class="btn" v-on:click="onRandomStr">パスワード発生</button>
  </div>
</template>

モジュール化前のrandom.jsを再度掲載します。

Vue.component('mine-product', {
  // テンプレート部分
  template: `
    <div>
      <span class="rndStrEach" v-for="rndStrEach in rndStr">
        {{rndStrEach}}
      </span>
      <!-- (1)「パスワード発生」ボタンの表示。押されたら「clickHandler」を呼ばれる(下のmethods) -->
      <button v-on:click="clickHandler">パスワード発生</button>
    </div>`,
  props: ['rndStr'],
    // メソッド
  methods: {
    clickHandler: function(){
      // (2) 「パスワード発生」ボタンが押されたら、子コンポーネントにカスタムイベント「child-click」を発生させる
      this.$emit('child-click');
    }
  }
});

ここから変わった部分は、

コンポーネント化前のrandom.jsの5行目の

<span class="rndStrEach" v-for="rndStrEach in rndStr">

が、Random.vueでは、

<span v-for="rndStrEach in rndStr" v-bind:key = "rndStrEach.id">

となり、v-bind:key = “rndStrEach.id”が追加になること、

そして、random.jsの9行目のv-on:click=の呼び出し先が、clickHandlerから、Random.vueでは、onRandomStr(同じRandom.vue中の<script>ブロックのmethodsオプションの中)に変更になったことです。

<script>ブロック

基本は、前項(1-1項)のmain.jsと細部を除き同じですが、letによる変数の宣言が必要になってきます。

<style>ブロック

前項(1-1項)のmain.cssに相当しています。

実は前項では省略したのですが、こんな設定がしてあります。

1-3.Vue.js(最初からコンポーネントを意識した機能追加)

実行するとこうなります。

文字種による選択部分が増えました。

ソースの説明と変更点

ファイル構成は、次のようになります。

componentsの中にSelect.vueが追加されます。App.vueも変更点があります。

今回は、いきなりSelect.vueを追加してみよう、という試みになります。

App.vue
<template>
  <div id="app">
    <Select
      v-bind:chkTypeNum="chkTypeNum"
      v-bind:chkTypeLow="chkTypeLow"
      v-bind:chkTypeHigh="chkTypeHigh"
      v-bind:chkTypeSymbol="chkTypeSymbol"
      v-on:chkTypeNumCheanged="chkTypeNum=!chkTypeNum"
      v-on:chkTypeLowCheanged="chkTypeLow=!chkTypeLow"
      v-on:chkTypeHighCheanged="chkTypeHigh=!chkTypeHigh"
      v-on:chkTypeSymbolCheanged="chkTypeSymbol=!chkTypeSymbol"
    />
    <Random
      v-bind:chkTypeNum="chkTypeNum"
      v-bind:chkTypeLow="chkTypeLow"
      v-bind:chkTypeHigh="chkTypeHigh"
      v-bind:chkTypeSymbol="chkTypeSymbol"
    />
  </div>
</template>
<script>
import Random from './components/Random'
import Select from './components/Select'
export default {
  name: 'App',
  components: {
    Select,
    Random
  },
  data: function(){
    return{
      chkTypeNum: true,
      chkTypeLow: true,
      chkTypeHigh: true,
      chkTypeSymbol: true
    }
  }
}
</script>
<style>
#app {
  font-family: 'Avenir', Helvetica, Arial, sans-serif;
  -webkit-font-smoothing: antialiased;
  -moz-osx-font-smoothing: grayscale;
  text-align: center;
  color: #2c3e50;
  margin-top: 60px;
}
</style>

Select.vueの新設

Select.vueの追加のほかに、文字種(chkTypeNumなど)の変更とその結果、そして初期値が、追加になっています。

そして、新設のSelect.vue

components/Select.vue
<template>
  <div id="sel">
    <div id="selChd">
      <label>
        <input type="checkbox"
          v-bind:checked="chkTypeNum"
          v-on:change="$emit('chkTypeNumCheanged')"
          >数字
      </label>
      <label>
        <input type="checkbox"
          v-bind:checked="chkTypeLow"
          v-on:change="$emit('chkTypeLowCheanged')"
          >英小文字
      </label>
      <label>
        <input type="checkbox"
          v-bind:checked="chkTypeHigh"
          v-on:change="$emit('chkTypeHighCheanged')"
          >英大文字
      </label>
      <label>
        <input type="checkbox"
          v-bind:checked="chkTypeSymbol"
          v-on:change="$emit('chkTypeSymbolCheanged')"
          >記号
      </label>
      </div>
  </div>
</template>
<script>
export default {
  name: 'Select',
  props: ['chkTypeNum', 'chkTypeLow', 'chkTypeHigh', 'chkTypeSymbol']
}
</script>
<!-- Add "scoped" attribute to limit CSS to this component only -->
<style scoped>
  #sel {
    display: flex;
    justify-content: center;
    align-items: center;
    height: 100px;
    color: white;
    background-color : blue;
  }
  #selChd {
  }
  .capitalize {
      text-transform: capitalize;
  }
</style>

チェックボックスを使っているため、<input type=”checkbox” ~>を使っています。

しかしこれが、NativeScriptへ移行したときに使えないという問題が、、、

components/Random.vue
<template>
  <div id="app">
    <span v-for="rndStrEach in rndStr" v-bind:key = "rndStrEach.id">
      <span class="rndStrEach">{{rndStrEach}}</span>
    </span>
    <!-- (1)「パスワード発生」ボタンの表示。押されたら「onRandomStr」を呼ばれる(下のmethods) -->
    <button class="btn" v-on:click="onRandomStr">パスワード発生</button>
  </div>
</template>
<script>
export default {
  name: 'Random',
  mounted: function(){
		document.title = "ランダム文字列";
	},
  props: ['chkTypeNum', 'chkTypeLow', 'chkTypeHigh', 'chkTypeSymbol'],
  data() {
    return {
      rndStr : '開始',
      rndStrLen : 0,
      targetStrNum : '0123456789',
      targetStrLow : 'abcdefghijklmnopqrstuvwxyz',
      targetStrSymbol1 : "'",
      targetStrSymbol2 : '!"#$%&()*+,-./:;<>=?@[]^_`{}|~',
    }
  },
  methods: {
    //(2)子から呼び出されるメソッド
    onRandomStr: function() {
      let targetStrHigh = this.targetStrLow.toUpperCase();
      let targetStrSymbol = this.targetStrSymbol1 + this.targetStrSymbol2;
      let targetStr = "";
      if (this.chkTypeNum) {targetStr += this.targetStrNum;}
      if (this.chkTypeLow) {targetStr += this.targetStrLow;}
      if (this.chkTypeHigh) {targetStr += targetStrHigh;}
      if (this.chkTypeSymbol) {targetStr += targetStrSymbol;}
      let targetLen = targetStr.length;
      // 乱数発生
      let uInt32 = new Uint32Array(1);
      this.rndStr = [];
      this.rndStrLen = 16;
      let cntL = 0;
      while ( cntL < this.rndStrLen ) {
        let cntA = Math.floor(cntL / 4);
        if (cntL % 4 == 0) {
          this.rndStr[cntA] = '';
        }
        crypto.getRandomValues(uInt32); // 符号なし32bitの数値を入れる
        let targetStrSta = Math.floor(uInt32[0]/(2**32)*targetLen);
        this.rndStr[cntA] = this.rndStr[cntA] + targetStr.substr(targetStrSta,1);
        cntL = cntL + 1;
      }
    }
  }
}
</script>
<!-- Add "scoped" attribute to limit CSS to this component only -->
<style scoped>
  .capitalize {
      text-transform: capitalize;
  }
  .rndStrEach {
    font-size: 32px;
    padding-right: 16px;
  }
</style>

17行目で、Select.vueからのデータ受け渡しである

props: ['chkTypeNum', 'chkTypeLow', 'chkTypeHigh', 'chkTypeSymbol'],

文字種選択が入るのと、文字種選択によって、パスワード対象文字(targetStr)が選択される処理が入ります。

2.NativeScript編

2-1.Vue.js用に作ったWEBアプリを、NativeScriptでスマホアプリ化する

今回の到達点です。

赤丸を付けたアプリが、今回作成したアプリです。

Androidスマホに、アイコン変更してデプロイまではできましたが、名前まで変えることができなかったので、ここで断念しています。

なお、このアプリを実行するとこうなります。

インストールと初期設定

NativeScript CLIをインストールします。

npm install -g nativescript

そして、テスト用のAndroidの実機を使用してテストします。

プロジェクトの作成は、まず作業用ディレクトリに移動して、Vue.js関係のパッケージをインストールします。

npm install -g @vue/cli @vue/cli-init

そして、プロジェクト名を決めたら(ここでは「random_ns」とします)、アプリのひな型を作成します。

vue init nativescript-vue/vue-cli-template random_ns

途中、いろいろ聞かれますが、今回はこうしました。

? Project name random_ns
? Project description A native application built with NativeScript-Vue
? Application name NativeScript-Vue Application
? Unique application identifier org.nativescript.application
? Project version 1.0.0
? Author
? License MIT
? Select the programming language javascript
? Select a preset (more coming soon) Simple
? Install vuex? (state management) Yes
? Install vue-devtools? No
? Color scheme none

「random_ns」というディレクトリが作成されますので、そこへ移動して、npmパッケージをインストールします。

cd random_ns
npm install

テスト方法

スマホ側に直接アプリをインストールしてテストを行うという方法もあるのですが、Nativescriptには「NativeScript Playground」という仕組みがあって、気軽にテストできますので、今回これを使います。

tns preview

すると、本当にコマンドライン上に巨大なQRコードが出現します。

スマホには、あらかじめ「NativeScript Playground」というアプリをインストールします。

インストールできたら起動して、「Scan QR code」をタッチします。

するとカメラが起動しますので、先ほどの巨大QRコードを「撮影」します。

そうすると画面遷移して、最終的には現在の「random_ns」の内容(最初に作られたアプリ)が表示されます。

ちなみに初期状態は、こうなります。

NativeScript-Vueの開発は、この中の「Hello World!」のアプリを改造、あるいは代替して行います。

参考資料

virtualiment「WSLのUbuntuでNativeScript+Vueの開発環境を構築する手順」

WSLのUbuntuでNativeScript+Vueの開発環境を構築する手順
NativeScriptは、Javascript、Angular.js、Vue.js、TypeScriptでアプリを作成することが可能なフレームワークです。この記事では、WSL上のUbuntu18.04にNativeScriptの開発環境を...

Vue.jsのアプリの移行

NativeScriptは、Vue.jsで作成したソースがそのまま流用できなかったので難儀したのですが、最終的にこうしました。

ファイル構成

ファイル構成的にNativeScriptの本来のものと違うような気がするのですが、今回はこう作りました。

ソースの説明と変更点

前項で登場したRandom.vueとSelect.vueが、random_ns/app/components/componentsの中に入っています。

random_ns/app/main.js(今回生成されたものの改造)
import Vue from 'nativescript-vue'
import App from './components/App'
import store from './store'
Vue.registerElement(
  'CheckBox',
  () => require('@nstudio/nativescript-checkbox').CheckBox,
  {
    model: {
      prop: 'checked',
      event: 'checkedChange'
    }
  }
);
// Prints Vue logs when --env.production is *NOT* set while building
//Vue.config.silent = (TNS_ENV === 'production')
Vue.config.silent = false
new Vue({
  render: h => h('frame', [h(App)])
}).$start()
チェックボックスが使えないので、プラグインをインストール

7~16行目の「Vue.registerElement~」が追加されているわけですが、これはNativeScript-vueでチェックボックスを使うためのプラグイン「nativescript-checkbox」(https://github.com/nstudio/nativescript-checkbox)を使うために追加したものです。

実は、NativeScript-vueでチェックボックスを使うときは、<input type=”checkbox”~が使えず、別途プラグインを使用します。

あと、このインストールはこうします。

tns plugin add @nstudio/nativescript-checkbox
random_ns/app/components/App.vue
<template>
    <Page
      actionBarHidden="true"
      xmlns="https://schemas.nativescript.org/tns.xsd"
      xmlns:CheckBox="@nstudio/nativescript-checkbox"
      loaded="pageLoaded"
    >
      <ActionBar title="どうしてぼくにはしっぽがないの" />
      <GridLayout colums="*" rows="60, 80, *">
        <Select
          col="0" row="0"
          backgroundColor="blue"
          v-bind:chkTypeNum="chkTypeNum"
          v-bind:chkTypeLow="chkTypeLow"
          v-bind:chkTypeHigh="chkTypeHigh"
          v-bind:chkTypeSymbol="chkTypeSymbol"
          v-on:chkTypeNumCheanged="chkTypeNum=!chkTypeNum"
          v-on:chkTypeLowCheanged="chkTypeLow=!chkTypeLow"
          v-on:chkTypeHighCheanged="chkTypeHigh=!chkTypeHigh"
          v-on:chkTypeSymbolCheanged="chkTypeSymbol=!chkTypeSymbol"
        />
        <Random
          col="0" row="1"
          v-bind:chkTypeNum="chkTypeNum"
          v-bind:chkTypeLow="chkTypeLow"
          v-bind:chkTypeHigh="chkTypeHigh"
          v-bind:chkTypeSymbol="chkTypeSymbol"
        />
      </GridLayout>
    </Page>
</template>
<script >
  import Select from './components/Select'
  import Random from './components/Random'
  export default {
    name: 'App',
    components: {
      Select,
      Random
    },
    data:function() {
      return {
        chkTypeNum: true,
        chkTypeLow: true,
        chkTypeHigh: true,
        chkTypeSymbol: true
      }
    }
  }
</script>
<style scoped>
    ActionBar {
        background-color: #53ba82;
        color: #ffffff;
    }
    .message {
        vertical-align: center;
        text-align: center;
        font-size: 20;
        color: #333333;
    }
</style>
App.vueの説明
<templeate>ブロック
<template>
    <Page
      actionBarHidden="true"
      xmlns="https://schemas.nativescript.org/tns.xsd"
      xmlns:CheckBox="@nstudio/nativescript-checkbox"
      loaded="pageLoaded"
    >
      <ActionBar title="どうしてぼくにはしっぽがないの" />
      <GridLayout colums="*" rows="60, 80, *">
        <Select
          col="0" row="0"
          backgroundColor="blue"
          v-bind:chkTypeNum="chkTypeNum"
          v-bind:chkTypeLow="chkTypeLow"
          v-bind:chkTypeHigh="chkTypeHigh"
          v-bind:chkTypeSymbol="chkTypeSymbol"
          v-on:chkTypeNumCheanged="chkTypeNum=!chkTypeNum"
          v-on:chkTypeLowCheanged="chkTypeLow=!chkTypeLow"
          v-on:chkTypeHighCheanged="chkTypeHigh=!chkTypeHigh"
          v-on:chkTypeSymbolCheanged="chkTypeSymbol=!chkTypeSymbol"
        />
        <Random
          col="0" row="1"
          v-bind:chkTypeNum="chkTypeNum"
          v-bind:chkTypeLow="chkTypeLow"
          v-bind:chkTypeHigh="chkTypeHigh"
          v-bind:chkTypeSymbol="chkTypeSymbol"
        />
      </GridLayout>
    </Page>
</template>

まずNativeScriptでは、<div>等が使えませんので、GridLayoutに変更しています。

ActionBarタグは必須のようですが、ここでは使わないのでどうしようか迷ったのですが、その上のPageタグのactionBarHiddenをtrueにすることで、非表示化することができます。

(だからこの落書きは表示されません)

Pageタグの中にあるほかのパラメータなのですが、これはmain.jsの項でもでた、nativescript-checkboxを使うためのものです。

SelectタグとRandomタグには、GridLayoutでの位置が入ります。また今回のSelectタグの背景色は、スタイルシートではなくSelectタグ中のパラメータに記述しました。

random_ns/app/components/components/Select.vue
<template>
  <StackLayout orientation=horizontal color=white>
    <check-box
      text="数字"
      :checked="chkTypeNum"
      @checkedChange="$emit('chkTypeNumCheanged')"
      fillColor="white"
      onCheckColor="white"
      tintColor="white"
    />
    <check-box
      text="英小文字"
      :checked="chkTypeLow"
      @checkedChange="$emit('chkTypeLowCheanged')"
      fillColor="white"
      onCheckColor="white"
      tintColor="white"
    />
    <check-box
      text="英大文字"
      :checked="chkTypeHigh"
      @checkedChange="$emit('chkTypeHighCheanged')"
      fillColor="white"
      onCheckColor="white"
      tintColor="white"
    />
    <check-box
      text="記号"
      :checked="chkTypeSymbol"
      @checkedChange="$emit('chkTypeSymbolCheanged')"
      fillColor="white"
      onCheckColor="white"
      tintColor="white"
    />
  </StackLayout>
</template>
<script>
export default {
  name: 'Select',
  props: ['chkTypeNum', 'chkTypeLow', 'chkTypeHigh', 'chkTypeSymbol']
}
</script>
<!-- Add "scoped" attribute to limit CSS to this component only -->
<style scoped>
</style>

チェックボックスの部分が、プラグインに変更されています。

パラメータについては、こちらを参考にしました。

skimemo NativeScriptのiOS対応時に気をつけること(https://www.webdb.co.jp/~atsumi/skimemo/index.php?skimemo%20-%20%C6%FC%B5%AD%2F2019-06-15%2FNativeScript%A4%CEiOS%C2%D0%B1%FE%BB%FE%A4%CB%B5%A4%A4%F2%A4%C4%A4%B1%A4%EB%A4%B3%A4%C8

random_ns/app/components/components/Random.vue
<template>
  <StackLayout  orientation="vertical" >
    <StackLayout  orientation="horizontal">
      <Label class="rndStrAny" :text="rndStrEach" v-for="rndStrEach in rndStr" v-bind:key="rndStrEach.id" />
    </StackLayout>
    <!-- (1)「パスワード発生」ボタンの表示。押されたら「onRandomStr」を呼ばれる(下のmethods) -->
    <button class="btn" v-on:tap="onRandomStr">パスワード発生</button>
  </StackLayout>
</template>
<script>
export default {
  name: 'Random',
  props: ['chkTypeNum', 'chkTypeLow', 'chkTypeHigh', 'chkTypeSymbol'],
  data: function()  {
    return {
      rndStr : '開始',
      rndStrLen : 0,
      targetStrNum : '0123456789',
      targetStrLow : 'abcdefghijklmnopqrstuvwxyz',
      targetStrSymbol1 : "'",
      targetStrSymbol2 : '!"#$%&()*+,-./:;<>=?@[]^_`{}|~',
    }
  },
  methods: {
     //(2)子から呼び出されるメソッド
    onRandomStr() {
      let targetStrHigh = this.targetStrLow.toUpperCase();
      let targetStrSymbol = this.targetStrSymbol1 + this.targetStrSymbol2;
      let targetStr = "";
      if (this.chkTypeNum) {targetStr += this.targetStrNum;}
      if (this.chkTypeLow) {targetStr += this.targetStrLow;}
      if (this.chkTypeHigh) {targetStr += targetStrHigh;}
      if (this.chkTypeSymbol) {targetStr += targetStrSymbol;}
      let targetLen = targetStr.length;
      // 乱数発生
      let uInt32 = new Uint32Array(1);
      this.rndStr = [];
      this.rndStrLen = 16;
      let cntL = 0;
      while ( cntL < this.rndStrLen ) {
        let cntA = Math.floor(cntL / 4);
        if (cntL % 4 == 0) {
          this.rndStr[cntA] = '';
        }
        let targetStrSta = Math.floor(Math.random() *targetLen);
        this.rndStr[cntA] = this.rndStr[cntA] + targetStr.substr(targetStrSta,1);
        cntL = cntL + 1;
      }
    }
  }
}
</script>
<!-- Add "scoped" attribute to limit CSS to this component only -->
<style scoped>
  .capitalize {
      text-transform: capitalize;
  }
  .rndStrAny {
    font-size: 24;
    padding-right: 16;
  }
</style>

まず<templeate>なのですが、ここはApp.vueの時と同じように、<div>等が使えませんので、StackLayoutなどで置き換えています。

<script>のほうでは、ここでは関数ではなくなっているのと、あと乱数発生部分はcrypto.getRandomValuesがNativeScriptでは使えなかったので、標準のMath.random()に置き換えています。

アイコンの登録

アプリのアイコンを登録します。

まずは画像から。

なんでもいいんですが、ここでは以前私が撮影した猫画像から猫を切り抜きます。

(切り抜きが雑だがご容赦を)

そしてこの猫画像の名前をneko.pngとし、random_ns/app/components/icons(たぶん、もっといいディレクトリがあると思う)に転送します。

そして、random_nsのディレクトリで、アイコンの登録は、

tns resources generate

で、できるので、

tns resources generate icons ./app/components/icons/neko.png

とすれば、アイコン登録完了です。

デプロイ

実機にインストールする場合は、実機に開発者向けオプションを表示させ、USBデバッグをONにし、実機と開発用マシンをUSBで接続します。

tns device

で接続していることを確認します。

大体こんなような画面が出ればOKです。

これが出たらいよいよ実機へのデプロイです。

tns deploy android

そして、これが成功すれば、スマホ側にアプリがインストールされます。

2-2.この時点で残っている課題

アプリの画面上の名称「NativeScript-Vue Application」を変える方法がわかりませんでした。

まだこれだけならいいのですが、このアプリ自体のファイル「app-debug.apk」を変えることができませんでした。

つまり、その後第2弾のアプリをインストールしても、第1弾のアプリが上書きしてしまうという問題が発生しています。

この問題で、悩んでいました。

もちろん、解決方法はあると思うのですが、ちょっとこちらも疲れてしまいまして、NativeScript-Vueの学習については、とりあえずここで断念した次第です。

また、iOS端末で確認する予定もあったのですが、これも断念いたしました。

3.あとがきに代えて

実質上ブログを1年ぐらい間を開けてしまったのですが、最後の近況報告以降、実はフロントエンドの勉強に没頭していました。

で、当初フロントエンドは、Python+Kivyでフロントエンドを作る方針でしたが、どうやらKivyランチャー経由で起動させなければならないものらしくて、Kivyについては断念しています。

そういうわけで素直にJavaScriptの世界に入ったわけですが、こちらとしてはKivyをやっていたころから「一つのソースで、Android、iOS両方とも動く」という虫のいいことを志向していまして、いろいろ調べていたらNativeScriptがいいんじゃないかという結論に、その時は思っていました。

で、それだけでは動かないようなので、Vue.jsも一緒に勉強しておりました。

その後、いよいよNativeScript-VueによるAndroidアプリがほぼできつつあったある日の昼休み、「React」という文字列を見てしまいました。

そのReact.jsとReactNativeについて調べてみたら、どうも採用実績がNativeScriptよりもあり、WEBサイト上の日本語の情報(これ極めて重要)が豊富で、Vue.jsについてはそうでもないのですが、NativeScriptの情報を調べている際に悩ませた孤独感(これはKivyの時以上)を解消できるのではないかと思いました。

そこで、思い切ってNativeScriptとVue.jsの学習を中断し、React.jsとReactScriptの方に転向する決断をいたしました。


もちろん、状況が変わればNativeScript-Vueに戻るかもしれませんが、こっちの水は甘いぞ、ではありませんが、学習コストのことを考慮しても、あえてReactNativeに乗り換えようと思っています。

この投稿は、再びVue.jsとNativeScriptが必要になっても、後戻りが最小限になるよう、今までやった学習用アプリ作成について、ここに記したいと思いました。

タイトルとURLをコピーしました