Why is my computed property not computing? - vue.js

Below is a single-file Vue.js template. The note object is passed to the template as a prop, and includes audio_length and id.
<template>
<span v-show="note.audio_length > 0" class="audio-element-wrapper">
<audio controls preload="none">
<source :src="audioURL" type="audio/webm">
Your browser does not support the audio element.
</audio>
<span>
({{note.audio_length}}s)
</span>
</span>
</template>
<script>
module.exports = {
mounted: function() {
console.log("mounted audioelement", this.note.id);
},
props: ['note'],
computed: {
audioURL: function() {
return "/notes/getAudio/" + this.note.id;
}
}
};
</script>
However, when I load the component, the computed audioURL results in /notes/getAudio/undefined. Apparently, it runs before the note property is loaded. The code inside the curly braces is interpreted correctly. How can I bind the src property to the correctly computed url?

i suppose is not working because your audioURL computed value return a path without your file format.
try to change your computed:
computed: {
audioURL: function() {
return "/notes/getAudio/" + this.id + ".webm";
}
}
For a better debug i advise you to install vue-devtools

Thanks for everyone's help. It turns out that the problem isn't really with Vuejs per se. It's a browser issue. Even though the src element was actually modified, it doesn't update the action of the element until you call .load()
So, the solution that worked was as follows:
<template>
<span v-show="note.audio_length > 0" class="audio-element-wrapper">
<audio ref="player" controls preload="none">
<source :src="audioURL" type="audio/webm">
Your browser does not support the audio element.
</audio>
<span>
</template>
And then, in the scripts:
updated: function() {
this.audioURL = "/notes/getAudio/" + this.note.id;
this.$refs.player.load();
},

