Vue.js最新版「Vue 3」を試してみた

f:id:tamb_kgn:20201007180846p:plain

はじめに

初登場のタンバリン大阪開発チームの加减です。よろしくお願いします。

今回は9月19日に正式リリースされたばかりのVue.jsの最新版「Vue 3」を触ってみたいと思います。

実行環境 バージョン
OS MacOS 10.15.6
Node.js 11.15.0
Vue CLI 4.5.6

インストール

とりあえず公式ドキュメントに沿ってやってみましょう。

v3.vuejs.org

いくつか方法がありますが、今回はVue CLIを使っていきます。

npm install -g @vue/cli
vue --version
@vue/cli 4.5.6

Vue CLIの準備ができたのでプロジェクトを作っていきます

vue create vue3-sample
Vue CLI v4.5.6
? Please pick a preset: Default (Vue 3 Preview) ([Vue 3] babel, eslint)

完了するまでちょっと時間がかかるのでコーヒーでも飲みながら待ちましょう

f:id:tamb_kgn:20201001004129p:plain

おっ!どうやら成功したようですね。指示通りに開発用のローカルサーバーを立ち上げてみましょう

cd vue3-sample
npm run serve

f:id:tamb_kgn:20201001004414p:plain

Oops... cannot find module 'vue-loader-v16/package.json' ok...

npm i --save-dev vue-loader-v16
+ vue-loader-v16@16.0.0-beta.5.4
added 9 packages from 43 contributors, updated 8 packages and audited 1326 packages in 7.039s
found 0 vulnerabilities

これでどやあ!

f:id:tamb_kgn:20201001005107p:plain

はい、お待たせしました。インストール完了です。

Composition API

Vue3に追加された機能の中でも、今回は「Composition API」にフォーカスしたいと思います。ちなみにVue.js 2系(以下Vue2)の「Options API」も引き続きサポートされています。 Composition APIの概要について、公式ドキュメントによると↓

Introduction | Vue.js

どうやらOptions APIだとdata, methods, computed, watchといったオプション単位でロジックを書く必要があり、コンポーネントが大きくなるにつれ複数機能の実装がコードの各所に散らばります。

結果、非常に読みづらいのに加え、ロジックの再利用性も低くなってしまいます。Composition APIではこの問題を解決してくれる、ということのようです。

ま、とりあえず触ってみましょう。

今回はサンプルとして、Vue2で書いた簡単なToDoリストアプリをVue3にリプレイスしてみたいと思います。全体のコードは以下から。

github.com

こんな感じの超シンプルなアプリです。

f:id:tamb_kgn:20201006190123g:plain

setup

大前提として、Composition APIではコンポーネントの処理をまとめてsetupメソッド内に定義します。 Vue2ではリアクティブデータはdataメソッド内で扱っていましたが、Vue3では新たに追加されたメソッドrefreactiveを利用して定義します。 それぞれ、reactiveはobject型、refはそれ以外のプリミティブ値に用います。使う際は忘れずimportしてください。

さらに、定義したリアクティブデータをtemplate内で使用するには最後にreturnしてあげる必要があります。

<script>
import { reactive, ref } from 'vue';
export default {
    name: 'Sample',
    props: {
        inputText: String
    },
    //data() {
    //  return {
    //      input: 'Add task here',
    //      items: [],
    //      id: 0
    //  }
    //},
    setup() {
        let input = ref('');
        let items = reactive([]);
        let id = ref(0);
        return {
            input,
            items,
        }
    }
}
</script>

methods

続いてmethodsの定義ですが、非常にシンプルです。ES6そのままな感じ。 注意点として、リアクティブデータの値にアクセスするには変数名の後ろに.valueを付ける必要があります。 ちなみにtemplate側では.valueなしで大丈夫です。

また、Vue2ではthisでVueインスタンスを参照した上でプロパティにアクセスしていましたが、setup内ではthisは不要です。そもそもアクセスできません。

