20190228のvue.jsに関する記事は7件です。

LaravelのElopuentでリレーション関係の子データを取得して、そのデータをVue.jsで使用するときに困った

はじめての記事なのでうまく伝わるかわかりませんが、
joinを使わないやり方で調べてもなかなか出てこなかったので備忘録。

DBの構成

Employee(社員)テーブル

+---------+--------+---------------+
| id      | name   | department_id |
+---------+--------+---------------+
|       1 | 田中    | 1             |
|       2 | 鈴木    | 2             |
|       3 | 佐藤    | 3             |
|       4 | 吉田    | 1             |
+---------+--------+---------------+

Department(部署)テーブル

Department(部署)テーブル
+---------+-----------+
| id      | name      |
+---------+-----------+
|       1 | 営業部     |
|       2 | 経理部     |
|       3 | 技術部     |
+---------+-----------+

このとき、EmployeeテーブルとDepartmentテーブルのリレーションは多対1になります。

リレーションの簡単な説明(前置き)

このテーブルを使って、社員の一覧を表示する。
その際に、社員それぞれの部署名も一緒に表示させたい。

こういう風な一覧を作りたい

+---------+--------+------------+
| id      | name   | department |
+---------+--------+------------+
|       1 | 田中    | 営業部    |
|       2 | 鈴木    | 経理部      |
|       3 | 佐藤    | 技術部      |
|       4 | 吉田    | 営業部      |
+---------+--------+------------+

Laravelでは、リレーションを張ることで、簡単に表示させることができるようです。

リレーションに関するドキュメント
https://readouble.com/laravel/5.7/ja/eloquent-relationships.html

それぞれのModelクラスを作り、Employee.phpを以下のように記述

Employee.php
<?php

namespace App;

use Illuminate\Database\Eloquent\Model;

class Employee extends Model
{
    /**
     * この社員が持っているdepartment_idと一致するレコードを部署テーブルから取得
     */
    public function department()
    {
        return $this->belongsTo('App\Department');
    }
}

belongsTo関数を第1引数しか使わない場合は、関数名はテーブル名にしないといけないようです。
関数名_id と belongsTo() の第1引数で指定したモデルが持つidを結びつけるとのこと。

idとして使うカラムの名前が違う場合は、第2、第3引数で変更できるみたいです。

そして、あとは社員テーブルの一覧を普通に取得するだけ

Employee.php
public function get()
{
    $results = Employee::all();
    return $results;
}

これだけで勝手にDepartmentテーブルのデータと結合してくれます。
phpで使うなら

$result->department->name

で部署名が取れます。

真ん中のdepartmentはテーブルじゃなく関数名っぽいです。

1対1だったり、この例題と逆の参照でやる場合とかはドキュメントに乗ってたり
他の人が書いてくれているのでそちらを参考に

ここからが本題

本題

これをAPIを使ってVue.jsで受け取ったあと

Vue.js
{{ result.department.name }}

こんな感じで表示させようとしたときにエラーがでました。

Error in render: "TypeError: Cannot read property 'name' of undefined"

console.logとかで見てもあるやん!としかならなくて困りました。

多分、レスポンスを返すときにresponse()->json()でjsonにしていたつもりが
departmentの中までは正しく変換されていなかった感じだと思います。
(よくわかんないので詳しい方解説してくれれば嬉しいです...)

そういう雰囲気で検索かけたら、stackoverflowに質問していた方がいて、
さらに回答してくれている方がいました!

解決策

https://stackoverflow.com/questions/37289274/convert-eloquent-relationship-into-vue-js-code

社員一覧を取得するときに、こう書けよということみたいです

Employee.php
public function get()
{
    // $results = Employee::all();
    $results = Employee::with('department')->all(); 
    return $results;
}

withの中はbelongsToを使っている関数名っぽいです

Modelクラスのつもりなんでそのままリターンしてますが、
クライアントに返すときはちゃんとJsonにして返すといいと思います。

指摘があれば教えて下さい。
同じ境遇の人の助けになると嬉しいですm(_ _)m

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

