Get Random Nested arrays of objects and v-for - vue.js

I am working on simple vuejs example projects.
Basically, I am trying to display set of data in a loop. But I can't print the random name array from the nested object in vue 2.
It prints as a full array like { "name": "check1.1" } I want to print them separately like Name, Chcek1.1 ... etc
new Vue({
el: '#sample',
data: {
private: {
folders : [{
name : 'folder1',
checks : [
{ "name" : 'check1.1' },
{ "name2" : 'check1.2' },
{ "name4" : 'check1.1' },
{ "abcd" : 'check1.2' },
]
},
{
name : 'folder2',
checks : [
{ "name" : 'check1.1' },
{ "name2" : 'check1.2'},
{ "abcs" : 'check1.1' },
{ "abcd" : 'check1.2' },
]
}
]
}
}
})
<script src="https://cdn.jsdelivr.net/vue/latest/vue.js"></script>
<div id="sample">
<h1>current result: </h1>
<div v-if = "private.folders!=null" v-for="folder in private.folders">
{{folder.name}}
<div v-for="check in folder.checks">
{{check}}
</div>
</div>
</div>
<h1>expected result: </h1>
<ul>
<li>folder1
<ul>
<li>name</li>
<li>check1.1</li>
</ul>
---
<ul>
<li>name2</li>
<li>check1.1</li>
</ul>
---
<ul>
<li>name3</li>
<li>check1.1</li>
</ul>
</li>
<li>folder2
<ul>
<li>name</li>
<li>check1.1</li>
</ul>
---
<ul>
<li>name2</li>
<li>check1.1</li>
</ul>
---
<ul>
<li>name3</li>
<li>check1.1</li>
</ul>
</li>
</ul>
The outer loop works just fine but inside checks[] I can't get them as I expected.

Related

How to check checkbox in Vue.js?

