vchaptsev / vue-yandex-metrika

Vue plugin for Yandex Metrika
MIT License
73 stars 34 forks source link

$metrika is undefined #10

Open andrei2424 opened 5 years ago

andrei2424 commented 5 years ago

Hey.

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/

picarsite commented 5 years ago

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.

shershen08 commented 5 years ago

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

picarsite commented 5 years ago

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 :)

shershen08 commented 5 years ago

@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 
});
veebull commented 4 years ago

@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
})
Workoverflow commented 4 years ago

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')

iafilin commented 4 years ago

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 commented 4 years ago

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!

pdapnz commented 4 years ago

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')

Sogl commented 1 year ago

Tried much options, but yacounterXXXXXXXXinited event not fired..

UPDATE

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