LaravelのEloquentでリレーション関係の子データを取得して、そのデータをVue.jsで使用するときに困った

はじめての記事なのでうまく伝わるかわかりませんが、
joinを使わないやり方で調べてもなかなか出てこなかったので備忘録。

DBの構成

Employee(社員)テーブル

+---------+--------+---------------+
| id      | name   | department_id |
+---------+--------+---------------+
|       1 | 田中    | 1             |
|       2 | 鈴木    | 2             |
|       3 | 佐藤    | 3             |
|       4 | 吉田    | 1             |
+---------+--------+---------------+

Department(部署)テーブル

Department(部署)テーブル
+---------+-----------+
| id      | name      |
+---------+-----------+
|       1 | 営業部     |
|       2 | 経理部     |
|       3 | 技術部     |
+---------+-----------+

このとき、EmployeeテーブルとDepartmentテーブルのリレーションは多対1になります。

リレーションの簡単な説明(前置き)

このテーブルを使って、社員の一覧を表示する。
その際に、社員それぞれの部署名も一緒に表示させたい。

こういう風な一覧を作りたい

+---------+--------+------------+
| id      | name   | department |
+---------+--------+------------+
|       1 | 田中    | 営業部    |
|       2 | 鈴木    | 経理部      |
|       3 | 佐藤    | 技術部      |
|       4 | 吉田    | 営業部      |
+---------+--------+------------+

Laravelでは、リレーションを張ることで、簡単に表示させることができるようです。

リレーションに関するドキュメント
https://readouble.com/laravel/5.7/ja/eloquent-relationships.html

それぞれのModelクラスを作り、Employee.phpを以下のように記述

Employee.php
<?php

namespace App;

use Illuminate\Database\Eloquent\Model;

class Employee extends Model
{
    /**
     * この社員が持っているdepartment_idと一致するレコードを部署テーブルから取得
     */
    public function department()
    {
        return $this->belongsTo('App\Department');
    }
}

belongsTo関数を第1引数しか使わない場合は、関数名はテーブル名にしないといけないようです。
関数名_id と belongsTo() の第1引数で指定したモデルが持つidを結びつけるとのこと。

idとして使うカラムの名前が違う場合は、第2、第3引数で変更できるみたいです。

そして、あとは社員テーブルの一覧を普通に取得するだけ

Employee.php
public function get()
{
    $results = Employee::all();
    return $results;
}

これだけで勝手にDepartmentテーブルのデータと結合してくれます。
phpで使うなら

$result->department->name

で部署名が取れます。

真ん中のdepartmentはテーブルじゃなく関数名っぽいです。

1対1だったり、この例題と逆の参照でやる場合とかはドキュメントに乗ってたり
他の人が書いてくれているのでそちらを参考に

ここからが本題

本題

これをAPIを使ってVue.jsで受け取ったあと

Vue.js
{{ result.department.name }}

こんな感じで表示させようとしたときにエラーがでました。

Error in render: "TypeError: Cannot read property 'name' of undefined"

console.logとかで見てもあるやん!としかならなくて困りました。

多分、レスポンスを返すときにresponse()->json()でjsonにしていたつもりが
departmentの中までは正しく変換されていなかった感じだと思います。
(よくわかんないので詳しい方解説してくれれば嬉しいです...)

そういう雰囲気で検索かけたら、stackoverflowに質問していた方がいて、
さらに回答してくれている方がいました!

解決策

https://stackoverflow.com/questions/37289274/convert-eloquent-relationship-into-vue-js-code

社員一覧を取得するときに、こう書けよということみたいです

Employee.php
public function get()
{
    // $results = Employee::all();
    $results = Employee::with('department')->all(); 
    return $results;
}

withの中はbelongsToを使っている関数名っぽいです

Modelクラスのつもりなんでそのままリターンしてますが、
クライアントに返すときはちゃんとJsonにして返すといいと思います。

指摘があれば教えて下さい。
同じ境遇の人の助けになると嬉しいですm(_ _)m

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Firebase+Nuxt.jsでともだちのSNSリンクを一カ所で管理できるサービスを作ってしまった......【個人開発】

