Im a capable react developer, but have inherited a vue.js project from another developer and have maintained it for several years now, unfortunately I haven’t gone through much personal effort to learn vue as i should.
I have a strange error being thrown from using lodash, I believe it doesnt like my _.debounce
call
Component:
<script>
import _ from 'lodash'
import CostCodeField from '@/components/workdays/CostCodeField'
// ...
</script>
<template lang='html'>
<!-- relevant code snippet -->
<!-- -->
<b-table class="charges-table is-fullwidth" :data="workday.charges" :striped="true" :mobile-cards="false" :row-class="chargeClass">
<b-table-column label="Cost Code" width="260">
<b-field expanded="expanded">
<cost-code-field
:value="props.row.cost_code.number" :disabled="timecard.is_submitted || locked || isLoading(props)"
:job="props.row.job"
@input="set($event, props.index, 'cost_code')"
@change="_.debounce(submit(props.row, props.index), 100)"
></cost-code-field>
<p class="control">
<a
class="button"
@click="triggerCostCode(props.row, props.index)" :disabled="props.row.job.jd_job_number_id === undefined || timecard.is_submitted || isLoading(props)"
>
<b-icon icon="magnify"></b-icon>
</a>
</p>
</b-field>
</b-table-column>
<!-- -->
</b-table>
</template>
console.error
[Vue warn]: Property or method "_" is not defined on the instance but referenced during render. Make sure that this property is reactive, either in the data option, or for class-based components, by initializing the property. See: https://v2.vuejs.org/v2/guide/reactivity.html#Declaring-Reactive-Properties.
found in
---> <ChargesTable> at src/components/workdays/ChargesTable.vue
<BTabItem>
<BTabs>
<WorkdayListItem> at src/components/workdays/WorkdayListItem.vue
<Timecard> at src/components/timecards/Timecard.vue
<App> at src/App.vue
<Root> vue.esm.js:628
VueJS 3
change ChargesTable.vue:127
VueJS 4
setCode CostCodeField.vue:108
VueJS 12
mutations timecards.js:322
mutations timecards.js:319
wrappedMutationHandler vuex.esm.js:844
commitIterator vuex.esm.js:466
commit vuex.esm.js:465
_withCommit vuex.esm.js:624
commit vuex.esm.js:464
boundCommit vuex.esm.js:409
submit CostCodeLookup.vue:134
submit CostCodeLookup.vue:16
VueJS 33
[Vue warn]: Error in v-on handler: "TypeError: _vm._ is undefined"
found in
---> <CostCodeField> at src/components/workdays/CostCodeField.vue
<BField>
<BTableColumn>
<BTable>
<ChargesTable> at src/components/workdays/ChargesTable.vue
<BTabItem>
<BTabs>
<WorkdayListItem> at src/components/workdays/WorkdayListItem.vue
<Timecard> at src/components/timecards/Timecard.vue
<App> at src/App.vue
<Root> vue.esm.js:628
TypeError: _vm._ is undefined
change ChargesTable.vue:127
VueJS 4
setCode CostCodeField.vue:108
VueJS 12
mutations timecards.js:322
mutations timecards.js:319
wrappedMutationHandler vuex.esm.js:844
commitIterator vuex.esm.js:466
commit vuex.esm.js:465
_withCommit vuex.esm.js:624
commit vuex.esm.js:464
boundCommit vuex.esm.js:409
submit CostCodeLookup.vue:134
submit CostCodeLookup.vue:16
VueJS 33
Advertisement
Answer
Accessing _
in template
Assuming you’re using the Options API, importing _
does not automatically make it available to the template (as @tao pointed out in his answer). The template can only access fields exposed via the component options (e.g., data
, props
, methods
, computed
, etc.) in addition to a few allow-listed globals (Vue 2 allowed globals, Vue 3 allowed globals).
Using _.debounce
_.debounce
‘s first argument is a function reference:
_.debounce(submit(props.row, props.index), 100) // ❌ calls `submit()`, and passes the result to _.debounce()
To create a debounced submit
, pass the submit
-reference as an argument to _.debounce
like this:
_.debounce(submit, 100)
You technically could invoke the debounced function immediately:
_.debounce(submit, 100)(props.row, props.index)
…but don’t do that in the template (see reason below).
Using a debounced event handler for v-on
When the value of the v-on
directive (@
for shorthand) is an expression (as in your case), the template compiler automatically wraps the expression in a function, so this:
@change="_.debounce(submit, 100)"
…essentially becomes:
@change="($event) => _.debounce(submit, 100)"
…which would have no effect, since debounce
doesn’t invoke the wrapped function itself.
You might be tempted to call the function immmediately in:
@change="_.debounce(submit, 100)(props.row, props.index)"
…but that creates a new debounced function on every event, which defeats the debouncing.
Solution
Create a debounced function in the <script>
part of the SFC that could then be used as the v-on
value:
Options API:
<script>
import { debounce } from 'lodash'
export default {
created() {
this.debouncedSubmit = debounce(this.submit, 100)
},
methods: {
submit(row, index) {/*...*/}
}
}
</script>
Composition API in setup()
option:
<script>
import { debounce } from 'lodash'
export default {
setup() {
const submit = (row, index) => {/*...*/}
return {
debouncedSubmit: debounce(submit, 100),
}
}
}
</script>
Composition API in <script setup>
:
<script setup>
import { debounce } from 'lodash'
const submit = (row, index) => {/*...*/}
const debouncedSubmit = debounce(submit, 100)
</script>
Template:
<cost-code-field @change="debouncedSubmit(props.row, props.index)" />