How to render a object to template view using vuejs - vue.js

I have a sample array from data
data() {
return {
states: [{id:1,name:"AL"},{id:2,name:"AK"},{id:3,name:"CA"},{id:4,name:"TX"},{id:5,name:"WI"},{id:6,name:"WY"}]
}
}
...
and view template
<template>
<div :id="states[2].id">
This is California - {{ states[3].name }}
<div>
</template>
I want render a object from array (id: 3), but result not work. I don't want to use v-for in this part.
Error: [Vue warn]: Error in render: "TypeError: Cannot read properties of undefined (reading 'id')"
I try again with indexOf() but result not work, too

You can create method or computed property and return filtered state, and use optional chaining for error:
new Vue({
el: '#demo',
data() {
return {
states: [{id:1,name:"AL"},{id:2,name:"AK"},{id:3,name:"CA"},{id:4,name:"TX"},{id:5,name:"WI"},{id:6,name:"WY"}]
}
},
methods: {
state(id) {
return this.states[id]?.name || 'no state'
}
}
})
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.17/vue.js"></script>
<div id="demo">
<div :id="states[2]?.id">
This is California - {{ state(3) }}
</div>
</div>

Related

Calling function from outside of component but rendered in v-for loop

I have some component which is rendered 6 times in v-for loop. I'm trying to do onclick function which will call method inside of specified chart component. But now i'm getting errors like this.$refs.chart1.pauseChart() is not an function. This is how i'm trying to achieve it:
<BaseChart ref="`chart$[index]`" #click="pauseChart(index)"/>
pauseChart(index) {
this.$refs.[`chart${index}`].pauseChart()
}
refs inside v-for are arrays rather than singulars. Therefore, if you have
<template v-for="(something, index) in list">
<BaseChart ref="chart" :key="index" #click="pauseChart(index)" />
</template>
you should use
methods:
{
pauseChart(idx)
{
this.$refs.chart[idx].pauseChart();
}
}
For more information - refer to Vue documentation
ref will be having an array. hence, you have to access that with 0 index.
Live Demo :
Vue.component('child', {
methods: {
pauseChart() {
console.log('child method call');
}
},
props: ['childmsg'],
template: '<p v-on="$listeners">{{ childmsg }}</p>',
});
var app = new Vue({
el: '#app',
methods: {
pauseChart(chartIndex) {
this.$refs[chartIndex][0].pauseChart();
}
}
});
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.17/vue.min.js"></script>
<div id="app">
<div v-for="index in 6" :key="index">
<child childmsg="Hello Vue!" :ref="`chart${index}`" #click="pauseChart(`chart${index}`)">
</child>
</div>
</div>

Do all imported arrays in Vue.js have to be reactive?

I'm importing a simple array of 4 strings called QUIZ_DATA into my Vue.js component:
["Question1?", "Question number 2?", "This is number 3!", "And # 4"]
These 4 questions are static and won't change. I don't need them to be reactive, so I import them into my component as QUIZ_DATA, but when I try to iterate through the array, it gives me an error that the property is non-reactive.
<template>
<div v-for="(question, index) in QUIZ_DATA">
{{ question }}
</div>
</template>
<script>
import QUIZ_DATA from "~/plugins/QuizData.js";
export default {
// Nothing here yet
}
</script>
The console says:
Make sure that this property is reactive.
I got rid of the error by copying the array into my component's data object, which feels like an unnecessary step, given that the array won't change:
data(){return {quizData: QUIZ_DATA}}
Is there a way of iterating through static objects without needing to copy them into the component's data() object first?
QUIZ_DATA must be a property of the component instance if you want to access it from within the template.
Non-reactive data can be assigned to the component instance in the created hook.
created() {
this.QUIZ_DATA = QUIZ_DATA;
}
You can return your array with computed property:
const QUIZ_DATA = ["Question1?", "Question number 2?", "This is number 3!", "And # 4"]
new Vue({
el: '#demo',
computed: {
questions() {
return QUIZ_DATA
}
}
})
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.17/vue.js"></script>
<div id="demo">
<div v-for="(question, index) in questions">
{{ question }}
</div>
</div>
Is there a way of iterating through static objects without needing to
copy them into the component's data() object first ?
Answer is Yes, ways to achieve :
By using computed property.
const QUIZ_DATA = ["Question1?", "Question number 2?", "This is number 3!", "And # 4"];
new Vue({
el: '#app',
computed: {
QUIZ_DATA() {
return QUIZ_DATA
}
}
});
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.17/vue.js"></script>
<div id="app">
<div v-for="(question, index) in QUIZ_DATA">
{{ question }}
</div>
</div>
As data is non-reactive we can use created hook.
let QUIZ_DATA = ["Question1?", "Question number 2?", "This is number 3!", "And # 4"];
new Vue({
el: '#app',
created: function () {
this.QUIZ_DATA = QUIZ_DATA;
}
});
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.17/vue.js"></script>
<div id="app">
<div v-for="(question, index) in QUIZ_DATA">
{{ question }}
</div>
</div>