じぇいです!

今回僕は、Firebaseの勉強にサービスを一つリリースしました。

それは!

ともだちのSNSリンクを一カ所で管理できる「ともだちびお」というサービスです。

Firebase+Nuxt.jsで作りました!Firebaseは初めての挑戦です。

リリースツイートはこちらです!え、リツイートしてくれますよね...?

サービス内容

サービス内容はともだちのSNSリンクを一カ所で管理できる連絡帳サービスです。

intro.png

Twitter, Facebook, Instagram, Githubなどのリンクを登録できます!

使ったもの

Nuxt.js

Vue.jsをさらに便利に活用できるフレームワーク。SPAもSSRもどっちも来い!っていうなんでもできちゃう感。SPAで今回は作りました!

Firebase Authentication

Twitter認証に使いました(後述)。

Firebase Realtime Database

基本的なCRUDを実装しました(後述)。ルール定義で自分の保存データしかアクセスできないようにすることで連絡帳サービスを実装しています。Firestoreも興味深かったのですが、学習コストがたかそうだったので、まずはRealtime Databaseからにしました。

Firebase Hosting

デプロイもものすごく簡単!カスタムドメインにもSSL接続を無料でできてハッピー!

FontAwesome

最初、CDNで使ってたんですが速度が遅かったので最終的にはnpmパッケージで使うことにしました。使うアイコンだけlibrary.add()すれば、読み込みも遅くなりません。

nuxt-buefy

BulmaのVueに最適化したフレームワーク。細かい部品は頼りきりでした。

vue-lazyload

画像の遅延読み込みに使いました。少しは速度は改善したはず......。

vue-burger-menu

おしゃれなバーガーメニューを実装できます。

vue-tags-input

タグの入力に使いました。

@nuxtjs/pwa

PWA対応を簡単にできます。ただTwitter OAuthを使っているので一度Safariに遷移してしまってあまりうまく使えてません。Androidはできてるのかな?

@nuxtjs/google-analytics

Google Analyticsの設定が楽々!

Firebaseの認証・CRUD

認証

Twitterログイン

methods: 
  twitterLogin () {
    var provider = new firebase.auth.TwitterAuthProvider()
      firebase.auth().signInWithPopup(provider)
    }
  }

ログアウト

logout: function() {
   firebase.auth().signOut();
},

ログインしてるかのチェック

firebase.auth().onAuthStateChanged(user => {
      if (user) {
        this.isLogin = true
        this.user = user
      } else {
        this.isLogin = false
        this.user = null
      };
    })

CRUD

Create(投稿)

var newLinkKey = firebase.database().ref().child('posts').push().key;
firebase
  .database()
  .ref('posts/' + this.user.uid + '/' + newLinkKey)
  .set({
     content: this.content,
     // 省略
  })

Read(読み取り)

firebase.database().ref('posts/' + this.user.uid).on('value', function(snapshot) {
   this.posts = snapshot.val()
})

Update(更新)

var newPost = {
   content: this.content
   // 省略
}
var updates = {}
updates['/posts/' + this.user.uid + '/' + key] = newPost
firebase.database().ref().update(updates)

Delete(削除)

firebase.database().ref('links/' + this.user.uid + '/' + key).remove();

最後に

Firebaseはこれだけでマイクロサービスを作り上げることができます。

Nuxt.jsと組み合わせることでサービスの幅も広がって良き良き。

Twitterフォローしてねー!

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Vue.jsでグラデーションのアニメーション表示をしよう!Granim.js

Granim.js

Granim.jsはグラデーションをアニメーション表示させることができるJavaScriptライブラリです。
これを使うと、リッチなWebサイトを作ることができます!

どのようにアニメーション表示されるかは公式サイトのトップページExamplesをご覧ください。
Screenshot_2019-02-28 20.44.37_XuGUDd.png

このGranim.jsをVue.jsのアプリケーションで使ってみます。

手順

1.Gramin.jsのインストール

