How to use "v-for", for array in computed object - vue.js

I am creating a website to list all of my projects. I am using Vuex and Firebase to store all my data.
I want each project to have its own "todo-list".
When I create a new project, I create an array witch is my todo-list. In Firebase it looks like this: Screenshot of Firebase
Now I want to display a list of my projects. First I get my projects as a computed prop like this:
<script>
export default {
computed: {
projects () {
return this.$store.getters.loadedProjects
}
},
created () {
this.$store.dispatch('loadProjects')
},
}
</script>
I can loop through projects and display the title for each project.
My question is: how do I loop through the todo-array inside the computed prop?
<template>
<div v-for="project in projects" :key="project.id">
<p>{{ project.title }}</p>
<!-- Looping through todos (array) in an computed prop -->
<div v-for="(todo, index) in todos" :key="todo.id">
<p>{{ project.todos[index].title }} </p>
<p>{{ project.todos[index].completed }} </p>
</div>
</div>
</template>
Tried to use the computed prop inside v-for, but that does not work.
Not sure if I understand the meaning of computed properties. Could I just define projects () inside data () ? and link the getter to the data?
<div v-for="todo in {{project.todos}} :key="todo.id>

If the second loop is inside the first one, you could access it this way:
<template>
<div v-for="project in projects" :key="project.id">
<p>{{ project.title }}</p>
<!-- Looping through todos (array) in an computed prop -->
<div v-for="todo in project.todos" :key="todo.id">
<p>{{ todo.title }} </p>
<p>{{ todo.completed }} </p>
</div>
</div>
</template>
When you create a loop, in this case, project is a single element of the array, so project.todos is the list of todos of that specific project.
https://v2.vuejs.org/v2/guide/list.html#Mapping-an-Array-to-Elements-with-v-for

Related

Vue: can slot data go back to parent to pass down a prop?

I have my parent component using a MyGrid component and slotting in data. Inside MyGrid, I am checking the item type and if it is a certain type, I am giving it a class typeSquare.
In the interest of keeping MyGrid "dumb", is there a way I can check the type and then have MyGrid pass in a prop for the class?
Parent
<MyGrid
:items="items"
columnGap="12"
rowGap="14"
>
<template v-slot="slotProps">
<Cover
v-if="slotProps.typename === 'NewOne'"
:project="slotProps.item.data"
/>
<Cover2 v-else-if="slotProps.typename != 'NewOne'" :project="slotProps.item.data"/>
</template>
</MyGrid>
MyGrid.vue
<template>
<div :class="$style.root">
<div :class="$style.gridContainer">
<template v-for="(item, index) in items">
<div
:key="index"
:class="[{ [$style.gridItem]: true }, { [$style.typeSquare]: item.typename === 'NewOne' }]"
:style="{
padding: `${columnGap}px ${rowGap}px`,
}"
>
<slot :item="item"></slot>
</div>
</template>
</div>
</div>
</template>
If your goal is to only pass items that are of a certain criteria you can filter them out using computed properties. In your script add computed object like so
computed:{
filteredItems(){
return this.items.filter(item=>item.typename === 'NewOne')
}
},
And after in your template you can pass computed property instead of the whole list
<MyGrid
:items="filteredItems"
columnGap="12"
rowGap="14"
>

How to access slot props from the component used inside the slot?

so everything i can find about scoped slots and passing props dont work for my specific situation:
i have following component order:
Home/List/ListItem
now i desided to replace the ListItem with a slot and because i use the List in a other Component too, but in there i need the ListOptionsItem.
in my home component i did this:
<list
class="mapkeyList"
:content="displayList"
:filterbar="true"
#handleSelection="addSelection"
#delete="deleteElement"
#editItem="editItem"
header="Mapkeys"
:key="mapkeyListKey"
>
<list-item>
</list-item>
</list>
in my List component i have this:
<template>
<div>
<h2 v-if="header">{{header}}</h2>
<div class="listContainer" v-if="showedContent.length > 0">
<div v-for=" (item, index) in showedContent" :key="index">
<slot
:item="item"
:index="index"
:dragable="dragableItems"
#auswahl="auswahlHandle"
#deleteElement="deleteElement"
#editItem="editItem"
:dontShowButtons="dontShowButtons"
#dragStart="handleOverDragStart"
:dragItem="dragItem"
#position="$emit('emitPosition',item)"
:deaktivierbar="deaktivierbar"
>
</slot >
</div>
finaly the listItem and the listOptionsItem need to access this props in the slot:
listItem:
<template>
<div class= "flexSpaceBetween" #click="$emit('auswahl',item)">
<div class="textFett">
{{item[0]}}
</div>
<div>
{{item[1]}}
</div>
</div>
i dont want to write all the neccessarry code in the home component because the listOptionsItem does need more informations and more space to write code.
my goal was it to define in the Home component that i want the list to use the listItem component and in the Options component the list should use the listItemOptions component. in the future there could be added new listItem versions.
Any component used inside scoped slot has no implicit access to the slot props. To make them available inside the component, you must pass it down to that component as props explicitly...
<list
class="mapkeyList"
:content="displayList"
:key="mapkeyListKey">
<template v-slot:default="{ item }">
<list-item :item="item">
</list-item>
</template>
</list>
If you have a lot of props/events you want to pass along, the ability of both v-bind and v-on to take an object as an argument is very useful because you can pass all the data and event handlers at the same time:
// List component
<template>
<div>
<h2 v-if="header">{{header}}</h2>
<div class="listContainer" v-if="showedContent.length > 0">
<div v-for=" (item, index) in showedContent" :key="index">
<slot :props="slotProps" :on="slotEventsHandlers"
</slot >
</div>
</div>
</div>
</template>
<script>
export default {
computed: {
slotProps() {
return {
item: this.item,
dragable: this.dragableItems
}
},
slotEventsHandlers() {
return {
deleteElement: this.deleteElement,
dragStart: this.handleOverDragStart
}
}
}
}
</script>
And use it in parent:
<list
class="mapkeyList"
:content="displayList"
:key="mapkeyListKey">
<template v-slot:default="{ props, on }">
<list-item v-bind="props" v-on="on">
</list-item>
</template>
</list>