理由は、setupが実行されるのがVueインスタンスが生成される前のbeforeCreateおよびcreatedのタイミングだからです。

<script>
import { reactive, ref } from 'vue';
export default {
    name: 'Sample',
    props: {
        inputText: String
    },
    // methods: {
    //     addListItem() {
    //         this.id++;
    //         const item = {
    //             id: this.id,
    //             text: this.input,
    //             isDone: false,
    //         }
    //         this.items.push(item);
    //         localStorage.setItem('taskList', JSON.stringify(this.items));
    //         localStorage.setItem('lastID', JSON.stringify(item.id));
    //         this.input = '';
    //     },
    //},
    setup() {
        // 省略
        const addListItem = () => {
            if (input.value !== '') {
                id.value++;
                const item = {
                    id: id.value,
                    text: input.value,
                    isDone: false
                }
                items.push(item)
                localStorage.setItem('taskList', JSON.stringify(items));
                localStorage.setItem('lastID', JSON.stringify(item.id));
                input.value = '';
            }
        }
        return {
            input,
            items,
            addListItem,
        }
    }
}
</script>

computed

次はcomputedです。算出プロパティと呼んだりもしますね。

こちらもimportした上でcomputedメソッドを定義していきます。引数にコールバック関数で処理を追加します。

<script>
import { reactive, ref, computed } from 'vue';
export default {
    name: 'Sample',
    props: {
        inputText: String
    },
    // computed: {
    //     doingNum: function() {
    //         return this.items.filter(item => item.isDone === false).length;
    //     }
    // },
    setup() {
        // 省略
        const doingNum = computed(() => {
            return items.filter(item => item.isDone === false).length;
        });
        return {
            input,
            items,
            addListItem,
            doingNum
        }
    }
}
</script>

mounted

Vue3でもライフサイクルフックは健在です。 ただ呼び出し方が少し変更されています。

hook名の頭にonを付けたメソッドとして定義し、引数にコールバック関数を指定します。

<script>
import { reactive, ref, computed, onMounted } from 'vue';
export default {
    name: 'Sample',
    props: {
        inputText: String
    },
    // mounted: function() {
    //         const tasks = JSON.parse(localStorage.getItem('taskList'));
    //         if (tasks != null && tasks.length > 0) {
    //             this.id = JSON.parse(localStorage.getItem('lastID'));
    //             tasks.forEach(task => {
    //                 this.items.push(task);
    //             });
    //         }
    //     }
    setup() {
        // 省略
        const setListItem = () => {
            const tasks = JSON.parse(localStorage.getItem('taskList'));
            if (tasks != null && tasks.length > 0) {
                id.value = JSON.parse(localStorage.getItem('lastID'));
                tasks.forEach(task => {
                    items.push(task);
                });
            }
        }
        onMounted(setListItem);
        return {
            input,
            items,
            addListItem,
            doingNum
        }
    }
}
</script>

onMounted以外のhookについては公式ドキュメントを参照してみてください。

Lifecycle Hooks | Vue.js

Vue.js devtools

chrome.google.com

Chrome等でVue.jsアプリを開発する際に便利な拡張機能「Vue devtool」ですが、残念なことに2020年10月時点ではVue3に未対応のようです。

ですが、ありました解決策が↓

chrome.google.com

β版ですが問題なく使えましたので、開発のお供にどうぞ。

まとめ

Vue.jsを久々に触りましたが、Vue3のComposition APIでだいぶ書きやすくなりましたね。

今回ご紹介したComposition API以外にも、便利な機能がいくつか追加されているようですし、TypeScriptとの親和性アップや、パフォーマンス向上、バンドルサイズの圧縮等々、全体的にパワーアップしているようです。

ただ、現時点ではIE11をサポートしていないようですので、実際のプロジェクトで使う場合はサポートブラウザの要件確認が必須です。将来的にはサポートを予定しているようです。

