Prop is undefined on Vue - javascript

HTML passed props to the Vue tag, no value existed. Even if I tried to print out mounted, created, undefined was printed.
Vue is running on the Django Server. So I changed delimiters and printed the apps, but nothing was printed out.
HTML Code
<radio-input :radio-content="personal"></radio-input>
Vue Code
radioInput = new Vue({
props: ["radioContent"],
template:
'<label>\n' +
' <input type="radio" :name="name" :value="value" :v-model="v_model" :required="required">\n' +
' <span>${ radioContent }</span>\n' +
'</label>\n',
delimiters: ['${', '}'],
el: 'radio-input',
data: {
name: 'A1_P1_S1_B0',
value: 'personal',
v_model: 'A1P1S1Q',
required: true,
}
});
Expected Result is "radioButton + 'personal' text". But "personal" text is not printed

Since personal is not a variable in the parents's data albeit its a static value that is passed to the <radio-input/> component.
To pass static props binding colons :someProp are not needed. When you use that its basically v-bind:someProp where the Vue component is going to look for someProp in its data or would try to consider it as a javascript expression like Number or Boolean or Array and not as static text to be passed on.
Change the HTML code for component to:
<radio-input radio-content="personal"></radio-input>
--Update based on code snippet & comment--:
Okay! couple of things here
props in Vue are something that is passed on from parent to child (consider it a way of communicating things parent->child which are Vue components or instance not pure HTML).
Why is not working here: You have only one Vue instance for
<radio-input/> that is sort of parent so defining prop onto it is
meaningless since there is no parent Vue that can pass this. Read
Some More
In this case you could simply remove the prop and the binding value
altogether and use personal text directly from data property
value that u've defined, if that's what you want.
<html>
<head>
</head>
<body>
<radio-input></radio-input>
<script src="https://unpkg.com/vue#2.6.10/dist/vue.js"></script>
<script>
radioInput = new Vue({
template: '<label>\n' +
' <input type="radio" :name="name" :value="value" :v-model="v_model" :required="required">\n' +
' <span>${ value }</span>\n' +
'</label>\n',
delimiters: ['${', '}'],
el: 'radio-input',
data: {
name: 'A1_P1_S1_B0',
value: 'personal',
v_model: 'A1P1S1Q',
required: true,
}
});
</script>
</body>
</html>

Related

Using Vue.js directives within component template

I'm new to Vue.js and trying to create a component that connects to one object within some global-scope data and displays differently based on the specifics of each object. I think I'm misunderstanding how the directives v-if and v-on work within component templates. (Apologies if this should actually be two different questions, but my guess is that the root of my misunderstanding is the same for both issues).
Below is a minimal working example. My goal is to have each member entry only display the Disable button if the associated member is active, and enable changing their status via the button. (I also want to keep the members data at the global scope, since in the actual tool there will be additional logic happening outside of the app itself).
<html>
<head>
<script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
</head>
<body>
<div id="app">
<member-display
v-for="member in members"
v-bind:member="member"
></member-display>
</div>
<script>
var members = [
{name: "Alex", status: "On"},
{name: "Bo", status: "On"},
{name: "Charley", status: "Off"}
]
Vue.component('member-display', {
props: ['member'],
computed: {
active: function() {
// Placeholder for something more complicated
return this.member.status == "On";}
},
methods: {
changeStatus: function() {
this.member.status = 'Off';
}
},
// WHERE MY BEST-GUESS FOR THE ISSUE IS:
template: `
<div>
{{member.name}} ({{member.status}})
<button v-if:active v-on:changeStatus>Disable</button>
</div>
`
});
var app = new Vue({
el: "#app",
data: {
members: members
}
})
</script>
</body>
</html>
Thanks for your help!
The code v-if and the v-on for the button just have the wrong syntax. The line should look like this:
<button v-if="active" v-on:click=changeStatus>Disable</button>

Vue.delete warning to avoid using JavaScript unary operator as property name on click