Display value emitted from Vue component

I created two separated Vue components and I able to emit a message thru a bus.
How can I render/display the message in the component that receives the message.
Example of the Vue component that receives the message:
<template>
<div v-model="cars">
Car model: {{ model }}
<input type="button" #click="display" value="example" />
</div>
</template>
<script>
export default {
data() {
return {
cars: null
}
},
mounted() {
bus.$on('CARS_LOADED', (cars) => {
this.cars = cars;
});
},
methods: {
display()
{
console.log(this.cars);
}
}
}
</script>
I can successfully emit and received the message, however the car model is not updated. I checked the message received and it contains the "model" key with a right value.
I cannot see any error in the Vue console and however if I replace "{{ model }}" by "{{ cars }}" I can see the full message object updated.
I am using Vue 2.x.
Update:
I enclose an example:
https://jsfiddle.net/kvzvxk4f/1/
As you can see in the example I cannot render an specific field from the object, however I can render the object as string.
I think that you are misunderstanding some parts of the vue syntax.
How to access properties of an object:
You just need to write {{ car.model }} to access a property of an object.
How to iterate through an array in a template:
If you want to display all the cars in your template, you should write:
<div v-for="car in cars">
{{ car }}
</div>
As you see, the v-for directive allows you to iterate through an array.
What is v-model?
v-model is used to bind a variable to an input or a component.
<template>
<div>
<input type="text" v-model="foo" />
</div>
</template>
<script>
export default {
data () {
return {
foo: 'bar'
}
}
}
</script>
In that case, the foo property will be bound to the input text.
Last point:
In your case, to make it work, you also need to create a root element for your template, because a template can't have multiple root elements:
<template>
<div>
<div v-for="car in cars">
{{ car }}
</div>
</div>
</div>
I found the answer.
I just have to type property separated by ".". Like for example {{cars.model}}.
<template id="compo2">
<div>
<div>
{{ field.name }}
</div>
<div>
Received: {{ field }}
</div>
</div>
</template>
Example:
https://jsfiddle.net/zuhb7s8q/3/

How get key value in vuejs in v-for directive?

i have this on vue template:
<div v-for="item in items" :key="item.id">
<!-- content -->
</div>
i want get the value of item.id and send via axios.
i dont know how bind de value from template to script section.
You can put a button with a on click handler inside the div:
<div v-for="item in items" :key="item.id">
<button #click="sendItem(item.id)">Send</button>
</div>
And define the handler in methods section:
<script>
export default {
data: ...
methods: {
sendItem: function(itemId) {
// Using axios here
}
}
}
</script>

VueJS: Is It Possible to Automatically Include One Component in Another Component

I would like to automatically include the contents of one component in a named slot of another component. I.e., something like this:
Vue.component('comp-one', {
template: `
<div class="comps>
<div class="comp-two">
<slot name="compTwo"></slot>
</div>
<div class="comp-one">
<slot></slot>
</div>
</div>
`
})
Vue.component('comp-two', {
slot: 'compTwo',
template: `
<div class="sub-comp-two">
<!-- content goes here -->
<slot></slot>
</div>
`
})
The idea is that if someone uses <comp-two>Some content</comp-two>, it will automatically get added to the slot compTwo in comp-one. Is there a way to do this?
NOTE: I made up slot: 'compTwo'. Just trying to illustrate what I'd like to accomplish.