GAミント至上主義

Web Monomaniacal Developer.

Dockerで動かしたHeadless ChromeでPDFにすると日本語が化ける問題

yagish履歴書でも使っている下記のPDF変換サーバーだけど、日本語の下記のような文字がPDFにすると全部□に化けてしまっていました。

〜◯◎■□◆◇❏★☆

ユーザーが使いそうなのだけ上げたので他にもありそう。

github.com

もちろん日本語フォントはインストールされているので、なんでだろうと思っていましたが、日本語系パッケージとlocale設定が足りなかった模様。
ベースのnode:10-slimはDebianだったので localesとtask-japanese入れてみたら無事表示された。

Dockerfile

FROM node:10-slim
LABEL maintainer="yu_yamazaki@bizocean.co.jp"

# Install fonts
COPY fonts /usr/share/fonts

# Install utilities
RUN apt-get update --fix-missing && apt-get -y upgrade

# Japanese
RUN apt-get install -y locales task-japanese
RUN locale-gen ja_JP.UTF-8
RUN localedef -f UTF-8 -i ja_JP ja_JP
ENV LANG ja_JP.UTF-8
ENV LANGUAGE ja_JP:jp
ENV LC_ALL ja_JP.UTF-8
# 以下略

ENVはこの記事を参考にしました
DockerfileでDebianコンテナの日本語環境を整える

Vue Router&Firebaseの認証付きページでログアウト後に任意のページに飛ばそうと思ったらハマった話

Vue RouterとFirebase Authenticationで認証が必要なページを作っていたが、ログアウト後に任意のページに飛ばそうと思ってもうまく行かなかった。
いろいろ試したけど、イベントリスナーonAuthStateChangedを多重登録しないよう事前にUnsubscribeしたら大丈夫になった。

SSSS.GRIDMANを見ると原作の内容よりも小学生のときの同級生がグリッドマンの絵つき道具袋を使っていたのを思い出します。

全体的なやり方は下記ページといっしょ。
qiita.com
qiita.com

最終的なコードは下記。
router.jsから抜粋

// 詳しくは公式ドキュメント
// https://firebase.google.com/docs/reference/js/firebase.auth.Auth#onAuthStateChanged
let onAuthStateChangedUnsubscribe

router.beforeEach((to, from, next) => {
  if (typeof onAuthStateChangedUnsubscribe === 'function') {
    onAuthStateChangedUnsubscribe()
  }
  if (!to.matched.some(record => record.meta.requiresAuth)) {
    next()
  } else {
    onAuthStateChangedUnsubscribe = firebase.auth().onAuthStateChanged(function (currentUser) {
      if (currentUser) {
        next()
      } else {
        next({
          name: 'login'
        })
      }
    })
  }
})

認証にfirebase.auth().onAuthStateChangedのコールバックを使うようになっているが、それがbeforeEachのタイミングではなく、Firebaseをログアウトした瞬間先に動いてしまい全部loginに飛んでしまって、飛ばしたいページには行かなかったもよう。

これに限らず、以前も書いたけどonなんちゃらのイベントリスナ登録系はなんども登録しないように、また何度も登録が必要な場合は以前のを消し、多重登録しないようにするのが大事だと改めて思う。
uyamazak.hatenablog.com

追記

上記参考ページにあるとおり、firebase.auth().currentUserがある場合の処理を追加したら、ムダも減るし、Unsubscribeしなくてもほとんど問題ないような気がしてきた。

let onAuthStateChangedUnsubscribe
router.beforeEach((to, from, next) => {

  if (!to.matched.some(record => record.meta.requiresAuth)) {
    next()
  } else {
    if (firebase.auth().currentUser) {
      next()
      return
    } else {
      if (typeof onAuthStateChangedUnsubscribe === 'function') {
        onAuthStateChangedUnsubscribe()
      }
      onAuthStateChangedUnsubscribe = firebase.auth().onAuthStateChanged(function (currentUser) {
        if (currentUser) {
          next()
        } else {
          next({
            name: 'login'
          })
        }
      })
    }
  }
})

Vue.js入門 基礎から実践アプリケーション開発まで

Vue.js入門 基礎から実践アプリケーション開発まで

Vue.jsからGTMを使ってGAのイベントを送る