I'm trying to get the total score by checking the checkbox on Vue.js, but I can't subtract from the total score when the same checkbox is clicked.
<template>
<div class="container">
<ul class="list-group" v-for="(data,key) in questionData" :key="key">
<li class="list-item" style="list-style-type:none;">
<h3 class="title">{{data.title}}</h3>
</li>
<ul class="list-group" >
<li class="list-question" v-for="(title,key) in data.questions" :key="key" style="list-style-type:none;"><input type="checkbox" #click="total(data.id, title.id)" :id="title.id" :value="title.question" v-model="selected">{{ title.question}} </li>
</ul>
</ul>
<div class="alert">
Toplam Puan : {{totalScore}}
</div>
</div>
</template>
<script>
import {dataMixin} from "./dataMixin"
export default {
mixins : [dataMixin],
data () {
return{
checked : false,
questionData : [],
selected : [],
totalScore : 0
}
},
methods :{
total(dataId,titleId){
this.questionData.forEach(data => {
data.questions.forEach(element => {
if(dataId == data.id && titleId == element.id){
this.checked = !this.checked;
if (this.checked == true){
this.totalScore += element.score
}
else {
this.totalScore -= element.score
}
}
});
});
}
},
created(){
this.getDataQuestions()
.then(response => {
this.questionData = response.data;
console.log(this.questionData)
})
}
}
</script>
Example JSON data
[
{
"id" : 1,
"title" : "Sözleşme Doğrulaması",
"questions" : [
{
"id" : 1,
"question" : "Geliştirici tüm isterleri karşılayabileceği güvenini vermektedir.",
"score" : 5
},
{
"id" : 2,
"question" : "İsterler tutarlı olup kullanıcı gereksinimlerini kapsamaktadır.",
"score" : 5
}
]
}
]
In my opinion using an array to store selected values is not a good design (using a checked boolean is worse). You have a very nice, functioning object structure for your questions and we can easily work with that.
Your question structure can easily be viewed as this:
[
{
"id" : 1,
"title" : "Group 1",
"questions" : [
{
"id" : 1,
"question" : "A question",
"score" : 5,
"selected" : false
},
{
"id" : 2,
"question" : "Another question",
"score" : 5,
"selected" : false
}
]
},
{ ... }
]
So it's easy to calculate the total score for each question group:
const groupScore = group.questions
.reduce((score, question) => question.selected ? score + question.score : score, 0)
And once you have the score for each question group, you can calculate the total score:
const totalScore = groupScoreArray.reduce((total, groupScore) => total + groupScore, 0)
So, given the data structure above, you can use a computed property to calculate total score.
<template>
<div class="container">
<ul class="list-group" v-for="(data, index) in questionData" :key="index">
<li class="list-item" style="list-style-type:none;">
<h3 class="title">{{data.title}}</h3>
</li>
<ul class="list-group" >
<li class="list-question" v-for="(title, qindex) in data.questions" :key="qindex" style="list-style-type:none;">
<!-- Delete click event, bind data to title.selected -->
<input type="checkbox" :id="title.id" v-model="title.selected">
{{ title.question}}
</li>
</ul>
</ul>
<div class="alert">
Toplam Puan : {{totalScore}}
</div>
</div>
</template>
export default {
data () {
return {
questionData : [{
"id" : 1,
"title" : "Sözleşme Doğrulaması",
"questions" : [{
"id" : 1,
"question" : "Geliştirici tüm isterleri karşılayabileceği güvenini vermektedir.",
"score" : 5
}, {
"id" : 2,
"question" : "İsterler tutarlı olup kullanıcı gereksinimlerini kapsamaktadır.",
"score" : 5
}]
}, {
"id" : 2,
"title" : "Sözleşme Doğrulaması",
"questions" : [{
"id" : 1,
"question" : "Geliştirici tüm isterleri karşılayabileceği güvenini vermektedir.",
"score" : 2
}, {
"id" : 2,
"question" : "İsterler tutarlı olup kullanıcı gereksinimlerini kapsamaktadır.",
"score" : 3
}]
}]
};
},
computed: {
totalScore() {
const scoreMap = this.questionData.map(questionItem =>
questionItem.questions.reduce((sum, q) => {
return q.selected ? sum + q.score : sum
}, 0));
return scoreMap.reduce((sum, score) => sum + score, 0);
}
}
}
This is a working example in my local environment (vue#2.6.10, vue-cli#4.0.0), and here is a codepen: https://codepen.io/jasminexie/pen/pooRaNr

Change component state form another component rendered in a v-for

I have a list of components rendered in a a v-for. I want to set the "show" Boolean property as false in the other components when one of them is set to true:
To simplify I am only adding two components
Main component code:
<template>
<aside class="main-sidebar">
<section class="sidebar">
<ul class="sidebar-menu" data-widget="tree">
<nav-bar-user-profile-item></nav-bar-user-profile-item>
<nav-bar-item></nav-bar-item>
<nav-bar-item></nav-bar-item>
</ul>
</section>
</aside>
</template>
<script>
import NavBarUserProfileItem from '#/components/NavBar/NavBarUserProfileItem';
import NavBarItem from '#/components/NavBar/NavBarItem';
export default {
name: 'NavBar',
components: {
NavBarUserProfileItem,
NavBarItem
},
methods: {
MenuHasBeenToggled(event) {
console.log(event);
}
}
}
NavBarItemComponent
<template>
<li class="treeview2 item" :class="{'menu-open': isOpen, 'active': menu.active}" #click="ToggleState">
<a href="#">
<i class="fa fa-th"></i>
<span>{{ menu.title }}</span>
<span class="pull-right-container">
<i class="fa fa-angle-right pull-right"></i>
</span>
</a>
<collapse-transition>
<ul class="treeview-menu" v-show="isOpen">
<li v-for="submenu in menu.submenus" :key="submenu.title" :class="{'active': (('active' in submenu) ? submenu.active : false)}">
<b-link :href="submenu.link">
<i class="fa fa-circle-thin"></i>
{{ submenu.title }}
</b-link>
</li>
</ul>
</collapse-transition>
</li>
</template>
<script>
export default {
name: 'NavBarItem',
data: function () {
return {
isOpen: false
}
},
computed: {
},
methods: {
ToggleState() {
this.isOpen = !this.isOpen;
this.$emit("toggle-state");
}
},
props: {
menu: {
type: Object,
default: function() {
return {
link: "#",
title: "Main menu",
active: true,
submenus: [
{
link: "#",
title: "Submenu 1",
},
{
link: "#",
title: "Submenu 2",
active: true
},
{
link: "#",
title: "Submenu 3",
},
]
}
}
}
}
}
</script>
<style scoped>
</style>
The goal is to click on one of the and show the menu contents while at the same time collapse the other components.
I thought about using an array of variables and bind it to the "show" prop and with an event listen to it and set every variable to false except the one form the component that sent the event.
How can I know which component sent the event?
Any better idea on how to accomplish this task?
I think, the best way to do it is to add a uniuque identifier property to each NavBarItem and a property for a selected NavBarItem. Then in the main component you can on click on NavBarItem set selected NavBarItem and in NavBarItem make the isOpen computed on the basis if current NavBarItem identifier equals the clicked NavBarItem. Something like this:
<template>
<aside class="main-sidebar">
<section class="sidebar">
<ul class="sidebar-menu" data-widget="tree">
<nav-bar-user-profile-item></nav-bar-user-profile-item>
<nav-bar-item item-id="1" :selected-item-id="selectedNavbarItemId" #click="selectedNavBarItemId = 1"></nav-bar-item>
<nav-bar-item item-id="2" :selected-item-id="selectedNavbarItemId" #click="selectedNavBarItemId = 2"></nav-bar-item>
</ul>
</section>
</aside>
</template>
<script>
import NavBarUserProfileItem from '#/components/NavBar/NavBarUserProfileItem';
import NavBarItem from '#/components/NavBar/NavBarItem';
export default {
name: 'NavBar',
components: {
NavBarUserProfileItem,
NavBarItem
},
data: function(){
return {
selectedNavBarItemId: 0
}
},
methods: {
MenuHasBeenToggled(event) {
console.log(event);
}
}
}
And in NavBarItem
<template>
<li class="treeview2 item" :class="{'menu-open': isOpen, 'active': menu.active}" #click="ToggleState">
<a href="#">
<i class="fa fa-th"></i>
<span>{{ menu.title }}</span>
<span class="pull-right-container">
<i class="fa fa-angle-right pull-right"></i>
</span>
</a>
<collapse-transition>
<ul class="treeview-menu" v-show="isOpen">
<li v-for="submenu in menu.submenus" :key="submenu.title" :class="{'active': (('active' in submenu) ? submenu.active : false)}">
<b-link :href="submenu.link">
<i class="fa fa-circle-thin"></i>
{{ submenu.title }}
</b-link>
</li>
</ul>
</collapse-transition>
</li>
</template>
<script>
export default {
name: 'NavBarItem',
data: function () {
return {
}
},
computed: {
isOpen:function(){
return itemId == selectedItemId;
}
},
methods: {
},
props: {
itemId:Number,
selectedItemId:Number,
menu: {
type: Object,
default: function() {
return {
link: "#",
title: "Main menu",
active: true,
submenus: [
{
link: "#",
title: "Submenu 1",
},
{
link: "#",
title: "Submenu 2",
active: true
},
{
link: "#",
title: "Submenu 3",
},
]
}
}
}
}
}
</script>
<style scoped>
</style>

VueJS: Why v-model does not work with a vuejs filter

Why v-model does not work with a filter getUppercase in <input v-model="filterText | getUppercase">
HTML
<template>
<div class="wrapper">
Check if fruit exist: <input v-model="filterText | getUppercase">
<ul v-for="fruit in filteredFruits">
<li> {{ fruit }} </li>
</ul>
</div>
</template>
VueJS
export default {
name: "filterText",
data() {
return {
msg: "Welcome to Your Vue.js App",
filterText: "",
fruits: ["Apple", "Banana", "Orange", "PineApple", 'Pina Colada']
};
},
computed: {
filteredFruits: function() {
var vm = this;
return vm.fruits.filter(function(item) {
return item.match(vm.filterText)
});
}
},
filters: {
getUppercase: function(obj) {
return this.obj.toUpperCase();
}
}
};
I can see what you are trying to do, however, because of the two way binding when using v-model, it will be better to just apply the getUppercase filter when displaying.
Your template would be something like this:
<template>
<div class="wrapper">
Check if fruit exist: <input v-model="filterText">
<ul v-for="fruit in filteredFruits">
<li> {{ fruit | getUppercase}} </li>
</ul>
</div>
</template>
But if you still wish to transform the filterText model value, you can use a directive. In that case, your VueJS code will be something like :
Vue.directive('getUppercase', {
twoWay: true, // this transformation applies back to the filterText
bind: function () {
var self = this;
self.handler = function () {
self.set(self.el.value.toUpperCase());
}
self.el.addEventListener('input', self.handler);
},
unbind: function () {
this.el.removeEventListener('input', this.handler);
}
});
Now use this directive in your template like :
<input v-model="filterText" v-get-uppercase="filterText">
It will do the same thing as <input v-model="filterText | getUppercase">
Two ways filters are replaced in vue.js please read the docs for more information.It is good to know.
However,as i understood you want to implement a search in array.See it in action here, or take a look below
<div id="app">
Check if fruit exist: <input v-model="filterText">
<ul v-for="fruit in filteredFruits">
<li> {{ fruit }} </li>
</ul>
</div>
new Vue({
el: "#app",
data: {
filterText: "",
fruits: ["Apple", "Banana", "Orange", "PineApple", 'Pina Colada']
},
computed: {
filteredFruits() {
return this.fruits.filter(item => item.toLowerCase().match(this.filterText.toLowerCase()))
}
}
})

Nested arrays of objects and v-for

Well, this problem has me stumped... Having a bit of trouble getting nested for-loop data to show up:
<div v-if = "private.folders!=null" v-for="folder in private.folders">
{{folder.name}}
<div v-for="check in folder.checks">
{{check.name}}
</div>
</div>
And then the data that I'm trying to use looks like this:
folders [Array]
-object [this is a single 'folder']
--name
--checks [array] [each folder has this array]
---[object] [the actual 'check' object]
----[name]
The outer loop works just fine, and returns the data I expect. However, check.name doesn't return anything, and there are no errors in the console. Is it possible to do nested for-loops like this?
I tested you template, it's works.
new Vue({
el: '#sample',
data: {
private: {
folders : [{
name : 'folder1',
checks : [
{ name : 'check1.1' },
{ name : 'check1.2' }
]
},
{
name : 'folder2',
checks : [
{ name : 'check2.1' },
{ name : 'check2.2' }
]
}
]
}
}
})
<script src="https://cdn.jsdelivr.net/vue/latest/vue.js"></script>
<div id="sample">
<div v-if = "private.folders!=null" v-for="folder in private.folders">
{{folder.name}}
<div v-for="check in folder.checks">
{{check.name}}
</div>
</div>
</div>
This is how you might set it up with HTML table:
new Vue({
el: '#sample',
data: {
private: {
folders : [{
name : 'folder1',
checks : [
{ name : 'check1.1' },
{ name : 'check1.2' }
]
},
{
name : 'folder2',
checks : [
{ name : 'check2.1' },
{ name : 'check2.2' }
]
}
]
}
}
})
<script src="https://cdn.jsdelivr.net/vue/latest/vue.js"></script>
<div id="sample">
<table>
<tr>
<th>Folder</th>
<th>Checks</th>
<th>Checks</th>
</tr>
<tr v-if = "private.folders!=null" v-for="folder in private.folders">
<td><b>{{folder.name}}</b></td>
<td v-for="check in folder.checks">
{{check.name}}
</td>
</tr>
</table>
</div>

How to vueJs filtering in on-click

My code :
<ul id="filter"><li>All</li>
<li>In source</li>
<li>Out source</li></ul>
<div v-for="asessesment in listAssesmentList">
<li>In source - Jhon</li>
<li>Out source - Ryan</li>
Can I filter In source or Out source using in vueJs?
Thanks in advance
Are you mean this ??
var vm = new Vue({
el: '#app',
data: {
sources: [{
name: 'In Source',
lists: [
'John', 'Ryan', 'Matt'
]
}, {
name: 'Out Source',
lists: [
'Mimi', 'Mike', 'Edrick'
]
}],
filterText: ''
},
methods: {
filter: function(name) {
this.filterText = name;
}
}
});
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/1.0.17/vue.min.js"></script>
<div id="app">
<ul>
<li>All
</li>
<li>In Source
</li>
<li>Out Source
</li>
</ul>
<ul v-for="source in sources | filterBy filterText in 'name' ">
<li>
{{ source.name }}
<ol>
<li v-for="user in source.lists | orderBy 'user'">{{ user }}</li>
</ol>
</li>
</ul>
</div>
https://jsfiddle.net/quetzalarc/LdcdLqe3/2/