How to access to item from HTML Node [Vue.js] - javascript

I have:
Vue app data:
data: function() {
return {
items: [
{id: 1, text: 'one', other_data: {}},
{id: 2, text: 'two', other_data: {}},
{id: 3, text: 'three', other_data: {}}
]
}
}
Template:
<div v-for="item in items" id="my_items">
<span>{{ item.text }}</span>
</div>
And i need to access from one item by external JS code like next:
let item_node = document.getElementById('my_items').children[1]; // get 2nd child node of #my_items
item_node.__vuedata__ // must be a 2nd item from items in Vue data. {id: 2, text: 'two'...
How to do like this?

Vue recommends to use ref over ID and classes for DOM references. We use ref as a replacement for id. Since it is used on top of the v-for directive, all child elements are now referenced as an array of the ref. So every span will now be hello[0] - hello[n] The mounted code outputs the item.text for first child as it uses 0.
I added a click listener, every time you click the element, the value of item is passed to the method. Here you can extract all values and do whatever manipulation you require. Hope this is what you are looking for.
<template>
<div id="app">
<div v-for="item in items" :key="item.id" ref='hello' #click="logItem(item)">
<span>{{ item.text }}</span>
</div>
</div>
</template>
<script>
export default {
name: "App",
data: function() {
return {
items: [
{id: 1, text: 'one', other_data: {}},
{id: 2, text: 'two', other_data: {}},
{id: 3, text: 'three', other_data: {}}
]
}
},
methods: {
logItem: (item) => console.log(item)
},
mounted: function() {
console.log(this.$refs.hello[0].innerText)
}
};
</script>

Related

Vue 3 How to dynamically load data from server and show in a v-for loop

I want to dynamically showing data from from the database in a v-for loop. However, if I try to push the new data into the array, they are displayed correctly in a console.log(), but nothing changes in the template.
I made a simplified representation of this in Codepen:
<template>
<ul v-for="number in numbers">
<li>{{ number.num }} : {{ number.text }}</li>
</ul>
{{ numbers }}
</template>
<script setup>
let numbers = [
{ num: 1, text: "One" },
{ num: 2, text: "Two" },
{ num: 3, text: "Three" }
];
// simulate server
setTimeout(() => {
numbers.push({ num: 4, text: "Four" });
console.log(numbers); // returns the array correct
}, 3000);
</script>
Link to the Snippet: https://codepen.io/Tenarius/pen/QWBLOJZ
Can someone help me?
When using vue, the variables must be reactive to make changes on data. It's also accessible on .value. Read more on this https://vuejs.org/guide/essentials/reactivity-fundamentals.html and accessing the refs https://vuejs.org/guide/essentials/template-refs.html#accessing-the-refs
Here's a sample working code
<template>
<ul v-for="number in numbers">
<li>{{ number.num }} : {{ number.text }}</li>
</ul>
{{ numbers }}
</template>
<script setup>
import { ref } from 'vue'
let numbers = ref([
{ num: 1, text: "One" },
{ num: 2, text: "Two" },
{ num: 3, text: "Three" }
]);
// simulate server
setTimeout(() => {
numbers.value.push({ num: 4, text: "Four" });
console.log(numbers); // returns the array correct
}, 3000);
</script>

I am trying to delete multiple elements from an array through checkbox i am using vue.js but i am unable to figure out how to do it