Vue - Unable to use variable in template

I have this code in my VUE file:
<template>
<div class="row">
<div class="col-12">
<section class="list">
<draggable class="drag-area" :list="picsNew" :options="{animation:200, group:'status'}" :element="'article'" #add="onAdd($event, false)" #change="update">
<article class="card" v-for="(photo, index) in picsNew" :key="photo.id" :data-id="photo.id">
<header>
{{ this.galCode }}{{ photo.filename }}
</header>
</article>
</draggable>
</section>
</div>
</div>
</template>
<script>
import draggable from 'vuedraggable'
export default {
components: {
draggable
},
props: ['myPics', 'galId', 'phCode', 'galCode'],
data() {
return {
picsNew: this.myPics,
}
},
methods: {
update() {
this.picsNew.map((photo, index) => {
photo.order = index + 1;
});
let photos = this.picsNew;
console.log(this.galCode)
axios.put('/gallery/' + this.galId + '/updateAll', {
photos: photos
}).then((response) => {
console.log(response.data);
}).catch((error) => {
console.log(error);
})
}
}
}
</script>
in the template, photo.filename works, but this.galCode throws these two errors:
app.js:44152 [Vue warn]: Error in render: "TypeError: Cannot read property 'galCode' of undefined"
found in
---> <DraggablePic> at resources/js/components/draggablepic.vue
app.js:45415 TypeError: Cannot read property 'galCode' of undefined
The variable contains a value, as i am printing it to console. What am I doing wrong?
The problem is that you try to access props variable directly from your template. To solve this, initalize a state variable (under data {}) Within props value as default like you did With picsNeW.
another remark, avoid to use "this" from template, you should access directly datas form its name. Use default value in your props, this is recommended :)
The line {{ this.galCode }}{{ photo.filename }} should be {{ galCode }}{{ photo.filename }}. Your problem is the this you added
First you should define Default value as :
props: {
name: {
type: String,
default: 'default'
},
...
galCode: {
type: Number,
default: 0
},
},
Second be sure you receive desired data, specially in nested object you should check the object defined to prevent receive undefined property error, like use v-if :
<header>
<template v-if="galCode">{{ galCode }}</template> // can use v-else here too print default value
<template v-if="photo.filename"> {{ photo.filename }}</template>
</header>

Vue custom filtering input component