Solution 1 (vue 2.x)
If you want to pass the object properties as a props then you have to use v-bind directive, see the docs here.
I suppose in your parent component you have something like that for data property:
data: function () {
return {
note: {
audio_lenght: 100,
id: 12
}
}
Then write your component as:
<your-component v-bind='note'></your-component>
And in YourComponent.vue:
...
props: ['id', 'audio_lenght']
...
computed: {
audioURL: function() {
return "/notes/getAudio/" + this.id;
}
}
Solution 2
Write your component as:
<your-component :note='note'></your-component>
In YourComponent.vue:
props: {
note: {
type: Object
}
},
computed: {
audioURL: function() {
return "/notes/getAudio/" + this.note.id;
}
}

Related

Vue Dynamic Form Values AND Keys, with pre existing value for render, why not rendering?

I am trying to create a dynamic 'quick input' form with Vue.
A simple text input that has a dynamic data key so that I can change what I'm submitting to axios. I couldn't figure out how to get a dynamic key name coming from a prop eg
data() {
return {
DYNAMIC-NAME-FROM-PROP: value
}
}
So I've got a values: {} bit of data that gets filled by the props instead.
The code below achieves everything EXCEPT pre-rendering the existing value.
See the comments next to v-model in the tempate:
<template>
<div class="relative">
<input
type="text"
v-model="values[fieldName]" // This is not rendering on load
// v-model="values[this.$props.field]" -> 'this' is null error #input
#keydown.enter.prevent="submit"
/>
</div>
</template>
<script>
export default {
props: ["userId", "field", "preFill"],
data() {
return {
values: {},
fieldName: this.$props.field,
};
},
mounted() {
this.values[this.$props.field] = this.$props.preFill;
},
methods: {
submit() {
axios.post(`/admin/${this.userId}/update`, this.values).then(response => {
// success
});
}
}
};
</script>
Am I going about this completely wrong?
Or am I 'nearly there' and just need to fix the v-model render issue?
To set a dynamic key name on an object in javascript, it turns out you can use square brackets, as in:
{
[keyName] : value
}
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Object_initializer#Computed_property_names
So my code is fixed by simply passing the prop as the key in the axios call:
submit() {
axios
.post(`/admin/${this.userId}/update`, {
[this.$props.field]: this.value
})
.then(response => {
// we out here
});
}

Vue watch page url changes

I'm trying to watch page url. I don't use Vue Router.
My final goal is to set page url as input value:
<template>
<div>
<input v-model="pageUrl">
</div>
</template>
<script>
export default {
data() {
return {
pageUrl: window.location.href,
link: ''
}
},
watch: {
pageUrl: function() {
this.link = window.location.href
}
}
}
</script>
The example above doesn't work somewhy.
I've also tried
watch: {
'window.location.href': function() {
this.link = window.location.href
}
},
Input value is being set only once on component render.
What can be wrong?
well, that is exactly the reason you want to use vue-router!
vue can only detect changes in reactive properties: https://v2.vuejs.org/v2/guide/reactivity.html
if you want to react to changes in the url, you have 2 ways:
https://developer.mozilla.org/en-US/docs/Web/API/Window/popstate_event or
https://developer.mozilla.org/en-US/docs/Web/API/Window/hashchange_event
i would rather use vue-router or a similar plugin.

Vue: Using input value in function

I am using Single File Components and I have a modal component that has an
input box but I can't get the value of the input in a function below using the v-modal name. It keeps coming back as 'name is not defined'. Am I using the v-model attribute incorrectly?
<template>
<input v-model="name" class="name"></input>
</template>
<script>
export default {
methods: {
applyName() {
let nameData = {{name}}
}
}
}
</script>
You're right, you're using the v-model property incorrectly.
First off you need to define a piece of state in your component, using data:
export default {
data: () => ({
name: '',
}),
methods: {
log() {
console.log(this.name);
}
}
}
You can then bind this piece of data in your component using v-model="name", just like you did. However, if you want to access this piece of state in your method, you should be using this.name in your applyName() method.
Your {{name}} syntax is used to get access to the data in your template, like so:
<template>
<span>
My name is: {{name}}!
</span>
</template>
You have to use this pointer to access the model:
<template>
<input v-model="inputName" class="name"></input>
</template>
<script>
export default {
data() {
return {
inputName: '',
}
},
methods: {
applyName() {
// Notice the use of this pointer
let nameData = { name: this.inputName };
}
}
}
</script>
Look at the doc https://v2.vuejs.org/v2/guide/forms.html#v-model-with-Components
In the template, you are referring by name to data, computed or methods. In this case, it refers to data. When the input changes the name then the data is updated.
It is possible to use in a function referring to this.
<template>
<input v-model="name" class="name"></input>
</template>
<script>
export default {
data() {
return { name: '' }
},
methods: {
applyName() {
let nameData = this.name
}
}
}
</script>

how to access refs in methods in vuejs

I want to access template ref in functions inside method objects. Currently, it throws undefined when accessing the refs.
My Code Below:
<template>
<ul ref="lvl1_target" style="width: 440px" class="lvl1_target milestone_asset_data">
<li :style="{'width': getMileStonePercent(4,'lvl1_target')}" class="hybse_data">
<span></span>
<i></i>
<small>$4M</small>
</li>
</ul>
</template>
And My Code in Script tag is below:
export default {
methods: {
getMileStonePercent(num, secWrp) {
let ele = this.$refs[secWrp]
return ele.offsetWidth + '%'
},
},
created() {},
}
Kindly, provide solution to get the width by accessing the template reference in my function. Thanks in advance.
You need to add a ref attribute to the element you want to target.
I'm not really sure what exactly you are trying to do so here is a generic example instead:
<template>
<div ref="myElement"></div>
</template>
export default {
methods: {
setText() {
this.$refs.myElement.innerHTML = "Hello world"
},
},
}
Vue ref documentation
You can try
export default {
methods: {
this.$next(tick() => {
setText() {
this.$refs.myElement.innerHTML = "Hello world"
}
})
}}

Vue model not updating

When I try to update my custom text-area component's model data this.message='<span id="foo">bar</span> the text and html does not display in the htmltextarea tag like it should, but I can see the update applied in the Vue dev tool's console. I've also tried switching to an object instead of a string and using Vue.set, but this does not work either.
Any suggestions on how to fix this?
The goal with the htmlTextArea component is to get the users text from the htmlTextArea tag (this works), manipulate this text and bind it back to the textarea, but with HTML in it.
Custom text-area component:
<template>
<div contenteditable="true" #input="updateHTML" class="textareaRoot"></div>
</template>
<script>
export default {
// Custom textarea
name: 'htmlTextArea',
props:['value'],
mounted: function () {
this.$el.innerHTML = this.value;
},
methods: {
updateHTML: function(e) {
this.$emit('input', e.target.innerHTML);
}
}
}
</script>
Other component:
<template>
...
<htmlTextArea id="textarea" v-model="message"></htmlTextArea>
...
</template>
<script>
data: {
return {
message: 'something'//this works
}
}
...
methods: {
changeText() {
this.message='<span id="foo">bar</span>'//this does not
}
},
components: {
htmlTextArea
}
</script>
You need to set the value explicitly after the value props change. you can watch for value change.
<template>
<div contenteditable="true" #input="updateHTML" class="textareaRoot"></div>
</template>
<script>
export default {
// Custom textarea
name: "htmlTextArea",
props: ["value"],
mounted: function() {
this.$el.innerHTML = this.value;
},
watch: {
value(v) {
this.$el.innerHTML = v;
}
},
methods: {
updateHTML: function(e) {
this.$emit("input", e.target.innerHTML);
}
}
};
</script>
Change the data property into a function, as you have it defined it is not reactive.
data () {
return {
message: 'something'//this works
}
}
Now when you update the message property in your method, the component will update accordingly.
Reactivity in depth