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)" />