ブログタイトルのGAはGoogle Analytics(以下GA)ではないですが、Google Tag Manager(以下GTM)でGAを読み込んでいる場合、独自イベントの送信がちょっと面倒です。

下記のようなVueプラグインもあるけど、Vue.jsの環境はVue CLI 3で、GTMのタグはindex.htmlに公式通りに入れているため、今回はスルーした。

www.npmjs.com

dataLayerを使う

GTMから独自変数を送るにはdataLayerというグローバル変数を使うらしい。

Developer Guide  |  Google Tag Manager for Web Tracking  |  Google Developers

dataLayerを制するものはGTMを制す - Qiita

こんな感じでなんでも送れる。

dataLayer.push({'event': 'event_name'});

dataLayer.push({
  'color': 'red',
  'conversionValue': 50,
  'event': 'customizeCar'
});

シンプルなVueプラグインを作る

とりあえずエラーイベントを送るように下記のような感じで作った。
category, action, labelはGAのイベントと合わせた。

gtm-datalayer.js

const gtmDataLayer = {
  install: function (Vue, options) {
    const dataLayer = window.dataLayer || []
    Vue.prototype.$gtm = {
      sendErrorEvent: function(category, action, label){
        dataLayer.push({
          event: 'error',
          eventCategory: category || 'defaultCategory',
          eventAction: action || 'defaultAction',
          eventLabel: label || 'defaultLabel'
        })
      }
    }
  }
}
export default gtmDataLayer

基本的にGTMのタグはHTMLの上の方で先に読み込んでいるはずで、dataLayerがセットされていないことはないはずだけど念の為、dataLayerがなくてもエラーが起きない(何も起きない)ようにしている。クリティカルな場面では使わないはず。

main.jsでインストールする

import GtmDataLayer from './plugins/gtm-datalayer'
Vue.use(GtmDataLayer)

これでvueのscript内から

this.$gtm.sendErrorEvent('お好きなカテゴリ', 'お好きなアクション', 'お好きなラベル')

みたいな感じで使える。

GTMで変数追加

勝手に送った変数はそのままでは使えない

ユーザー定義変数に上記、eventCategory、eventAction、eventLabelを追加する

f:id:uyamazak:20181114174848p:plain

GTMでカスタムイベント追加

カスタムイベントを受け取るトリガーを追加する。
f:id:uyamazak:20181114175009p:plain

とりあえず「すべてのカスタムイベント」にしたけど、あとで分けるなら、追加した変数を指定すればよさそう。

GTMでGAイベントのタグを追加

上で作った変数とトリガーを使って作る。
f:id:uyamazak:20181114175206p:plain

あとは公開するだけ。

Vue.js側から送信

これで送る。
今回はヤギが渡したはがきを食べた場面で送るようにした

//省略
  methods: {
    sampleError: function(){
      this.$gtm.sendErrorEvent('yagi', 'eat', `yagi ate postcard! ${this.eatCount}: ${this.content}`)
    }
  }
//省略

GAのリアルタイムイベントで確認

f:id:uyamazak:20181114175826p:plain

取れた。

イベント名を汎用的なものに変更(2018/11/16追記)

GTM側のGTMのトリガーのイベント名をerrorで作ってしまったけど、エラーに限らず汎用的に使えるのでappEventに変更し、エラーはCategoryを使うようにしてみた。

const EVENT_NAME = 'appEvent'
const gtmDataLayer = {
  install: function (Vue, options) {
    const dataLayer = window.dataLayer || []
    Vue.prototype.$gtm = {
      sendError: function(action, label){
        dataLayer.push({
          event: EVENT_NAME,
          eventCategory: 'appError',
          eventAction: action || 'defaultAction',
          eventLabel: label || 'defaultLabel'
        })
      }
    }
  }
}
export default gtmDataLayer

これで、エラーを送るときはカテゴリはappErrorで固定、actionとlabelだけ指定すればよくなった。actionに要約、labelに詳細みたいな感じで書けるので使いやすそう。

this.$gtm.sendError('yagiEte', `yagi ate postcard! count:${app.eatCount}, message:${tmpContent}`)

Vue.js入門 基礎から実践アプリケーション開発まで

Vue.js入門 基礎から実践アプリケーション開発まで