I'am trying to create a component that have 'just' an text input. String typed in this input will be used to filter a list. My problem is that I cannot handle how to share this filter string between my component and the main app that contains the list to filter.
I tried several things and most of the time I get the error :
Avoid mutating a prop directly since the value will be overwritten whenever the parent component re-renders. Instead, use a data or computed property based on the prop's value
So I looked Vuex but I thinks it cannot help in this case because I can have several filter component used in he same page for different list, and I don't want them to be synchronized ^^
Here is what I have:
The filter component
<script type="x/template" id="filterTpl">
<div>
<span class="filter-wrapper">
<input type="search" class="input input-filter" v-model.trim="filter" />
</span>
</div>
</script>
<script>
Vue.component('list-filter', {
props: {
filter: String
}
template: '#filterTpl'
});
</script>
And my main app:
<div id="contacts">
<list-filter :filter="filter"></list-filter>
<ul class="contacts-list managed-list flex">
<li class="contact" v-for="contactGroup in filteredData">
[...]
</li>
</ul>
</div>
<script>
var contactsV = new Vue({
el: '#contacts',
data: {
filter: "",
studyContactsGroups: []
},
computed: {
filteredData: function(){
// Using this.filter to filter the studyContactsGroups data
[...]
return filteredContacts;
}
}
});
</script>
Thanks for any help or tips :)
You can synchronize child value and parent prop either via explicit prop-event connection or more concise v-bind with sync modifier:
new Vue({
el: '#app',
data: {
rawData: ['John', 'Jane', 'Jim', 'Eddy', 'Maggy', 'Trump', 'Che'],
filter: ''
},
components: {
'my-input' : {
// bind prop 'query' to value and
// #input update parent prop 'filter' via event used with '.sync'
template: `<input :value="query" #input="updateFilter">`,
props: ['query'],
methods: {
updateFilter: function(e) {
this.$emit('update:query', e.target.value) // this is described in documentation
}
}
}
},
computed: {
filteredData: function() {
// simple filter function
return this.rawData.filter(el => el.toLowerCase()
.match(this.filter.toLowerCase()))
}
}
});
<script src="https://unpkg.com/vue/dist/vue.js"></script>
<div id="app">
<my-input :query.sync="filter"></my-input>
<hr>
<ul>
<li v-for="line in filteredData">{{ line }}</li>
</ul>
</div>

Vuejs 2.1.10 method passed as prop not a function

I'm very new to Vuejs and JS frameworks in general, so bear with me. I'm trying to call a method that resides in my root component from a child component (2 levels deep) by passing it as a prop, but I get the error:
Uncaught TypeError: this.onChange is not a function
at VueComponent._onChange (category.js:8)
at boundFn (vendor.js?okqp5g:361)
at HTMLInputElement.invoker (vendor.js?okqp5g:2179)
I'm not sure if I'm on the right track by assigning the prop to a method inside the child component, but see what you think:
index.js
var app = new Vue({
el: '#app',
data: function () {
return {
categories: [],
articles: []
}
},
methods: {
onChange: function () {
console.log('first one');
return function () {
console.log('second one');
}
}
},
});
The html:
<div id="app">
<sidebar :onChange=onChange :categories=categories></sidebar>
<varticles :articles=articles></varticles>
</div>
sidebar.js:
Vue.component('sidebar', {
props: ['onChange', 'categories'],
methods: {
_onChange: function () {
this.onChange();
}
},
template: `
<div class="sidebar">
<category v-for="item in categories" :onChange="_onChange" v-bind:category="item"></category>
</div>
`
});
category.js:
Vue.component('category', {
props: ['category', 'onChange'],
methods: {
_onChange: function () {
this.onChange();
}
},
template: `
<div class="category">
<h2>{{ category.name }}</h2>
<ul>
<li v-for="option in category.options">
<input v-on:change="_onChange" v-bind:id="option.tid" type="checkbox" v-model="option.checked">
<label v-bind:for="option.tid">{{ option.name }}</label>
</li>
</ul>
</div>
`
});
There's got to be simpler way to do this!
I'd suggest taking a look at this https://v2.vuejs.org/v2/guide/components.html#camelCase-vs-kebab-case. A simplified version of your code is in this fiddle https://jsfiddle.net/z11fe07p/641/
When writing props in your templates declare them without Capital letters.
A prop declared as onChange in your props is equivalent to on-change in your html.
<sidebar :on-change=onChange :categories=categories></sidebar>
Also I would suggest looking at events and non parent-child communication if you want a link between components that are more than 1 level deep. https://v2.vuejs.org/v2/guide/components.html?#Non-Parent-Child-Communication