vue-yandex-metrika icon indicating copy to clipboard operation
vue-yandex-metrika copied to clipboard

$metrika is undefined

Open andrei2424 opened this issue 6 years ago • 10 comments

Hey.

  • vue-yandex-metrika version: 1.7.2
  • node version: 10.13.0
  • npm (or yarn) version: 6.4.1

Relevant code or config

handleImport() {
     this.$metrika.reachGoal('import')

      this.showModal(this.$lang.actions.import, 'import')
},

Problem description: After call method handleImport():

https://sentry.io/share/issue/9c0025640c52451baaf56572326847bf/

andrei2424 avatar Nov 08 '18 08:11 andrei2424

You probably have a race condition. You think that the prototype $metrika is already available but it is not. You can check that by adding this to your URL:

/?_ym_debug=1

If the error occurs before "PageView. Counter XXXXXXXX . URL: ............" you have a race condition problem.

I'm using the reachGoal function in a action (Vuex) and I have the same issue. You could use a try and catch block.

picarsite avatar Apr 21 '19 13:04 picarsite

I am experiencing the same issue had to like this in all places

  mounted() {
    setTimeout(() => {
      //call my plugin code with golas here
    }, 2000)
  }

which is not desirable solution

shershen08 avatar Apr 24 '19 14:04 shershen08

Setting everywhere a timeout is not a great solution. Especially when the DOM is still not rendered after 2 seconds for whatever reason (theoretically). After some reasearch and reading the Vue.js documentation, I came to the solution that you have to use the mounted and the $nextTick function in combination to be sure that the DOM is fully rendered (and so the Yandex Metrika script):

Note that mounted does not guarantee that all child components have also been mounted. If you want to wait until the entire view has been rendered, you can use vm.$nextTick inside of mounted:

Source: Vue.js documentation

Just implement their example:

mounted: function () { this.$nextTick(function () { // Call Metrika here }) }

After some tests, it seems that my try-and-catch-block is not triggered anymore. It works and it is a cleaner solution than a timer. Hopefully I could help :)

picarsite avatar Apr 28 '19 21:04 picarsite

@picarsite yes $nextTick is a common way to ensure the Vue's DOM operations are done, but if we look deeper into YM docs - https://yandex.com/support/metrica/code/counter-initialize.html#counter-initialize__check-initialize

we could try to achieve this with:

  1. create internal array for events that were called by the consumer while YM was not operating
  2. add triggerEvent: true in config https://github.com/vchaptsev/vue-yandex-metrika/blob/master/src/config.js#L14
  3. add to init code
document.addEventListener('yacounter${this.config.id}inited', () => {
  //callback to execute all items from internal array for events 
});

shershen08 avatar May 01 '19 15:05 shershen08

@picarsite yes $nextTick is a common way to ensure the Vue's DOM operations are done, but if we look deeper into YM docs - https://yandex.com/support/metrica/code/counter-initialize.html#counter-initialize__check-initialize

we could try to achieve this with:

  1. create internal array for events that were called by the consumer while YM was not operating
  2. add triggerEvent: true in config https://github.com/vchaptsev/vue-yandex-metrika/blob/master/src/config.js#L14
  3. add to init code
document.addEventListener('yacounter${this.config.id}inited', () => {
  //callback to execute all items from internal array for events 
});

... If it doesn't give results in localhost set env: 'production' in options of VueYandexMetrika in main.js Cause warning image

Vue.use(VueYandexMetrika, {
    id: XXXXXXXX,
    router: router,
    env: 'production',
    triggerEvent: true,
    // other options
})

Then use @shershen08 solution in your component

document.addEventListener('yacounterXXXXXXXXinited', () => {
  //callback to execute all items from internal array for events
  this.$metrika.userParams({
     pet: 'cat'
  }) 
  //or whatever you want
})

veebull avatar Oct 19 '19 14:10 veebull

I faced with same problem. If user enable AdBlock or other script remover, i can't call reachGoal(). I get error. I solved this problem with try-catch. Now, it's work perfect.

new Vue({ components: { App }, template: "<App/>", router, data: function () { return { loading: false } }, methods: { **reachGoal: function (goal) { try {this.$metrika.reachGoal(goal)} catch (e) {} }** } }).$mount("#app");

Call in your code: this.$root.reachGoal('goal')

Workoverflow avatar Jan 16 '20 10:01 Workoverflow

Add $metrikaEvents:

export default function install (Vue, options = {}) {
   updateConfig(options); // Merge options and default config
    checkConfig(); // Check if all required options are presented
    Vue.prototype.$metrikaEvents = Vue.$metrikaEvents = new Vue();
    loadScript(() => { // Load Metrika script
        const metrika = createMetrika(Vue) // Create Metrika
        startTracking(metrika) // Start autotracking
        Vue.$metrikaEvents.$emit('ym:ready', metrika);
    }, options.scriptSrc)
}

Listen loadScript:

mounted() {
            this.$metrikaEvents.$on('ym:ready',(metrica) => {
                metrica.reachGoal('aim1');
            })
        },

iafilin avatar Feb 21 '20 18:02 iafilin

Add $metrikaEvents:

export default function install (Vue, options = {}) {
   updateConfig(options); // Merge options and default config
    checkConfig(); // Check if all required options are presented
    Vue.prototype.$metrikaEvents = Vue.$metrikaEvents = new Vue();
    loadScript(() => { // Load Metrika script
        const metrika = createMetrika(Vue) // Create Metrika
        startTracking(metrika) // Start autotracking
        Vue.$metrikaEvents.$emit('ym:ready', metrika);
    }, options.scriptSrc)
}

Listen loadScript:

mounted() {
            this.$metrikaEvents.$on('ym:ready',(metrica) => {
                metrica.reachGoal('aim1');
            })
        },

fixed and updated. it works!

iafilin avatar Feb 21 '20 20:02 iafilin

Had same issue in Firefox private tab.

Add check before submit goal if this.$metrika is defined:

if (this.$metrika) this.$metrika.reachGoal('my-goal')

pdapnz avatar Sep 30 '20 10:09 pdapnz

Tried much options, but yacounterXXXXXXXXinited event not fired..

UPDATE

@armbull Works with env: 'production' only. Thx!

Sogl avatar Jun 03 '23 17:06 Sogl