npm install granim

2.コンポーネントの作成

granim.vue
<template>
  <div>
    <canvas id="granim-canvas"></canvas>
  </div>
</template>

<script>
import Granim from 'granim'

export default {
  name: 'granim',
  data () {
    return {
      GObj: Object
    }
  },
  mounted () {
    this.GObj = new Granim({
      element: '#granim-canvas',
      name: 'granim',
      opacity: [1, 1],
      states: {
        'default-state': {
          gradients: [
             ['#29323c', '#485563'],
             ['#FF6B6B', '#556270'],
             ['#80d3fe', '#7ea0c4'],
             ['#f0ab51', '#eceba3']
          ]
        }
      }
    })
  }
}
</script>

<style scoped>
#granim-canvas {
  width: 100vw;
  height: 100vh;
}
</style>

キモはコンポーネントのmountedライフサイクルフックでGranimをnewするところです。
mounted()に記述するコードは公式サイトの通りに記述すればOKです。
newするときのオプションでスピードや色の指定など様々な表現ができます。

※上のサンプルコードではESLintのエラー回避のためにdataにnewしたGranimのインスタンスを代入しています。

3.完成!

ezgif.com-video-to-gif (2).gif

バリエーション

Granim.jsを使うとただのグラデーションだけではなく、画像と組み合わせたり、文字に対してグラデーションのアニメーションをつけることができます。公式サイトにやり方が載っています。
Screenshot_2019-02-28 20.55.56_RaImf3.png
Screenshot_2019-02-28 20.56.03_dPpNVJ.png

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Vueで画像を遅延読み込みするライブラリ v-lazy-image

先日書いた表示速度改善の続きで、画像の遅延読み込みをするために、ライブラリを探してみたところ、v-lazy-imageが良い感じだったので紹介。

導入

$ npm install v-lazy-image --save

使い方

App.vueとかでプラグインとして読み込む場合

import Vue from "vue";
import { VLazyImagePlugin } from "v-lazy-image";

Vue.use(VLazyImagePlugin);

必要な箇所で個別に使う場合

import VLazyImage from "v-lazy-image";

export default {
  components: {
    VLazyImage
  }
};

あとは公式にある通り、v-lazy-imageタグを使ってやれば良い。

<template>
    <v-lazy-image src="http://lorempixel.com/400/200/" />
</template>

もちろん、propsやdataの値を渡すこともできる。

<template>
    <v-lazy-image :src="imageURL" />
</template>

<script>
import VLazyImage from "v-lazy-image";
export default {
    components: {
        VLazyImage
    },
    props: {
        imageURL: {
            type: String,
            default: null
        }
    }
}
</script>

あとは、出力されるimgタグにv-lazy-imagev-lazy-image-loadedのclassが付与されるので、こんな感じでアニメーションをつけることができる。

<style scoped>
.v-lazy-image {
  filter: blur(10px);
  transition: filter 0.7s;
}
.v-lazy-image-loaded {
  filter: blur(0);
}
</style>

最初はvue-lazyloadを使おうかと思ってたけど、画像の遅延読み込みのためだけならv-lazy-imageの方が軽くて良い感じ。

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

laravel-mix + Vue.js (ES) + WebWorker (TS)

追記

2019-02-28 12:33

よさそう。
GoogleChromeLabs/comlink: Comlink makes WebWorkers enjoyable.
参考: WebWorkerをenjoyableにするComlinkとは何者か - Qiita


概要

laravel-mix + Vue.js (ES2018+) + ロジック部分は TypeScript というプロジェクトに、ちょっと Web Worker を導入したくなったのでやったというものです。
この記事は「こうやったら出来た」というメモでありベストプラクティスではないので、 Web Worker 初歩の参考の1つ程度にしていただければと思います。

Worker とは

Worker です。非同期処理による似非マルチスレッドではなく、真のマルチスレッドをもたらします。恐らく。
詳細や正確なところはは色んな方が解説されているかと思うのでぐぐってください!

Web Workers API - Web API | MDN