I get the following warning when using Vue.delete:
[Vue warn]: Error compiling template: avoid using JavaScript unary operator as property name: "delete(testObj,)" in expression #click="Vue.delete(testObj,'label')"
I can use Vue.delete anywhere else and it seems fine. Am I using it wrong?
new Vue({
el: '#app',
data() {
return {
testObj: {
label: "The label"
}
};
}
});
<div id="app">
{{testObj.label}}
<button #click="Vue.delete(testObj,'label')">Delete</button>
</div>
<script src="https://cdn.jsdelivr.net/npm/vue#2.6.11/dist/vue.js"></script>
The global Vue will not be available inside your template. Everything in a template is scoped to the current Vue instance, so you're effectively trying to access this.Vue. Trying to access any of the properties of the global Vue won't work.
You can use $delete instead of Vue.delete inside a template.
https://v2.vuejs.org/v2/api/#vm-delete
new Vue({
el: '#app',
data() {
return {
testObj: {
label: "The label"
}
};
}
});
<div id="app">
{{testObj.label}}
<button #click="$delete(testObj, 'label')">Delete</button>
</div>
<script src="https://cdn.jsdelivr.net/npm/vue#2.6.11/dist/vue.js"></script>
I would add that the specific error message you're seeing relates more generally to trying to use any property called delete but that's something of a moot point as you should be using $delete anyway.

How to pass data into Vue instance so that subsequent updates are reflected?

I'm trying to dynamically update a Vue JS prop after the view has been loaded and the custom has been initialised. I'm building a custom Vue plugin and am using props to pass options, one of which is a object which I need to dynamically update the value passed after the component has loaded, e.g:
<div id="app">
<script>
var seedData = {
percent: 50,
name: 'Smith'
}
setInterval(() => {
seedData = {
percent: Math.random(),
name: 'Smith'
}
}, 1000)
</script>
<offers :parent-data="seedData"></offers>
</div>
Vue.component('offers', {
template: '<h1>Parent Data: {{ parentData.percent }}</h1>',
props: {
parentData: {
default: () => ({
percent: 0,
name: 'John'
}),
type: Object
},
}
});
// create a new Vue instance and mount it to our div element above with the id of app
var vm = new Vue({
el: '#app'
});
This will load the initial name/values from offersData, however, the new values on the setInterval doesn't get passed through.
I've tried adding a watcher inside of my custom Vue plugin that gets loaded through <offers> but this doesn't seem to work either:
watch: {
parentData: function (newVal) {
this.parentData = newVal
}
}
UPDATE
The following is my implementation:
Code Pen -> https://codepen.io/sts-ryan-holton/pen/VwYNzdZ
There are multiple problems with your code
Everything inside <div id="app"> is treated by the Vue as a template and compiled - see the docs
If neither render function nor template option is present, the in-DOM HTML of the mounting DOM element will be extracted as the template. In this case, Runtime + Compiler build of Vue should be used.
Including <script> tag there is wrong. Just try to include vue.js (debug build) instead of vue.min.js (minified production build of Vue) and you will see bunch of errors (btw its always good idea to use debug build for development as it gives you lots of useful errors and warnings)
The fact that it "somehow works" in prod build (ie the initial values are shown on the page) doesn't mean it's supported...
So the inside of <div id="app"> is template for Vue. As I said before in the comments, all data referenced by template must be in the context of Vue instance. You cannot pass some global variable into props. So moving <script> outside of <div id="app"> won't help
[Vue warn]: Property or method "seedData" 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 initialising the property.
What you can do is to pass seedData object into root Vue instance like this:
var vm = new Vue({
el: '#app',
data: {
seedData: seedData
}
});
Now the errors are gone but data changes is not reflected still. Reason for that is not Vue specific. Its simple JavaScript. Object are passed by reference in JS. Look at this code:
var a = { name: 'John' }
var b = a
a = { name: 'Smith' }
// b is still pointing to "John" object
To workaround it, don't replace whole object. Just mutate it's properties (beware of Vue reactivity caveats)
setInterval(function() {
seedData.name = 'John';
seedData.percent = Math.random();
}, 1000)
Whole solution:
Vue.component('offers', {
template: '<h1>{{ parentData.name }}: {{ parentData.percent }}</h1>',
props: {
parentData: {
default: () => ({
percent: 0,
name: 'John'
}),
type: Object
},
}
});
// create a new Vue instance and mount it to our div element above with the id of app
var vm = new Vue({
el: '#app',
data: {
seedData: seedData
}
});
<script>
var seedData = {
percent: 60,
name: 'Smith'
}
setInterval(function() {
seedData.name = 'John';
seedData.percent = Math.random();
}, 1000)
</script>
<div id="app">
<offers :parent-data="seedData"></offers>
</div>
<script type="text/javascript" src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.6.10/vue.js"></script>

Trying to change object property and keep reactivity. Property or method "vm" is not defined on the instance but referenced during render

