69

I have a vue component with

 <form @keydown="console.error($event.target.name);">

gives

app.js:47961 [Vue warn]: Property or method "console" is not defined on the instance but referenced during render.

window.console doesn't work either

What is the proper way to use console and window in a template to debug?

Wenfang Du
  • 8,804
  • 9
  • 59
  • 90
Toskan
  • 13,911
  • 14
  • 95
  • 185

11 Answers11

40

Simplest way of providing global objects to the template is to place them in computed, like this:

console: () => console. Same goes for window,

computed: {
  console: () => console,
  window: () => window,
}

See it here.

tao
  • 82,996
  • 16
  • 114
  • 150
  • If you use `vue-class-component`, simply adding `console = console;` in your class definition will work; – Lou Garczynski Jun 25 '21 at 13:25
  • 1
    @Louis, true, but potentially problematic. When you do that, it's the equivalent of declaring it in `data` function. You make the entire contents of `console` reactive, which is potentially lot of work for Vue, depending on what you have in `console` at any one time. Where as the above computed (or `get console() { return console }` in a `vue-class-component`) only exposes a reference to the object, without adding refs to it. This becomes a lot more clear in Vue 3, which does a much better job at exposing how reactivity works. – tao Jun 25 '21 at 13:55
  • You cannot make the content of console reactive to my knowledge, it's a read-only object – Lou Garczynski Jun 26 '21 at 15:07
  • Nope. `console` is not readonly. Remarkably easy to test, too. Regardless, that's completely besides the point. The point is that Vue will try to make whatever you pass to it reactive with one method and will not attempt this with the other method. That's the point I was making. Is it problematic? In most cases, no. Is it potentially problematic? Yes. There are cases where it can become a problem. – tao Jun 26 '21 at 18:59
  • `console.foo = console.log; console.log = () => console.foo('my knowledge is limited'); console.log('console is a read-only object');` – tao Jun 26 '21 at 19:16
  • After checking, you're correct, it does wrap the property in a proxy object. You could however have `console=Object.freeze(console)`. If you don't like the side effect, you could declare `Object.freeze(console)` in your main.js. – Lou Garczynski Jun 29 '21 at 13:52
  • That's potentially even more problematic. I don't want to interfere with the `console` object. I want it to function and mutate as it was designed to, if it needs to. I just to provide a reference to it in template and essentially, not touch it. And that's achieved by providing it via a `getter` a.k.a `computed` in Vue 2. – tao Jun 29 '21 at 14:35
  • Your solution is absolutely better, just wanted to point out that Vue can avoid any wrapping if you want it. – Lou Garczynski Jun 30 '21 at 17:34
  • 1
    I just experienced the weirdest infinite loop due to console=console :-( It caused random computed properties to be re-calculated indefinitely, even those outside the component, and also after closing the troubled component. DON'T use console=console! – Bouke Versteegh Jan 31 '22 at 13:47
  • 1
    Actually, it was not a comment to you @tao, it seems like you posted the right answer. But I wanted to share this experience to add a practical example of how an app might go really nuts from this mistake. To further scare people: my app was making 1000 requests per second and crashed my browser completely. But not immediately, only after triggering some reactivity on the component. So use a getter everyone :-) – Bouke Versteegh Jan 31 '22 at 20:42
  • Does that work with CompositionAPI? It's not recommended anyway but I'm simply curious about how far we can push this hack. – kissu Dec 02 '22 at 01:40
  • @kissu, doesn't work in ` – tao Dec 02 '22 at 09:12
  • But the question is how to make them available in ` – tao Dec 02 '22 at 09:31
30

If you want to run it inline instead of using a method, just add this to the form:

Codepen: https://codepen.io/x84733/pen/PaxKLQ?editors=1011

<form action="/" @keydown="this.console.log($event.target.name)">
  First: <input type="text" name="fname"><br>
  Second: <input type="text" name="fname2"><br>
</form>

But it'd be better to use a method instead of running functions inline, so you have more control over it:

<!-- Don't forget to remove the parenthesis -->
<form action="/" @keydown="debug">
  First: <input type="text" name="fname"><br>
  Second: <input type="text" name="fname2"><br>
</form>

...

methods: {
  debug (event) {
    console.log(event.target.name)
  }
} 
Un1
  • 3,874
  • 12
  • 37
  • 79
18

You can use $el.ownerDocument.defaultView.console.log() inside your template

Pro: Doesn't require any component changes
Con: Ugly

Arik
  • 5,266
  • 1
  • 27
  • 26
  • 1
    this works for Vue 2 in contrast to 'this.console.log()', which didn't work. For quick debugging without bigger code changes this was the best solution for me – greglit Oct 06 '21 at 11:56
14

With Vue ^3.3, you can now use console directly in the template:

<template>
  <!-- just works, no more `console` doesn't exist -->
  <button @click="console.log">Log</button>
</template>

If using Vue prior to 3.3, do:

const app = createApp(App)

app.config.globalProperties.console = console

If also using TypeScript:

// types.d.ts
export {}

declare module 'vue' {
  interface ComponentCustomProperties {
    console: Console
  }
}

If using Vue 2, do:

Vue.prototype.console = console

Use console.* inside the template:

<h1>{{ console.log(message) }}</h1>

To not interfere with the rendering, use console.* with ?? (or || if using Vue 2, since ?? is not supported in the Vue 2 template):

<h1>{{ console.log(message) ?? message }}</h1>
Wenfang Du
  • 8,804
  • 9
  • 59
  • 90
9

Also if you want to access console from {{ }} you can use global mixin:

Vue.mixin({
    computed: {
        console: () => console
    }
})
Denis Matiash
  • 91
  • 1
  • 2
2

You can use this.console instead console or wrap call to console in a method, i am using eslint config with rule 'no-console': 'off'

2

You can use computed property or methods for this case. If you need to code it as javascript in the Vue template. you have to define console in the data.

Please check the code below.

data(){
        return {
                selected :"a",
                log : console.log
                }
            }
<span>{{log(selected)}}</span>

This will make functionality of console.log available, while resolving the template.

Flair
  • 2,609
  • 1
  • 29
  • 41
1

I'd make a getter for console template variable:

    get console() { return window.console; }
Vaulter
  • 196
  • 2
  • 6
1

For Vue 3, SFC Composition API, you have to define a function and call console or alert inside that function

    <script setup>
    import Child from "./Child.vue";
    
    function notify(message) {
      alert(message);
    }
    </script>
    
    <template>
      <Child @some-event="notify('child clicked')" />
    </template>

dungvo
  • 289
  • 4
  • 7
0

I found this template code that may be useful, https://gist.github.com/jensendarren/11afda8dee3171a192df3443f7a1508a

<!--Make sure to install @vue/cli-service-global first-->
<!--Serve this up using `vue serve` at the command line-->
<!--Details here: https://cli.vuejs.org/guide/prototyping.html -->
<template>
  <div>
    <h1>{{name}}</h1>
    <b>Logging To Vue Component? <span>{{logging}}</span></b>
    <br />
    <button @click="testLog">Test Log</button>|<button @click="testWarn">Test Warn</button>|<button @click="toggleLogging">Toggle Logging</button>
    <hr/>
    <p v-for="(log, i) in logs" :key="i" :style="log.style" class="linebreaks" >{{log.message}}</p>
  </div>
</template>

<script>
export default {
  data() {
    return {
      name: 'Console Log Prototype',
      logs: [],
      o_log: null,
      o_warn: null,
      logging: true
    }
  },
  methods: {
    testLog() {
      var var1 = 'Darren'
      var var2 = 'Jensen'
      console.log('in testLog()')
      console.log('This should be\non a new line')
      console.log(`First name: ${var1}, last name: ${var2}`);
      console.log('string 1', 'string 2', var1, var2)
      console.log(`%c[routeTo] ${var1}`, "color: #00b4e8;")
    },
    testWarn() {
      console.warn('in testWarn()')
    },
    toggleLogging() {
      if(this.logging) {
        // Disable logging
        console.log = this.o_log
        console.warn = this.o_warn
        this.clearLogs();
      } else {
        // Activate logging
        this.overrideLogging();
      }
      this.logging = !this.logging
    },
    clearLogs() {
      this.logs = []
    },
    overrideLogging() {
      let self = this;
      this.o_log = console.log
      this.o_warn = console.warn
      function customLog(...msg) {
        var entry = parseMsgArray(msg)
        self.logs.push(entry)
        self.o_log.apply(console, arguments)
      }
      function customWarn(msg) {
        var entry = {color: 'yellow', message: msg}
        self.logs.push(entry)
        self.o_warn.apply(console, arguments)
      }
      function parseMsgArray(msgArray) {
        var entry;
        if(msgArray[0].includes('%c')) {
          // 2nd param will contain styles to apply
          var applyStyle = msgArray[1]
          var msg = msgArray[0].replace('%c', '')
          entry = {style: applyStyle, message: msg }
        } else {
          entry = {style: {color: 'black', background: 'pink'}, message: msgArray.join(' ')}
        }
        return entry
      }
      console.log = customLog;
      console.warn = customWarn;
    }
  },
  created() {
    this.overrideLogging();
  }
}
</script>

<style scoped>
.linebreaks {
  white-space:pre;
}
</style>
Ahmed Sayed Sk
  • 584
  • 6
  • 20
0

It has been fixed in Vue 3.3, see the GitHub issue. You can use console in the template.

<script setup>
import { ref } from 'vue'
const count = ref(0)
</script>

<template>
  <h1>{{ count }}</h1>
  <button @click="console.log(++count)"</button>
</template>

This post has some background reading on it.

Adriaan
  • 17,741
  • 7
  • 42
  • 75