本記事では専用 Worker についてのみ言及します。

Worker の使い方

メインの JS とは別に Worker 用のコードを用意します。
インラインで文字列として記述したり別の <script> タグ中に記述したりでも大丈夫なそうですが、ここでは別ファイルとして用意します。

大まかに以下のような感じで使います。

main.js
import Worker from './path/to/file.worker.js'

const worker = new Worker()
// `new Worker('./path/to/file.worker.js')` とかでも出来るそうな…。
// 個人的にはコード中で参照されるファイルパスは
// `import ~ from ~` のように一箇所にまとめたい気持ちがあります。

worker.addEventListener('message', e => {
  const { data } = e

  console.log( data ) // => 500

  // worker を終了させる場合は実行。
  worker.terminate()
  // もしくは worker 側から `self.close()` を実行するとその場で worker が終了します。
})

// worker に `100` を送信。
worker.postMessage(100)
path/to/file.worker.js
addEventListener('message', e => {
  // `data` にはメインから渡された `100` が入ってくる。
  const { data } = e
  // `5` を掛けてメインスレッドに送信。
  postMessage( data * 5 )
})

今回やったこと

laravel-mix をコンパイラとして使っているプロダクトで、 Vue.js 製アプリケーション内から TypeScript 製 WebWorker を利用するということを行いました。

laravel-mix の設定

.webpackConfig() で Webpack の設定を行うだけです。

ローダーは worker-loader を以下のように設定しました。
Worker のファイル自体は TypeScript で書いていくので、 ts-loader も一緒に指定しています。

{
  module: {
    rules: [
      {
        test: /\.worker\.ts$/,
        use: [
          {
            loader: 'worker-loader',
            options: {
              name: '[name].js'
            }
          },
          'ts-loader'
        ]
      },
      {
        test: /\.ts$/,
        // Worker は Worker としてファイルを分離したいので除外設定 (そうしないとバンドルされちゃうかなぁと思って…)
        exclude: /\.worker\.ts$/,
        loader: 'ts-loader'
      }
    ]
  }
}

上記の設定に落ち着くまで以下のような挙動をされて躓きました。

  • worker-loadername を指定しないと出力ファイル名がハッシュ値になり、ビルドの度に違う Worker ファイルが生まれる。
  • worker-loaderpublicPath を指定するとそのディレクトリに出力されるが、 Vue.js アプリケーション内からの Worker 参照パスがおかしい。(存在しないパスを参照して 404 になる)

これらの理由から name を固定し、 publicPath を設定していません。
ただこれだと Worker ファイルの出力先が laravel-mix デフォルトの ./public 直下になってしまうので、ちょっと嫌だなぁとなっているところです。ちょっとどうにかしたい…。

tsconfig.json の設定

compilerOptions.lib"webworker" を追加します。
VSCode でも IntelliSense が効いてくれるようになります。

Worker を書く

TSで書きます。

path/to/file.worker.ts
interface IMessageEvent extends MessageEvent {
  data: number
}

addEventListener('message', (e: IMessageEvent) => {
  postMessage( e.data * 5 )
})

最低限これだけです。
最終的に Webpack で1つのファイルにまとめるので、 Worker 内でも importrequire() が使えます。

Vue.js コンポーネント内から Worker を利用する

利用します。

path/to/vue/component.vue
import Worker from 'path/to/file.worker.ts'

export default {
  // ...
  data(){
    return {
      num: 1
    }
  },
  methods: {
    handleWorker(){
      const worker = new Worker()

      worker.onmessage = e => {
        this.$data.num = e.data
        worker.terminate()
      }

      worker.postMessage( this.$data.num )
    }
  }
}

handleWorker() を実行する度に Worker を経由して $data.num の数値が5倍されていきます。


MDNの こことか にも結構複雑なコードがあったりするので、 Worker をラップした何かを使ってコントロールするのがいいんでしょうか。
素のまま Worker を使っていくのはなんだか苦しく感じました。

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

8年前に作った HTML5 アプリを最近の技術で作り直した話(5年ぶり2回目)