Until now, I was trying to change the value of an object property to false using #click='review.isActive = true', however, it turns out Vue cannot detect such changes which means that property won't be reactive.
Based on the documentation https://v2.vuejs.org/v2/guide/list.html#Caveats, this can be avoided by using one of the following methods:
Vue.set(vm.reviews, index, true)
vm.reviews.splice(index, 1, true)
Sadly I get an error using either way. I assume I don't quite understand how the Vue instance works and I'm trying to access it when I shouldn't be able to.
I assume Vue/vm are supposed to be the Vue instance but I can't seem to access it.
In both cases, I get error "Property or method "vm"/"Vue" is not defined on the instance but referenced during render."
I'm using Vue webpack-simple webpack template and my Vue instance seems to be initialized in main.js file like this:
var vm = new Vue({
el: '#app',
router: router,
store: store,
render: h => h(App)
});
The file in which the array manipulation is happening is different and a component.
Assuming your review object is an item in your reviews array and that they do not initially have an isActive property but you'd like to add one, you can use vm.$set to add a new reactive property.
For example
new Vue({
el: '#app',
data: {
reviews: [{
id: 1,
name: 'Review #1'
},{
id: 2,
name: 'Review #2'
},{
id: 3,
name: 'Review #3'
}]
}
})
<script src="https://cdn.jsdelivr.net/npm/vue"></script>
<div id="app">
<div v-for="review in reviews" :key="review.id">
<p>
Review {{ review.name }} status:
<code>{{ review.isActive ? 'ACTIVE' : 'INACTIVE' }}</code>
</p>
<button #click="$set(review, 'isActive', true)">Activate</button>
</div>
</div>
Note the use of $set in the #click handler.
From the comment below, if you wanted to add new properties after the data is retrieved, do it before you assign the value to your data property, eg
this.reviews = data.from.somewhere.map(review => ({...review, isActive: false}))
If you must add the new property after the data is assigned, you need to use vm.$set to make it reactive
this.reviews.forEach(review => {
this.$set(review, 'isActive', false)
})
Once these properties are added reactively, you can go back to simply changing them without $set
<button #click="review.isActive = true">

bind data to vue model dynamically in component

I'm trying to make a simple form that will accept user's input for different types of currency.
Here's a (broken) fiddle that hopefully gets across what I want to do:
https://jsfiddle.net/4erk8yLj/7/
I'd like my component to bind data to my root vue instance, but I'm not sure if my v-model string is allowable. Check it out:
Vue.component('conversion-row', {
props: ['currency', 'values'],
template: '<div>{{currency}}:</div><div><input v-model="values[currency]></div><',
});
var vm = new Vue({
el: "#app",
data: {
currencies: ['USD', 'BTC'],
values: {
'BTC': '',
'USD': ''
}
}
});
template:
<div id="app">
<li>
<conversion-row is li v-for="currency in currencies" v-bind:currency="currency">
</conversion-row>
</li>
</div>
What's a good way to fix this?
Couple of things you might need to correct:
First, the data property must be a function rather than an object. This allows every instance to get data recomputed every time it is being created, see:
var vm = new Vue({
el: "#app",
data() {
return {
currencies: ['USD', 'BTC'],
values: {
'BTC': 'BTC Value',
'USD': 'USD Value',
},
};
}
});
Second, <conversion-row> doesn't have values property bound. Here's what you can do:
<div id="app">
<li v-for="currency in currencies">
<conversion-row :currency="currency" :values="values"></conversion-row>
</li>
</div>
Last, the component should always aim for one root element (wrapper) and then you can nest as many children inside as you want. What's more, instead of using v-model, you can bind value which is the proper way to pass a value to an input (one-way data binding), check the following:
Vue.component('conversion-row', {
props: ['currency', 'values'],
template: '<div>{{currency}}:<input type="text" :value="values[currency]"></div>'
});
There's more improvements you could possibly make here like re-thinking if you need to pass values as well as currency to the conversion-row but I'm pretty sure you'll figure it out later on.
All that above will make your code run and execute properly, here's the working example (fork of yours):
https://jsfiddle.net/maciejsmolinski/mp8m0ben/1/
Does this help?
Not sure what you're aiming for in terms of using v-model, but here's an example of working v-model (based on your example):
Vue.component('conversion-row', {
props: ['currency', 'values'],
template: '<div>{{currency}}:<input type="text" v-model="values[currency]"></div>'
});
And the corresponding template:
<div id="app">
<p><strong>USD Value:</strong> {{ values.USD }}</p>
<p><strong>BTC Value:</strong> {{ values.BTC }}</p>
<br>
<li v-for="currency in currencies">
<conversion-row :currency="currency" :values="values"></conversion-row>
</li>
</div>
You can find it under the following URL:
https://jsfiddle.net/maciejsmolinski/0xng8v86/2/

Categories

Resources