0

I have an input with an initial value:

<input type="text" v-model="name" ref="input" />
<button type="submit" :disabled="$refs.input.defaultValue == $refs.input.value">Submit</button>

However the disabled binding gives an error: Cannot read property defaultValue of undefined. Best way to do this without spamming vm.data too much?

1
  • "this gives an error" ... and the error is? Commented Mar 31, 2018 at 21:35

2 Answers 2

3

The error:

Cannot read property defaultValue of undefined

Is because the ref is not available so soon:

An important note about the ref registration timing: because the refs themselves are created as a result of the render function, you cannot access them on the initial render - they don’t exist yet! $refs is also non-reactive, therefore you should not attempt to use it in templates for data-binding.

And when you add it to the button's template, it tries to use it too soon.

The workaround would be to add a simple conditional:

<button type="submit" :disabled="!$refs.input || $refs.input.defaultValue == $refs.input.value">Submit</button>

But don't be happy just yet.


The defaultValue won't have the value you think

When using v-model, defaultValue will actually always be empty string ("") because Vue initially renders the <input> with an empty value.

To use a variable in the disabled button like you want, my suggestion is: use a mouted() logic to "save" the initial value and, in the button template, compare to it instead of defaultValue.

Demo below.

new Vue({
  el: '#app',
  data: {
    name: 'Hello Vue.js!'
  },
  mounted() {
    this.$refs.input.dataset.defVal = this.$refs.input.value;
  }
})
<script src="https://unpkg.com/vue"></script>

<div id="app">
  <p>{{ name }}</p>
  <input type="text" v-model="name" ref="input" />
  <button type="submit" :disabled="!$refs.input || $refs.input.dataset.defVal == $refs.input.value">Submit</button>
</div>


Alternative: Going Vue all the way

Of course, if it's a possibilty, you should take advantage of Vue's data-driven reactive nature, as tackling with the DOM is always tricky.

The solution would be to just create another variable and populate it on mounted():

new Vue({
  el: '#app',
  data: {
    name: 'Hello Vue.js!',
    defaultName: null
  },
  mounted() {
    this.defaultName = this.name;
  }
})
<script src="https://unpkg.com/vue"></script>

<div id="app">
  <p>{{ name }}</p>
  <input type="text" v-model="name"/>
  <button type="submit" :disabled="name == defaultName">Submit</button>
</div>

Or, if you can set both name and defaultName to the same initial value, the mounted() logic above wouldn't even be necessary.

Sign up to request clarification or add additional context in comments.

Comments

0

My solution would probably be more verbose than using a disabled flag. But I would use: @click.prevent="submit"

Then make a submit method to handle the check, return false if input has not changed.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.