i have a scenario where I am triggering an event on button click after selecting elements from the grid when the button is clicked I need to remove that element from the current modal/grid
CheckedNames:["a","b","c"],
CheckedNamesId:[1,2,3],
DeletefromArray(){
this.CheckedNames.forEach(element => {
this.deleteItem(this.CheckedNamesId,this.CheckedNamesId.length);
});
},
deleteItem(index,length) {
this.List.splice(index, length)
},
check: function(e,row) {
this.CheckedNamesId.push(row.id)
console.log(this.CheckedNamesId)
},
Now if I select "a","b","c" by the check box, I need to remove it from the array,
If I understood you correctly try something like following snippet:
new Vue({
el: '#demo',
data() {
return {
names: [{id: 1, name: 'a'},{id: 2, name: 'b'}, {id: 3, name: 'c'}, {id: 4, name: 'd'}, {id: 5, name: 'e'}, {id: 6, name: 'f'}, {id: 7, name: 'g'}],
checkedNames: []
}
},
methods: {
check(item) {
if (!this.checkedNames.length || !this.checkedNames.find(f => item.id === f.id)) {
this.checkedNames.push(item)
} else {
this.checkedNames = this.checkedNames.filter(f => item.id !== f.id)
}
},
del() {
this.names = this.names.filter(a => !this.checkedNames.includes(a))
this.checkedNames = []
}
}
})
Vue.config.productionTip = false
Vue.config.devtools = false
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.17/vue.js"></script>
<div id="demo">
<ul>
<li v-for="item in names" :key="item.id">
{{ item.name }}
<input type="checkbox" #click="check(item)" />
</li>
</ul>
<button #click="del">Delete</button>
</div>
You can try like the code below. When looping in List array check if id of the element is present in checkedNameID array by using indexof. If present add that element's index in a array defined outside loop. Then you can slice it away using another loop from array. When you slice use 1 as second parameter it will remove 1 element at a a time in loop.
const arr = []
deleteFromArray(){
this.List.forEach((element,index) => {
if(this.checkedNameID.indexOf(element.id) !== -1){
this.arr.push(index);
}
});
arr.forEach(item => {
this.List.splice(index,1);
}
},

Nested Vue components with counts of direct children and nested children

I am trying to implement nested comments in vue.js and nuxt.js.
Each comment can have one or more children comments.
Each child comment, can again, have one or more children comments.
Unlimited levels of nested comments is possible.
As you can see in the diagram I have attached, I would like each comment to "know" (for the sake of simplicity, to display) the following information:
The depth of the comment (I have this working already). Example, all of the "top-level" comments are at depth=0, all their children are at depth=1, and so on.
The number of direct children
the number of children (including nested children, unlimited levels deep)
I came across this question on StackOverflow but it doesn't quite do the trick. Or maybe I am doing something wrong.
In case you want to take a look at my (very messy) code, here it is. However, I'm willing to start over, so appreciate any pointers on how to pass the data up / down the chain of nested comments (vue components). Some sample code would be great.
components/PostComment.vue:
<template>
<div>
<div class="tw-flex tw-flex-wrap tw-justify-end">
<div :class="indent" class="tw-w-full tw-flex">
<div class="tw-font-bold tw-p-4 tw-border-gray-400 tw-border tw-rounded tw-text-right">
<div class="kb-card-section">
<div class="kb-card-section-content tw-flex tw-flex-wrap tw-items-center tw-text-left">
<div class="tw-flex tw-w-full">
<div class="tw-hidden md:tw-block md:tw-w-2/12 tw-text-right tw-my-auto">
<div class="tw-flex">
<p class="tw-w-full tw-text-xs tw-text-gray-600 tw-text-right">children: {{ numNestedChildComments }}, depth: {{depth}}</p>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
<div class="tw-w-full" v-if="commentData.nested_comments" v-for="nestedComment in commentData.nested_comments">
<post-comment
:commentData="nestedComment"
:depth="depth + 1"
:numChildCommentsOfParent=numNestedChildComments
/>
</div>
</div>
</div>
</template>
<script>
export default {
name: 'post-comment', // necessary for recursive components / nested comments to work
props: {
depth: {
type: Number,
required: true
},
postAuthorData: {
type: Object,
required: true
},
commentAuthorData: {
type: Object,
required: true
},
commentData: {
type: Object,
required: true
},
numChildCommentsOfParent: {
type: Number,
required: true
},
},
data() {
return {
numNestedChildComments: this.numChildCommentsOfParent,
}
},
mounted() {
this.incrementNumParentComments();
},
methods: {
incrementNumParentComments() {
this.numNestedChildComments++;
this.$emit('incrementNumParentComments');
},
},
computed: {
indent() {
switch (this.depth) {
case 0:
return "tw-ml-0 tw-mt-1";
case 1:
return "tw-ml-4 tw-mt-1";
case 2:
return "tw-ml-8 tw-mt-1";
case 3:
default:
return "tw-ml-12 tw-mt-1";
}
},
},
}
</script>
Figured it out with some help from Rodrigo Pedra from the Laracasts community.
Here as a parent component calling the tree roots:
<template>
<div>
<MyTree v-for="item in records" :key="item.id" :item="item" />
</div>
</template>
<script>
import MyTree from './MyTree';
const FIXTURE = [
{
id: 1,
children: [
{
id: 2,
children: [{id: 3}, {id: 4}, {id: 5}],
},
{
id: 6,
children: [
{id: 7},
{id: 8, children: [{id: 9}, {id: 10}]},
],
},
],
},
{
id: 11,
children: [
{id: 12, children: [{id: 13}, {id: 14}, {id: 15}]},
{id: 16, children: [{id: 17}]},
{id: 18},
],
},
];
export default {
components: {MyTree},
data() {
return {
records: FIXTURE,
};
},
};
</script>
And here is the tree component:
<template>
<div>
<div style="border: 1px solid black; padding: 5px;" :style="offset">
id: {{ item.id }}
// depth: {{ depth }}
// direct: {{ direct }}
// children: {{ childrenCount }}
</div>
<template v-if="item.children">
<MyTree
v-for="record in item.children"
:key="record.id"
:item="record"
:depth="depth + 1"
#born="handleBorn()" />
</template>
</div>
</template>
<script>
const COLORS = [
'white',
'lightgray',
'lightblue',
'lightcyan',
'lightskyblue',
'lightpink',
];
export default {
// MUST give a name in recursive components
// https://vuejs.org/v2/guide/components-edge-cases.html#Recursive-Components
name: 'MyTree',
props: {
item: {type: Object, required: true},
depth: {type: Number, default: 0},
},
data() {
return {
childrenCount: 0,
};
},
computed: {
direct() {
if (Array.isArray(this.item.children)) {
return this.item.children.length;
}
return 0;
},
offset() {
return {
'margin-left': (this.depth * 20) + 'px',
'background-color': COLORS[this.depth % COLORS.length],
};
},
},
mounted() {
this.$emit('born');
},
methods: {
handleBorn() {
this.childrenCount++;
this.$emit('born');
},
},
};
</script>
Screenshot:

How to push items into nested array in Vuetify treeview component

I am attempting to setup a Vuetify treeview component to enable the pushing of input into the nested array of existing items. So far, I have been able to set up the component to enable the pushing of input into the nested array of a dynamically added item. However, I want to push new input into the nested array of the existing 'applications' item, instead of creating an entirely new item. How can I achieve this? See my code below. Thanks!
<template>
<v-app data-app>
<v-app-bar app flat>
<v-text-field v-model="newItem" placeholder="add new item"></v-text-field>
<div class="flex-grow-1"></div>
<v-btn color="success" #click="addItem()">
Add Item
</v-btn>
</v-app-bar>
<v-treeview :items="items"></v-treeview>
</v-app>
</template>
<script>
export default {
data: () => ({
newItem: null,
items: [
{
id: 1,
name: 'Applications :',
children: [
{ id: 2, name: 'Calendar : app' },
{ id: 3, name: 'Chrome : app' },
{ id: 4, name: 'Webstorm : app' },
],
},
],
}),
methods: {
addItem () {
this.items.push({
name: 'Applications :',
children: [
{ id: 5, name: this.newItem },
],
})
}
}
}
</script>

Multiple select Vue.js and computed property

I'm using Vue.js 2.0 and the Element UI library.
I want to use a multiple select to attribute some roles to my users.
The list of all roles available is received and assigned to availableRoles. Since it is an array of object and the v-model accepts only an array with value, I need to extract the id of the roles trough the computed property computedRoles.
The current roles of my user are received and assigned to userRoles: [{'id':1, 'name':'Admin'}, {'id':3, 'name':'User'}].
computedRoles is then equals to [1,3]
The preselection of the select is fine but I can't change anything (add or remove option from the select)
What is wrong and how to fix it?
http://jsfiddle.net/3ra1jscx/3/
<div id="app">
<template>
<el-select v-model="computedRoles" multiple placeholder="Select">
<el-option v-for="item in availableRoles" :label="item.name" :value="item.id">
</el-option>
</el-select>
</template>
</div>
var Main = {
data() {
return {
availableRoles: [{
id: 1,
name: 'Admin'
}, {
id: 2,
name: 'Power User'
}, {
id: 3,
name: 'User'
}],
userRoles: [{'id':1, 'name':'Admin'}, {'id':3, 'name':'User'}]
}
},
computed : {
computedRoles () {
return this.userRoles.map(role => role.id)
}
}
}
I agree mostly with #wostex answer, but he doesn't give you the userRoles property back. Essentially you should swap computedRoles and userRoles. userRoles becomes a computed property and computedRoles is a data property. In my update, I changed the name of computedRoles to selectedRoles.
var Main = {
data() {
return {
availableRoles: [{
id: 1,
name: 'Admin'
}, {
id: 2,
name: 'Power User'
}, {
id: 3,
name: 'User'
}],
selectedRoles:[1,2]
}
},
computed : {
userRoles(){
return this.availableRoles.reduce((selected, role) => {
if (this.selectedRoles.includes(role.id))
selected.push(role);
return selected;
}, [])
}
}
}
var Ctor = Vue.extend(Main)
new Ctor().$mount('#app')
And here is the fiddle.
Check the solution: jsfiddle
The caveat here is that computed properties are getters mainly. You can define setter for computed property, but my approach is more vue-like in my opinion.
In short, instead of v-model on computed set v-model for data property.
Full code:
<script src="//unpkg.com/vue/dist/vue.js"></script>
<script src="//unpkg.com/element-ui/lib/index.js"></script>
<div id="app">
<template>
<el-select v-model="ids" multiple placeholder="Select" #change="logit()">
<el-option v-for="item in availableRoles" :label="item.name" :value="item.id">
</el-option>
</el-select>
</template>
</div>
var Main = {
data() {
return {
availableRoles: [{
id: 1,
name: 'Admin'
}, {
id: 2,
name: 'Power User'
}, {
id: 3,
name: 'User'
}],
userRoles: [{'id':1, 'name':'Admin'}, {'id':3, 'name':'User'}],
ids: []
}
},
mounted() {
this.ids = this.userRoles.map(role => role.id);
},
methods: {
logit: function() {
console.log(this.ids);
}
}
}
var Ctor = Vue.extend(Main)
new Ctor().$mount('#app')

Categories

Resources