ふりかえり手法の紹介:感謝(Appreciations)

こんにちは、タンバリンの髙橋です。

ふりかえりの「感謝」(Appreciations)がとても良かったので紹介します。

「感謝」とは言葉の通り、チームメンバーがお互いに感謝し合う活動です。

  • 言わなくても感謝の気持ちは伝わってるはず!
  • ありがとうはいつも言ってる!

その感謝の気持ち、改めてお伝えしてみませんか?

実際に「感謝」のワークをやってみた時のやり方を紹介します。

実施の事前準備

  • 道具を用意する:付箋、サインペン、模造紙やホワイトボード
    • オンライン実施の場合は、使用ツールがあれば事前動作確認の依頼必須です。
    • trelloやJamboard、miro、DropboxPaper、スプレッドシートなどお好みで。
    • グラフィカルにやりたいか、議事録で二次利用したいか等を判断材料に。
  • 参加者の招待、時間・場所の確保(5分〜30分くらい)
    • 6人のチームメンバーで実施した時は、15分くらいでした
  • 工程の終わりや、プロジェクトの終わりなど、区切りのタイミングのふりかえりと合わせてやってみる

実施

0. 案内

助けてくれたり、貢献してくれたり、問題を解決してくれたりしたメンバーに感謝します。 感謝はあくまで任意、強制ではありません。

1. デモンストレーション

「今回のふりかえり期間(リリース、プロジェクト)で、みんなの貢献に関して、お互いに感謝しあいましょう」と言い、デモンストレーションを行います。

相手の名前を呼んでから、 「◯◯さん、あなたの XXXX に私は感謝しています。ありがとう」と言います。

XXXX には、その人についてや、その人の行ったことを入れます。 自分に与えた影響について説明しても良いです。

2. お互いに感謝を述べる

じっくり考えて発言したい人もいますので、沈黙があっても、待ちます。

ファシリテーターは皆の発言を付箋にメモするのに徹し、 メンバーが発言に集中できるようにしたり、 付箋にメモをして発言していないメンバーを促したりします。

シールや絵文字を用意して「私もそう思う!」付箋に、どんどんマークをしていくのも 感謝の気持ちの表明になります。

3. クロージング

チームメンバー全員に対し「ありがとう」と言い合い、みんなで拍手して終わります。

効果

実際やってみて、メンバーの皆さんから「照れる」「嬉しい」という言葉がありました。 普段伝えているつもりでも、機会を設けて感謝を伝えることで より関係性が良くなり、意見相違があってもお互いに尊重しあえる効果があると言われています。

f:id:takamarix:20200930124420p:plain

ぜひ、やってみてくださいね!

参考リンク

アジャイルレトロスペクティブズ 強いチームを育てる「ふりかえり」の手引き(書籍)

notionでタスク管理した

こんにちは、タンバリン東京開発デザインチームの斎藤です。

今回はタスク管理にnotionを導入した話をしたいと思います。

経緯

タンバリンは今年の春頃から在宅勤務がメインで、僕は出社していた頃と比べてある程度家のことや趣味に時間を費やせるようになりました。

それらのタスクを今までは頭の中で管理していたのですが、どうせなら管理してしまおうと思いたったのが経緯です。

以上を踏まえた上で僕は以下の点を求めました。

  • 日常/趣味/仕事をまとめて管理出来る
  • 上記を大カテゴリとして階層構造でタスクを管理出来る
  • タスクを細かく区切って管理出来る
  • 時間割的な感じでその日にやることがわかる

可能であったら以下の機能もあるといいなと思っていました。

  • 読んだ本の情報や感想もそこにあると嬉しい
  • 仕事の情報や勉強したことがそこにあると嬉しい

逆に以下の点は求めませんでした。

  • オフライン対応(ずっと家にいるから)
  • 表計算(お金周りは別のを使ってる)

これらの要素を満たすのがnotionかなと思ったのでnotionを使うことにしました。