icotile は、HTML5 を活用して iTunes のような操作感で Twitter の友達やリストを管理できる Web アプリケーションです。2011年に最初のバージョンをリリースし、2014年には、AngularJS と Bootstrap で全面的に書き換えた icotile2 をリリースしました。この8年間で延べ 55,000ユーザーに利用されている Web アプリです。

icotile3 のメイン画面

随分ほったらかしにしていたのですが、いまだに意外と使われていて、たまに要望とかもいただいていました。最近プロダクトを作っていなかったので、最新の技術を勉強するために、icotile3 の開発に着手しました。

随分古い技術スタックだったのと、勉強も兼ねているので、できるだけまだ使ったことのない技術を採用してみました。また、サーバサイドの Node.js への置き換えも行って、フロントエンドとバックエンドの共通化も図ってみました。機能はほぼ同等+アルファですが、データベース以外はほぼ置き換えです。

icotile (2011) icotile2 (2014) icotile3 (2019)
Framework jQuery AngularJS 1.x Vue.js / Vuex
UI jQuery UI Bootstrap Vuetify
Style スキューモーフィック風 フラットデザイン マテリアルデザイン
Lang JavaScript JavaScript TypeScript
Backend PHP / CakePHP PHP / CakePHP NodeJS / Express
DB MySQL MySQL MySQL
Server Apache Apache Apache + nginx
Protocol HTTP HTTP HTTPS
その他 - - docker / docker-compose での環境構築、PM2 でのプロセス管理等。
関連記事 Twitter の友達やリストを iTunes 風に管理できるアプリ「icotile」(アイコタイル)をリリース 3年ぶりのバージョンアップ!AngularJS などの最新技術で再構築した Twitter リスト管理アプリ「icotile2」リリース!

Vue.js/Vuex/Vuetify

Angular は複雑で理解するのに時間がかかったのですが、Vue.js/Vuex は構造がシンプルで、ドキュメントもわかりやすく、どんどん理解できてどんどんコードが書ける感じが気持ちよかったです。

React/Redux は仕事で散々やったので、今回 Vue をトライしてみました。React は、Redux とのつなぎこみが面倒だったので、その辺りは Vuex の方がうまく統合されていてわかりやすかったです。

あと、どちらかというと、UI フレームワークの Vuetify がとても良くできていて、素早く UI 開発ができました。前回は、Angular
と Bootstrap を自分で統合していたのですが、それが一切なく、すぐにマテリアルデザインのアプリが出来上がるのがよかったです。また、今回初のモバイル対応も簡単にできました。

TypeScript

TypeScript は部分的に使った感じでしたが、定義したデータ型をフロントエンドとバックエンドで共有して、コンパイル時にエラーをチェックできたのはよかったです。それ以外は結構 any で逃げた感じ。

Node.js/Express

さすがにもう PHP は覚えていないので、Node.js/Express に置き換え。フロントエンドとバックエンドで同じ言語で開発できるのはやっぱり楽ですね。プロセスの管理は PM2 を使用しました。

nginx

フロントエンドと API をルーティングするために、nginx を使ってみました。フロントは、普通に Web サーバとして、ビルドされたスタティックをサーブし、API は Express のローカルサーバへのプロキシとして動かしてます。これが正しいのかは良くわからず...

Docker

一番大変だったのは、Docker での環境構築で、概念を習得するのも、試行錯誤するのも時間がかかりました。いまだに正しい使い方をしているか自信ないですが、ステージングと本番環境をすぐに立ち上げられたり、ローカルで同じ環境で試せるのはよかったです。

あと、PM2 の設定や Bitbucket からの Webhook での更新とか、いろいろトライして、いろいろ勉強になりました。

今後

今回モバイル対応もしたので、Android 向けに PWA でアプリ化したいのと、iOS でネイティブクライアントを作ろうと思っています。MVVM や RxSwift の勉強のために。

と、特にオチもないですが、機能的にも色々進化しているので、Twitter をやってましたら、ぜひ icotile3 をお試しください。

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む