vue v-for object props - javascript

I am trying to pass an array object to a component, but am not getting anything
create.vue
<el-row >
<component1 v-for="product in products" :value="product" :key="product.id"></component1>
</el-row>
//script section
data() {
return {
products: [] //there have (id = 1, name = first), (id = 2, name = second)
}
}
component1.vue
<el-row>
<div>
{{ product.name }}
</div>
</el-row>
export default {
props: ['value'],
watch: {
value: {
hander: function (val) {
console.log(val);
this.product = {
id: val.id,
name: val.name
}
},
deep: true
}
},
data() {
return {
product: {
id: null,
name: null
}
}
},
but watch not worked ( {{product.name}} its null), why? or how fixed this?

You're trying to watch a property that doesn't change, in your example i don't figure out the need of using watch property, but you could achieve that by assigning value to your data property called product in the mounted life cycle hook as follows :
<el-row>
<div>
{{ product.name }}
</div>
</el-row>
export default {
props: ['value'],
watch: {
value: {
handler: function (val) {
console.log(val);
this.product = {
id: val.id,
name: val.name
}
},
deep: true
}
},
data() {
return {
product: {
id: null,
name: null
}
}
},
mounted(){
this.product= {
id: this.value.id,
name: this.value.name
}
}

Related

How to render an specific task by id from an store in Vuex

I am trying to render a specific id when i click on that taskId. Right now when i click on it, it takes me to the route /tasks/details/id# and shows me the specific id in the URL but the details page shows me all the tasks instead of the specific one i want to see. I would really appreciate the time to review this and Thank you in advance.
in VUEX:
import { createStore } from "vuex";
export default createStore({
state: {
tasks: [
{
id: 1,
projectType: "Web Development",
taskName: "Fixing Bug",
projectDescription: "Task Assigned to fix navbar bug",
dateCreated: "12/06/2021",
duration: 1,
status: "Completed",
completeDate: "12/06/2021",
notes: "difficult task, i posponed copuple of times",
},
{
id: 2,
projectType: "Python",
taskName: "Tutorials",
projectDescription: "Looking at python tutorials",
dateCreated: "12/06/2021",
duration: 1,
status: "Completed",
completeDate: "",
notes: "difficult task, i posponed copuple of times",
taskHistory: [],
},
],
},
mutations: {
addTask(state, payload) {
state.tasks.unshift(payload);
},
},
actions: {
addTask(context, data) {
const registerTask = {
id: Math.floor(Math.random() * 1000),
projectType: data.project,
taskName: data.task,
status: data.status,
duration: data.duration,
created: data.created,
completeDate: data.completed,
notes: data.notes,
history: data.history,
projectDescription: data.desc,
};
context.commit("addTask", registerTask);
},
},
getters: {
taskList(state) {
return state.tasks;
},
getByLength(state) {
return state.tasks.length;
},
getId: (state) => (id) => {
return state.tasks.find((task) => task.id === id);
},
},
modules: {},
});
In TASKLIST page:
<template>
<base-card class="wrapper">
<h2>Task Details</h2>
<base-button link :to="'/tasks/add'">Add task</base-button>
</base-card>
<items-list
v-for="task in tasks"
:key="task.id"
:id="task.id"
:project="task.projectType"
:description="task.projectDescription"
:task="task.taskName"
:duration="task.duration"
:created="task.dateCreated"
:complete="task.dateCompleted"
:status="task.status"
>
</items-list>
</template>
<script>
import ItemsList from "../components/layout/ItemsList.vue";
export default {
data() {
return {
};
},
components: { ItemsList },
computed: {
tasks() {
return this.$store.getters.taskList;
},
detailsLink() {
// id# passed from ItemsList to find specific id
return "/tasks/details/" + this.id;
},
},
};
ITEMSLIST Component: (component that pass the props to TaskList)
<template>
<section>
<base-card class="wrapper">
<ul>
<li>
<h3>{{ project }}</h3>
</li>
<li><span>Task Name:</span> {{ task }}</li>
<li><span> Date Created:</span>{{ created }}</li>
<li><span> Status: </span> {{ status }}</li>
<li><span>Duration: </span>{{ duration }} hr</li>
<li><span>Description: </span> {{ description }}</li>
<li><span>Id#</span> {{ id }}</li>
<div class="buttons">
<base-button link :to="'/tasks/details/' + this.id"
>Details</base-button
>
</div>
</ul>
</base-card>
</section>
</template>
<script>
import BaseCard from "../ui/BaseCard.vue";
import BaseButton from "../BaseButton.vue";
export default {
props: [
"id",
"project",
"task",
"created",
"status",
"duration",
"description",
],
components: {
BaseCard,
BaseButton,
},
data() {
return {
};
},
computed: {
tasks() {
return this.$store.getters.taskList;
},
getByLength() {
return this.$store.getters.getByLength;
},
// getId() {
// return this.$store.getters.taskList.find((task) => task.id === this.id);
// },
},
};
</script>
And finally, the TaskDetails Page(Will show details using id passed from a prop from the state.$store from VUEX:
<template>
<base-card class="wrapper">
<h2>Task Details</h2>
</base-card>
<task-items
v-for="task in tasks"
:key="task.id"
:id="task.id"
:project="task.projectType"
:description="task.projectDescription"
:name="task.taskName"
:duration="task.duration"
:created="task.dateCreated"
:complete="task.dateCompleted"
:status="task.status"
:notes="task.notes"
:history="task.history"
></task-items>
<router-view></router-view>
</template>
<script>
import BaseCard from "../components/ui/BaseCard.vue";
import TaskItems from "../components/layout/TaskItems.vue";
export default {
components: { BaseCard, TaskItems },
data() {
return {};
},
computed: {
tasks() {
return this.$store.getters.taskList;
},
getById() {
return this.$store.getters.getId;
},
taskId() {
return this.$store.getters.getId.id;
},
},
};
</script>

How to run a method using v-for in Vue.js?

I want to get the following output for the following data.
・3
・1
and sample data :
export const dummyData = [
{
id: "1",
name: "a",
sub: [
{
id: "1#1",
name: "b",
sub_sub: [
{ id: "1#1#1", name: "b-a" },
{ id: "1#1#2", name: "b-b" },
]
},
{
id: "1#2",
name: "c",
sub_sub: [
{ id: "1#2#1", name: "c-a" },
]
},
]
},
{
id: "2",
name: "d",
sub: [
{
id: "2#1",
name: "e",
sub_sub: [
{ id: "1#2#1", name: "e-a" },
]
}
]
},
]
I want to count how many elements of sub_sub are includes in object "a" and "d".
So, I made the following code.
<template>
<div>
<ul>
<li v-for="item in items" :key="item.i">{{rowSpanCalc(item.id)}}</li>
</ul>
</div>
</template>
<script lang="ts">
import { Component, Vue } from 'nuxt-property-decorator'
import { dummyData } from '~/store/dummy'
#Component({})
export default class extends Vue {
items: any = []
created() {
this.items = dummyData
}
rowSpanCalc(item: any) {
const count = item.sub.reduce(
(total: any, curr: any) => total + curr.sub_sub.length,
0
)
return count;
}
}
</script>
I ran my code and got an error in console like
  
  item.sub.reduce is not a function
Could anyone please advise me how to fix this errors?
Methods in the template are used as events handler not for rendering, try to use that method inside a computed property then use that property for render your items :
#Component({})
export default class extends Vue {
items: any = []
created() {
this.items = dummyData
}
get customItems(){
return this.items.map(item=>({...item,count:this.rowSpanCalc(item.id)}))
}
rowSpanCalc(item: any) {
const count = item.sub.reduce(
(total: any, curr: any) => total + curr.sub_sub.length,
0
)
return count;
}
}
template :
...
<li v-for="item in customItems" :key="item.id">{{item.count}}</li>
...

How to use conditional rendering with Vue.js slots?

How can I use the result of a v-if to render two different components in Vue? If it matters, I'm also trying to do this while rendering a TreeView using v-for. Here's what I tried so far.
TreeView.vue:
<template>
<span>
<ul v-show="isOpen" v-if="isFolder">
<p
v-for="(child, index) in item.children"
v-bind:item="child"
v-bind:key="index"
<span v-if="!child.isFolder">
<slot v-bind:individualBookmark="child"></slot>
</span>
<span v-else>
<div v-bind:class="{bold: isFolder}" v-on:click="toggle" v-on:dblclick="makeFolder" v-on:contextmenu="rightClick">
{{ child.name }}
<span v-if="isFolder">[{{ isOpen ? '-' : '+' }}]</span>
</div>
</span>
</p>
</ul>
</span>
</template>
<script>
export default {
name: "TreeView",
props: {
item: Object
},
components: {
},
data: function() {
return {
isOpen: false
}
},
computed: {
isFolder: function() {
return this.item.children.length > 0;
//return this.item.children && this.item.children.length;
}
},
methods: {
toggle: function() {
if (this.isFolder) {
this.isOpen = !this.isOpen;
}
},
makeFolder: function() {
if (!this.isFolder) {
this.$emit("make-folder", this.item);
this.isOpen = true;
}
},
rightClick: function() {
alert("Right click action")
}
}
}
</script>
App.vue (the Bookmark tag is a custom component I built that does not have the isFolder computed property):
<template>
<div id="app">
<TreeView v-slot:default="slotProps"
v-bind:item="treeData"
v-on:make-folder="makeFolder"
v-on:add-item="addItem">
<Bookmark v-bind="slotProps.individualBookmark"></Bookmark>
</TreeView>
</div>
</template>
<script>
import Bookmark from './components/Bookmark.vue'
import TreeView from './components/TreeView.vue'
import Vue from 'vue'
export default {
name: 'App',
components: {
Bookmark,
TreeView
},
data: function() {
return {
treeData : {
name: "My Tree",
children: [
{ name: "hello" },
{ name: "wat" },
{
name: "child folder 1",
children: [
{
name: "child folder",
children: [{ name: "hello" }, { name: "wat" }]
},
{ name: "hello" },
{ name: "wat" },
{
name: "child folder",
children: [{ name: "hello" }, { name: "wat" }]
}
]
}
]
}
}
},
methods: {
makeFolder: function(item) {
Vue.set(item, "children", []);
this.addItem(item);
},
addItem: function(item) {
item.children.push({
name: "new stuff"
});
}
}
}
</script>
When I run this, for some reason, the v-if="!child.isFolder" in TreeView.vue always evaluates to true so it renders the Bookmark component. Even in the case when the v-if should be evaluating to false like when given the "child folder 1" child object of treeData, it still evaluates to true. I have a feeling the problem is to do with how I'm using <slot></slot> in TreeView.vue but I'm not sure. Why is the v-if always evaluating to true and never false?
The problem may also have to do with how I wrote the isFolder property.
Add a computed property called computedChildren inside the Treeview component which maps the item prop by adding isFolder property :
computed: {
isFolder: function() {
return this.item.children.length > 0;
//return this.item.children && this.item.children.length;
},
computedChildren(){
return this.item.children.map(child=>{
child.isFolder=child.children?child.children.length>0:false //this add the property isFolder
return child
})
}
},
Then loop through it as follows :
<p
v-for="(child, index) in computedChildren"
...
For replacing the slot you could use template element :
<TreeView
v-bind:item="treeData"
v-on:make-folder="makeFolder"
v-on:add-item="addItem">
<template v-slot:default="slotProps">
<Bookmark v-bind="slotProps.individualBookmark"></Bookmark>
</template>
</TreeView>

Data not being passed to Vue component

I have a Vue component receiving an array of 'items' from its parent.
I've sorted them into categories, two 'items' in each category:
computed: {
// sort items into categories
glass: function() {
return this.items.filter(i => i.category === "glass").slice(0, 2);
},
ceramics:
// etc...
I need to place both items in categories.items to then pass them as props to another component:
data() {
return {
categories: [
{ name: "Glass", sort: "glass", items: {} },
{ name: "Ceramics", sort: "ceramics", items: {} },
{ name: "Brass", sort: "brass", items: {} },
{ name: "Books/Comics", sort: "books", items: {} },
{ name: "Collectibles", sort: "collectibles", items: {} },
{ name: "Pictures", sort: "pictures", items: {} },
{ name: "Other", sort: "other", items: {} }
]
};
},
When I use created or mounted nothing is passed through, when I use beforeDestroy or destroy and console.log the results it works fine, but, they're of no use when exiting the page.
The 'items' are from an Axios GET request, could this be why?
GET request from parent component:
methods: {
fetchItems() {
// items request
let uri = "http://localhost:8000/api/items";
this.axios.get(uri).then(response => {
// randomize response
for (let i = response.data.length - 1; i > 0; i--) {
const j = Math.floor(Math.random() * (i + 1));
[response.data[i], response.data[j]] = [
response.data[j],
response.data[i]
];
}
this.items = response.data;
});
}
},
Passing props to child component:
<div
class="items-container"
v-for="category in categories"
:key="category.name"
>
<router-link :to="'category.link'" class="category-names-home-link">
<h2 class="category-names-home">{{ category.name }}</h2>
</router-link>
<router-link
:to="'category.link'"
class="home-view-all"
#mouseover.native="expand"
#mouseout.native="revert"
>View All...</router-link
>
<div class="items">
<!-- Pass to child component as props: -->
<SubItem :item="categories.items" />
<SubItem :item="categories.items" />
</div>
</div>
Don't bother adding the items to the categories, keep them separate
Instead of multiple computeds, use one computed object hash to store all the filtered sets:
computed: {
filtered() {
if (!this.items) return null;
const filtered = {};
this.items.forEach(item => {
if (filtered[item.category]) {
filtered[item.category].push(item);
} else {
filtered[item.category] = [item];
}
});
return filtered;
}
}
Result:
{
'Glass': [ ... ],
'Ceramic': [ ... ]
...
}
In the template:
<div>
<div v-for="category in categories" :key="category.name">
<div class="items" v-for="item in filtered[category.name]">
<SubItem :item="item" />
</div>
</div>
</div>
You can use a v-if in the parent to prevent displaying anything until the data is loaded:
<display v-if="items" :items="items"></display>
Here is a demo

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