nuxt-community / google-gtag-module

Enable google gtagjs for NuxtJs
MIT License
145 stars 29 forks source link

Set GA tracking ID outside of Nuxt Config somehow? #9

Closed drewbaker closed 5 years ago

drewbaker commented 5 years ago

It would be great if there was a way to add the tracking IDs into this module somehow. Currently I use nuxtServerInit to fetch them via Apollo add them into Vuex, but I can't get them into Nuxt Config obviously.

Because the tracking codes are stored in our CMS, and we like clients to be able to change them without requiring a code change from us.

dohomi commented 5 years ago

I'm pretty sure you can do this with setting it through the this.$gtag('config', 'YOUR_ID'). Give it a try it should work.

drewbaker commented 5 years ago

Thanks @dohomi, good idea. I wasn't sure if this would allow me to actually start gtag with no ID set in nuxt.config.js, but good idea.

I tried it like you said, but it crashes with an error that this.$gtag is not a function when I use it in NuxtServerInit, because this is the wrong scope I think. But it feels like we are close. I tired context.app.$gtag and it also didn't work. Any ideas?

If anyone is curious, this is how I'm doing it.

export const actions = {
    async nuxtServerInit(store, context) {

        // Get site settings from WordPress and save them to store
        let client = context.app.apolloProvider.defaultClient
        await client
            .query({
                query: WpSettingsQuery
            })
            .then(({ data }) => {
                let settings = _get(data, 'generalSettings', {})

                let meta = {
                    title: settings.title,
                    host: context.req.headers.host,
                    description: settings.description,
                    themeScreenshotUrl: settings.themeScreenshotUrl,
                    gaTrackingCodes: [
                        settings.gaTrackingCode1,
                        settings.gaTrackingCode2
                    ]
                }
                store.commit('SET_SITE_META', meta)
            })

        // Set GA tracking codes
        store.state.siteMeta.gaTrackingCodes.forEach(code => {
             // code = UA-XXXX-XX
             if (code) {
                context.app.$gtag('config', code, {
                    anonymize_ip: true,
                    send_page_view: false
                })
            }
        })
    }
}
dohomi commented 5 years ago

I don't think you necessarily need to put that code into onServerInit. We are talking about GA and that is a client side thingy. Just add this code into a router hook or into your initial layout file on mounted. That should be sufficient in my opinion

dohomi commented 5 years ago

By the way: you have to add anyhow an initial ID of GA that the plugin starts to initialise its code. After that you add your custom code. Maybe its just better if you don't rely on this plugin at all and wire up the code in your project individually. If you check out the code base, its very few lines of code you need to place in your app to make it work.

drewbaker commented 5 years ago

Yeah at this point I think you're right, I'm going to try and convert it into a plugin.

drewbaker commented 5 years ago

@dohomi I was hoping you might be able to point me in the right direction here, I'm super close.

I have a plugin file loading on client side only.

I largely tried to convert this module into a plugin. But the issue is head.script.push doesn't result in the script being added on first page load, so this doesn't track the first page load, but it does for all subsequent page transitions.

The issue is that gtag script needs the UA code in it's URL, so I can't just add that into the regular script object in nuxt.config.js.

// plugins/google.gtag.client.js with "mode": "client
export default ({ store, app: { head, router, context } }, inject) => {
    // Remove any empty tracking codes
    const codes = store.state.siteMeta.gaTrackingCodes.filter(Boolean)

    // // Abort if no codes
    if (!codes.length) {
        if (context.isDev) console.log('No Google Anlaytics tracking codes set')
        return
    }

    // Abort if in Dev mode, but inject dummy functions so $gtag events don't throw errors
    if (context.isDev) {
        inject('gtag', () => {})
        return
    }

    // Add script tag to head
    head.script.push({
        src: `https://www.googletagmanager.com/gtag/js?id=${codes[0]}`,
        async: true
    })
    console.log('added script')

    // Include Google gtag code and inject it (so this.$gtag works in pages/components)
    window.dataLayer = window.dataLayer || []
    function gtag() {
        dataLayer.push(arguments)
    }
    inject('gtag', gtag)
    gtag('js', new Date())

    // Add tracking codes from Vuex store
    codes.forEach(code => {
        gtag('config', code, {
            send_page_view: false // necessary to avoid duplicated page track on first page load
        })

        console.log('installed code', code)

        // After each router transition, log page event to Google for each code
        router.afterEach(to => {
            gtag('config', code, { page_path: to.fullPath })
            console.log('afterEach', code)
        })
    })
}
dohomi commented 5 years ago

I guess you are looking for something like this:

let script = document.createElement(`script`)
script.defer = true
script.onload = () => {
   // set a global var that your ga tag is loaded ?
}
script.src = `https://www.googletagmanager.com/gtag/js?id=${codes[0]}`
document.head.appendChild(script)

wrap this code into a function and just load it as long the script never been loaded before. I hope this leads you to the right way

drewbaker commented 5 years ago

For future reference, this is what I ended up with and it seems to work great. The only thing I' not sure on is @dohomi has defer = true on the script, but google has async = true so I went with that instead.

export default ({ store, app: { router, context } }, inject) => {
    // Remove any empty tracking codes
    const codes = store.state.siteMeta.gaTrackingCodes.filter(Boolean)

    // Abort if no codes
    if (!codes.length) {
        if (context.isDev) console.log('No Google Anlaytics tracking codes set')
        return
    }

    // Abort if in Dev mode, but inject dummy functions so $gtag events don't throw errors
    if (context.isDev) {
        inject('gtag', () => {})
        return
    }

    // Check if we already added script to head
    let gtagScript = document.getElementById('gtag')
    if (gtagScript) {
        return
    }

    // Add script tag to head
    let script = document.createElement('script')
    script.async = true
    script.id = 'gtag'
    script.src = `https://www.googletagmanager.com/gtag/js?id=${codes[0]}`
    document.head.appendChild(script)    

    // Include Google gtag code and inject it (so this.$gtag works in pages/components)
    window.dataLayer = window.dataLayer || []
    function gtag() {
        dataLayer.push(arguments)
    }
    inject('gtag', gtag)
    gtag('js', new Date())

    // Add tracking codes from Vuex store
    codes.forEach(code => {
        gtag('config', code, {
            send_page_view: false // necessary to avoid duplicated page track on first page load
        })

        // After each router transition, log page event to Google for each code
        router.afterEach(to => {
            gtag('config', code, { page_path: to.fullPath })
        })
    })
}