notionの使い方の予定を組む

まず、なんとなく機能を調べて出来そうな形に落とし込むことにしました。

求める機能 使い方の予想
日常/趣味/仕事をまとめて管理出来る テーブルでタグを使いカテゴライズする
タスクを細かく区切って管理出来る テーブル内にページがあるので、その中に入れ子でテーブル作る
時間割的な感じでその日にやることがわかる 上のを参考にカラムで組む

ぱっと調べた限りではいけそうだと感じたので実装していきました。

日常/趣味/仕事をまとめて管理する

まず、タスクの一覧用のページにテーブルをいれました。

思い立ったタスクは分類問わずとりあえずここに格納しています。

タスクが発生した時はここに登録することから始めます。

f:id:otiasotika:20200916135304p:plain

タスクを細かく区切って管理出来る

テーブルになにか登録すると自動で詳細等を書き込めるページが生成されます。

下の方にテーブルや文章を自由に入れられる自由記述欄があるので、僕はテーブルに細かなタスクを書き出し管理しています。

f:id:otiasotika:20200916141448p:plain

しかし、これだけだと

プロジェクトのテーブルをクリック>タスクが書いてあるテーブルを見る>戻る

と、少し面倒な上にタスクを俯瞰して見れないのでこれらをまとめたページを作ることにしました。

タスクがまとまってるページを作る

ここでは上で作った細かなタスクを書き出したテーブルを転載します。

終わったタスクは消すので、プロジェクトのページ以上に今着手していることがわかりやすくなっています。

また、toggle listを使うことである程度分類出来るようにしました。

仕事系はなるべくネストが深くならないようにし、趣味や家事は気の赴くままネストしていきます。

f:id:otiasotika:20200916143217p:plain

時間割的な感じでその日にやることがわかる

まずこんな感じで目次を作ることにしました。

長く運用していくとこの類は氾濫しそうな予感がしたからです。

単位は一週間ごとで、大体月曜日にテンプレートを複製して、タスクを埋めていく感じです。 f:id:otiasotika:20200916144241p:plain

中身はこうなっています。 1日を朝と就業時間と夜に分けて管理していて、その時間が来たらそのtoggle listを開くみたいな運用です。

文字とかタイトルとかの要素をまとめて選択して、一行目の横にもってくると勝手にカラムにしてくれるので、それを使っています。

また、下のwishlistにはその週やるであろうタスクのテーブルのリンクが添付してあって、そこからタスクを卸してきます。

f:id:otiasotika:20200916150512p:plain

開くとこんな感じになっています。

○とか●はトマトのメタファーです。

普段ポモドーロ法を使っているので○が大体25分。●は一つで2セットを意味します。

休憩時間にやることは、このポモドーロ法を使った時に生まれる休憩時間に上から順にやっていきます。

f:id:otiasotika:20200916150325p:plain

使い方

新たなタスクが生まれたらプロジェクトに登録、データベースに転載、時間割のwishlistに入れていけそうな時間に登録。

通常時は時間割を見て自分のタスクを確認。

適宜、プロジェクトを眺めて趣味のタスクを入れたり、データベースを眺めて終わったものを消したりして管理。

という感じで使っています。 一度作ってしまったらなんでもコピペで済んでしまうので意外と楽でいいです。

まとめ

今回は僕のタスクの管理の方法を紹介しました。

僕の要求はデータベースとリンクで解決出来ましたが、notionには色々な使い方があるようです。

皆さんもぜひ使ってみてください。

https://www.notion.so/

Salesforceの記事のインポート機能を利用したKnowledgeのインポートにおける注意点

はじめまして、タンバリンのエンジニアの菊地と申します。

はじめに

記事のインポート機能とは以下の画面からCSVファイルとプロパティファイルを作成し圧縮したzipファイルをアップロードすることでSalesforceのKnowledgeをインポートできる機能である。 f:id:hk_tb:20200831160338p:plain f:id:hk_tb:20200831160454p:plain 上記の記事のインポート機能を利用した場合に項目エラー以外で正しく取り込めないケースが存在する。

インポート時にエラーになった場合に調査すること

  1. CSVファイルの圧縮方法の確認
  2. CSVファイルの文字コードと日付フォーマットの確認
  3. データカテゴリの入力方法の確認

調査内容詳細

  1. CSVファイルの圧縮方法の確認
    CSVファイルは、フォルダを圧縮するのではなく、CSVファイルとプロパティファイルを選択して圧縮する

    • 誤った例 f:id:hk_tb:20200831160905p:plain
    • 正しい例 f:id:hk_tb:20200831161207p:plain
  2. CSVファイルの文字コードと日付フォーマットの確認

    • CSVファイルに使用する文字コードはプロパティファイルで指定している文字コードと一致している必要がある f:id:hk_tb:20200831161520p:plain
    • 日付フォーマットはプロパティファイルで指定していない場合は、「yyyy-MM-dd」形式で入力する
    • 特にExcelでファイルを修正している場合は、文字コード、日付形式共に自動で変換される可能性があるため注意が必要
  3. データカテゴリの入力方法
    カテゴリグループの一意の名前をCSVファイルのヘッダーに指定し、その配下にあるカテゴリを+で結合する

    • 以下のようにデータカテゴリグループ及びデータカテゴリが設定されている場合 f:id:hk_tb:20200831162340p:plain f:id:hk_tb:20200831162353p:plain f:id:hk_tb:20200831162406p:plain
      上記の場合、CSVへの記載方法は以下となる

      …,Demo,…
      …,X1,…
      …,X1+Y1,…
      

単体テスト(フロント寄り)の分類をしてみる

ご挨拶

タンバリンの佐藤と申します。

ここ最近はプロジェクトでテストケース作成と打鍵を担当しているので、 整理がてら単体テストの分類をまとめてみようと思います。

なお、ここでいう単体テストは「フロント(画面)寄りの単体テスト」になります。 バックエンド寄りになるとメソッド単位のテストだとかになるので、ご承知おきください。

単体テストの分類

f:id:shota_tmb:20200831140810p:plain

デザインチェック

  • デザイン仕様書をベースとして、パーツの配置がずれていないか、パーツが過不足なくあるかを確認する
  • 観点表(こういったところを確認しますよ表)を作成して、横断的にチェックすることが多い印象。なので、既存のものがあれば流用がしやすい

f:id:shota_tmb:20200831140844p:plain

バリデーションチェック

  • 「チェックロジックがあり、エラー判定を返す」機能が対象
  • チェック対象が1項目だけの「単項目チェック」と、チェック項目が複数の「相関チェック」がある。相関チェックはテストパターンが多くなるため、注意が必要
  • また、複数のチェックが同一項目にかかる場合は、チェック順序も確認する必要がある
  • テストパターンが多くなる&改修頻度が多いため、自動テスト化することが多い

ロジックチェック

  • 「操作(多くはクリック)することで画面上になんらかの動きが発生する」機能が対象
  • 具体的には「画面遷移」「モーダルウィンドウ表示」「アンカーリンク」「パスワードの表示・非表示」など

以上です。

Laravelで二重登録を阻止する3つの方法

ご挨拶

タンバリンの若林と申します。

データをPOSTする際、何らかの不具合でデータベースに同じデータが複数登録されてしまう…そんな過ちを最近犯してしまったので、この記事で懺悔します。

前提

  • 言語:PHP
  • FW:Laravelの6系
  • lib:jQuery
  • 次のプロセスを想定:入力画面 → 確認画面 → 完了画面

1. 送信ボタンの連続クリックを阻止

確認画面 → 完了画面 の順に遷移する際、送信ボタンを複数回クリックすると、その回数分データが送信されてしまいます。

これは確認画面に下記の処理を書くことで阻止できます。 formからデータを送信時、送信ボタンを非活性にしてるんですね。

【View】

<script>
    $(function () {
        $('form').submit(function () {
            $(this).find(':submit').prop('disabled', 'true');
        });
    });
</script>

2. ブラウザバックによる再度送信を阻止

完了画面 → 確認画面 の順にブラウザバック後、 確認画面 → 完了画面 の順に遷移する際、再度送信ボタンをクリックすると、また同じデータが送信されてしまいます。

これは完了画面へ遷移する際のメソッドに下記の処理を書くことで阻止できます。 送信後にCSRFトークンを再生成してるんですね。 これでブラウザバック後に再度送信しても元々のトークンと値が違うため、419のエラーが出てきて送信ができなくなります。

【Controller】

$hoge->createUser($request) // テキトーに登録系の処理
$request->session()->regenerateToken(); // これを書く。トークンを再生成
return view('complete');

3. 完了画面のリロードによる再度送信を阻止

確認画面 → 完了画面 の順に遷移後、完了画面のまま画面をリロードすると、また同じデータが送信されてしまいます。ブラウザのリロードは直前のリクエストを再度実行することができるので、直前に登録系の処理があった場合は二重登録の原因になります。

これは完了画面へ遷移する際に別のメソッドを挟む下記の処理を書くことで阻止できます。 登録系の処理完了画面を見せる処理 の2つに分けてるんですね。 これで完了画面をいくらリロードしようが、直前のリクエストは画面を見せるだけなので問題ありません。

【Controller】

public function register(Request $request)
{
    $hoge->createUser($request) // テキトーに登録系の処理
    return redirect()->route('complete'); // web.phpでルーティングを定義しておく
}

public function complete()
{
    return view('workshop.complete'); // 完了画面を見せるだけの処理
}

以上になります。 いかがでしたでしょうか。割と簡単にできる再現性の高い内容かと思います。 皆様の開発業務に貢献できていましたら幸いです。

参考文献

https://qiita.com/syobochim/items/120109315f671918f28d https://www.bnote.net/blog/laravel_double_submit.html

二段階認証設定をしたSalesforceと連携したTeamSpiritで起こっていたバグの解消

こんにちは。 タンバリンのクラウドアプリケーションディレクターの中川です。

弊社はTeamSpiritを使って勤怠打刻や経費精算を行っています。 社内のSalesforceと連携させているのですが、どうやら設定している二段階認証と相性が悪いようで、 スマートフォンから勤怠打刻ができなくなってしまっていました。

二段階認証のレベルは下げず、スマートフォンからも打刻できるようにする方法を記事にします。

前提

  • Salesforce側の二段階認証をプロファイルで ログインに必要なセッションセキュリティレベル が [高保証] に設定 としていました
  • 二段階認証のレベルは下げずにスマートフォンからも打刻できるようにしたい、という要望です
  • SalesforceのappexchangeでTeamspiritを使っています。 以下参照

appexchangejp.salesforce.com

方法

スマートフォンで打刻できなかったのはTeamSpirit側との相性の問題で、 プロファイルで ログインに必要なセッションセキュリティレベル が [高保証] に設定 として二段階認証を設定していると出るエラーのようでした。

プロファイルで ログインに必要なセッションセキュリティレベル が [高保証] に設定 となっているこれを外します。 更に、同じくプロファイルで ユーザインターフェースログインの 2 要素認証 をチェックした上で API ログインの 2 要素認証 にもチェックつけることで二段階認証を保ちつつアプリで打刻できるようにしています。


ちなみに…… デフォルトで作成されたシステム管理者プロファイルだとユーザインターフェースログインAPIログインのチェックが触れなかったので、シス管プロファイルのみコピーして設定しなおし、ユーザを当て直したりしました。

以上です。 みなさんも良き二段階認証ライフをお過ごしください。