children is not showing - javascript

I have a array with a nested parent-child structure. I need to recursively go through this array and find wanted element. I wrote this function:
component child, this component calls itself, and displays on behalf of each child:
List.vue
<template>
<div>
<div v-for="(item, index) in items" :key="index" >
<div v-if="item.data != null " >
<template v-if="Object.keys(item.data).length > 0">
<template v-for="(item, index) in item.data" >
<v-simple-table :key="index">
<thead>
<tr class="d-flex flex-row">
{{ '░'.repeat(count) }} {{count.length}} {{ item['name'] }}
</tr>
</thead>
<tbody>
</tbody>
</v-simple-table>
</template>
</template>
</div>
<div v-if="item.children != null " >
<template v-if="Object.keys(item.children).length > 0">
<template v-for="(itemD, indexD) in item.children" >
<List :key="indexD" name="List" :items="itemD" :count="count + 1" />
</template>
</template>
</div>
<div v-else></div>
</div>
</div>
</template>
<script>
export default {
name: 'List',
props: {
items: {
type: [Array, Object],
default: [],
},
count: {
type: [Number]
}
},
}
</script>
component parent, this component call component List:
<template>
<div>
<v-row >
<v-col>
<v-card>
<v-card-title>
Estruct
</v-card-title>
<v-row>
<List :items="desserts" :count="count + 1" />
</v-row>
</v-card>
</v-col>
</v-row>
</div>
</template>
<script>
import axios from "axios";
import List from "./List.vue";
export default {
components: { List },
data: (vm) => ({
desserts: [],
count: 0
}),
watch: {
config() {
this.getData();
},
},
methods: {
getData() {
let vm = this;
//
//
axios["get"](process.env.api_url + "/estruct", {
})
.then((response) => {
console.log("response", response.data);
vm.desserts = response.data;
})
.catch((error) => {
console.log(error);
});
},
}
</script>
struct array is like, I am looking at this structure from console.log()
[
{
"data":[{}],
"children":[]
},
{
"data":[
{
"id":64,
"name":"YSVERIS"
}
],
"children":[
{
"data":[
{
"id":85,
"name":"MERCEDES"
}
],
"children":[]
},
{}
]
},
{
"data":[
{
"id":9,
"name":"YUDITH"
}
],
"children":[]
},
{
"data":[
{
"id":10,
"name":"YERNY DEL SOCORRO"
}
],
"children":[]
},
{
"data":[
{
"id":11,
"name":"NIDIA"
}
],
"children":[
{
"data":[
{
"id":21,
"name":"VIVIKEY"
}
],
"children":[]
},
{
"data":[
{
"id":18,
"name":"RODIMIRA "
}
],
"children":[]
}
]
},
{
"data":[],
"children":[]
},
{
"data":[
{
"id":78,
"name":"LUCRECIA"
}
],
"children":[]
},
{
"data":[
{
"id":22,
"name":"DAXI XIOMARA"
}
],
"children":[]
}
]
currently the parent data is being displayed, but not the child data, it should work correct as far as I understand. what do you think??? ....... recursive function is a function that calls itself until it doesn’t. And this technique is called recursion. A recursive function always has a condition to stop calling itself. Otherwise, it will call itself indefinitely. so I don't understand what I'm doing wrong....
I tested in component List.vue, before recursive in <List key="indexD" name="List"....... I added this {{ items }}, so I could see the data object, something like: [ { "data": [ { "id": 85, "name": "MERCEDES"} ] }]
Here is the code in codesandbox code in codesanbox

Related

How to iterate array value by comparing it with another array in Vuejs?

HelloWorld.vue
<template>
<div>
<div v-for="box in boxes" :key="box.sname">
<BaseAccordian>
<template v-slot:title>{{ box.sname }}</template>
<template v-slot:content>
<div v-for="paint in paints" :key="paint.tname" class="line">
<List :content="matchingdata" :sname="box.sname" />
</div>
</template>
</BaseAccordian>
</div>
</div>
</template>
<script>
import BaseAccordian from "./BaseAccordian.vue";
import List from "./List.vue";
export default {
name: "HelloWorld",
components: {
BaseAccordian,
List,
},
data() {
return {
boxes: [
{
sname: "apple",
},
{
sname: "bananna",
},
{
sname: "grapes",
},
{
sname: "choc",
},
],
paints: [
{
tname: "a",
},
{
tname: "b",
},
{
tname: "c",
},
{
tname: "d",
},
{
tname: "e",
},
],
matchingdata: [
{
matchid: "1",
OverallStatus: "ok",
sname: "choc",
},
{
matchid: "2",
OverallStatus: "notok",
sname: "grapes",
},
],
};
},
};
</script>
BaseAccordion.vue
<template>
<div class="wrapper">
<div class="accordion">
<input type="checkbox" #click="toggleItem" />
<h2 class="title">
<slot name="title"></slot>
</h2>
</div>
<div v-show="show" class="content">
<slot name="content"></slot>
</div>
</div>
</template>
<script>
export default {
components: {},
data: function () {
return {
show: false,
};
},
methods: {
toggleItem: function () {
this.show = !this.show;
},
},
};
</script>
List.vue
<template>
<div class="">
<div
v-for="match in matchingData"
:key="match.matchid"
:class="{
green: match.OverallStatus === 'ok',
red: match.OverallStatus === 'notok',
}"
>
{{ match.OverallStatus }}
</div>
</div>
</template>
<script>
export default {
components: {},
props: {
content: {
type: Array,
required: true,
},
sname: {
type: String,
required: true,
},
},
data: function () {
return {};
},
methods: {},
computed: {
matchingData() {
return this.content.filter((a) => {
if (a.sname === this.sname) {
return true;
} else {
return false;
}
});
},
},
};
</script>
<style scoped>
</style>
I three arrays called matchingdata,boxes,paints array based on this three arrays, i am trying to filter the array.(nested v-for)
Now, I want to iterate the matchingdata array by comparing it with sname in boxes array. and Common value between matchingdata and boxes array is ""sname""
I tried above logic, and struck with computed property.
Expected Output:-
In List.vue component , i have
{{ match.OverallStatus }} where that field , i want to show,(from the matchingdata array) when user clicked on checkbox.
Taking the ""sname"" the common value from the matchingdata array and the boxes array
code:- https://codesandbox.io/s/damp-pine-27s2kn?file=/src/components/List.vue
As you're passing the sname property as a string via a prop to your List.vue component, you'll just need to use that string in your filter function.
matchingData() {
return this.content.filter((a) => a.sname === this.sname)
},
I've tried this in your codesandbox link and it is giving some output - but I'm not clear enough on what you're trying to achieve to know if this is the intended outcome.
Just incase you're not aware the 'filter' function returns a new array. It's not going to return a 'true/false' which I feel you may be trying to do.
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/filter

Vue: How to get rid of duplicate content

I have an array of names using v-for I get the names, as you can see I have two v-fors where the content is duplicated in this example my content is small and doesn't look so scary in real life it can be much bigger and all the problem is that the content is repeated, I tried to apply slots but could not cope
Template
<template>
<div>
<div v-for="(item, index) in array" :key="index" class="names">
<div class="show-names">
<p>{{ item.name }}</p>
</div>
<div
v-for="(girlNames, index) in item.girlNames"
:key="index"
class="names"
>
<div class="show-names">
<p>{{ girlNames.name }}</p>
</div>
</div>
</div>
</div>
</template>
Script
<script>
export default {
data() {
return {
array: [
{ name: "Alex" },
{ name: "Jacob" },
{ name: "Robert" },
{
girlNames: [
{
name: "Anna",
},
{
name: "Kiwi",
},
{
name: "Ava",
},
],
},
],
};
},
};
</script>
Yes, this picture shows where the content is repeated
You can also see code example in codesandbox
The only problem I see here is bad data structure. In my opinion it should be an object with two fields, which seperate in your case boys and girls, and in this object should be actual data:
<script>
export default {
data() {
return {
names: {
boys: [
{ name: "Alex" },
{ name: "Jacob" },
{ name: "Robert" },
],
girls: [
{ name: "Anna" },
{ name: "Kiwi" },
{ name: "Ava" },
]
}
},
],
};
},
};
</script>
They your code in template will be like:
<template>
<div>
<div class="names">
<div v-for="(item, index) in name.boys" :key="index" class="show-names">
<p>{{ item.name }}</p>
</div>
<div v-for="(item, index) in name.girls" :key="index" class="show-names">
<p>{{ item.name }}</p>
</div>
</div>
</div>
</template>

How to create with Quasar page that load while scroling

I trying to create component that render more elements while scroling a page. I use Quasar framework for vue. This is code needed to render a single block that contains a information from json file
https://quasar.dev/vue-components/infinite-scroll
Quasar has built in infinite-scroll component, but in these examples, they're adding only empty elements to render. In my case, I need to render elements in order from object. I tried already adding q-infinite-scroll component inside v-for but this in most cases created an infinity loop
Can someone explain to me how can I adapt this example to render more complex objects ?
<div v-for="(p,k) in returnProductsBySelectedType()" class='caption' :key="p.name">
<q-card class="my-card" flat bordered v-if="p.name !== undefined">
<q-item class="" >
<q-item-section >
<div class="text-body1 q-mt-sm q-mb-xs" >{{ p.name }}</div>
</q-item-section>
<q-btn flat class="bg-primary" style="width:50px; height: 50px;" color="white" :icon='includeProduct(p,k) ? "remove" : "add"' #click=' includeProduct(p) ? removeProducts(p) : addProductToList(p)' :key="k"></q-btn>
</q-item>
<q-separator />
<q-item-section class="q-pa-sm bg-grey-3">
<div class="text-subtitle2 flex ">Kalorie: (Kcal) Tłuszcze: (g) Weglowodany: (g) Bialko: (g)</div>
</q-item-section>
</q-card>
</div>
data(){
return{
products:json,
dropdownType:'zboża'
}
},
methods:{
returnProductsBySelectedType(){
let arr=[]
for(const {type,products} of this.products){
if(this.dropdownType === type ) return products
if(this.dropdownType === 'wszystko') arr.push(...products)
}
return arr
},
}
Json file
[
{
"type":"zboża",
"products":[
{
"id":1,
"name":"Bagietki francuskie"
},
{
"id":2,
"name":"Bułeczki do hot-dogów"
},
{
"id":3,
"name":"Bułka tarta"
},
{
"id":4,
"name":"Bułki grahamki"
},
{
"id":5,
"name":"Bułki i rogale maślane"
},
{
"id":6,
"name":"Bułki mieszane, z cebulą"
},
{
"id":7,
"name":"Bułki mleczne"
},
{
"id":8,
"name":"Bułki owsiane"
},
{
"id":9,
"name":"Bułki pszenne zwykłe"
},
{
"id":10,
"name":"Bułki pszenne zwykłe, z serwatką"
},
{
"id":11,
"name":"Bułki sojowe"
},
{
"id":12,
"name":"Bułki szwedki"
},
{
"id":13,
"name":"Bułki wrocławskie"
},
{
"id":14,
"name":"Chałki zdobne"
},
{
"id":15,
"name":"Chleb baltonowski"
},
{
"id":16,
"name":"Chleb beskidzki"
},
{
"id":17,
"name":"Chleb chrupki"
},
]
}
]
Check out the Infinite Scroll component APi: https://next.quasar.dev/vue-components/infinite-scroll#qinfinitescroll-api
You need to listen to the #load event which is fired when the component needs more data. There you can inject more data from the JSON file.

How to get the data from a dynamically generated form in VUE?

Hello Community
I have a parent component that includes a child component, in which a form with different controls is dynamically rendered from a JSON object (obtained through a Get request from Axios).
My objective is to read / loop through all form fields values found in child component, from parent component. How would I do it with Vue JS?
Here is part of the code, if someone has proposals for improvements they will be well received. For example, better structure my code so that it is more organized and clean or use best programming practices with Vue. Thank you.
Parent Component
<template>
<div id="app">
<h1>{{msg}}</h1>
<b-container>
<b-card>
<b-card-title>Formulario Dinámico</b-card-title>
<b-card-body>
<FormControls :fields="fields"></FormControls>
</b-card-body>
<b-card-footer>
<button class="btn btn-primary" #click="send">Enviar</button>
</b-card-footer>
</b-card>
</b-container>
</div>
</template>
<script>
import FormControls from "./FormControls.vue";
import ComponentTest from "./ComponentTest.vue";
import axios from 'axios';
export default {
name: 'app',
components: {
FormControls,
ComponentTest
},
created() {
axios.get('./src/form.json').then(response => this.fields = response.data);
},
data () {
return {
msg: 'Bienvenido',
fields: [] // Array que almacenará el json proveniente de la petición get
}
},
methods: {
send: () => {
alert('Enviar Formulario');
}
}
}
</script>
JSON
[
{
"name": "fechaRegistro",
"label": "Fecha de Registro",
"type": "date",
"placeholder": "Ingresa Fecha"
},
{
"name": "nombreDeUsuario",
"label": "Nombre de Usuario",
"type": "text",
"placeholder": "Ingresa Usuario"
},
{
"name": "passwordUsuario",
"label": "Password",
"type": "password",
"placeholder": "Contraseña"
},
{
"name": "adjuntarArchivo",
"label": "Adjuntar",
"type": "file"
},
{
"name": "roles",
"label": "Roles",
"type": "select",
"sortedByKey": false,
"options": [{
"name": "admin",
"label": "Administrador"
},
{
"name": "user",
"label": "Usuario"
},
{
"name": "guest",
"label": "Invitado"
}
]
},
{
"name": "description",
"label": "Descripción",
"type": "textarea"
},
{
"name": "multiSelect",
"label": "Selección Multiple",
"type": "multiselect",
"options": [{
"name": "op1",
"label": "Opcion1"
},
{
"name": "op2",
"label": "Opcion2"
},
{
"name": "op3",
"label": "Opcion3"
}
]
}
]
Child Component
<template>
<div>
<form>
<ul>
<li v-for="field in fields" :key="field">
<label :for="field.name">{{field.label}}</label>
<input v-if="isInput(field.type)"
:id="field.name" :type="field.type" :placeholder="field.placeholder" >
<select v-else-if="field.type === 'select'" :name="field.name">
<option v-for="opt in field.options" :key="opt" :value="opt.name">
{{opt.label}}
</option>
</select>
<textarea v-else-if="field.type === 'textarea'" :id="field.name" />
<div v-else-if="field.type === 'multiselect'" class="multi-select">
<multiselect v-model="values" tag-placeholder="Agregar etiqueta" :placeholder="field.placeholder" label="label" track-by="name" :options="field.options" :multiple="true" :taggable="true" #tag="agregarEtiqueta"></multiselect>
</div>
</li>
</ul>
</form>
</div>
</template>
<script>
export default {
props: ['fields'],
name: 'FormControls',
data () {
return {
titulo: 'Formulario Dinámico',
// Aqui va lo del MultiSelect
values: [],
options: []
}
},
methods: {
isInput(type) {
return ['text', 'password', 'checkbox', 'file', 'date'].includes(type);
},
// metodo multiselect
agregarEtiqueta (newTag) {
const tag = {
name: newTag,
label: newTag.substring(0, 2) + Math.floor((Math.random() * 10000000))
}
this.options.push(tag)
this.value.push(tag)
},
devolverControl(){
}
}
}
</script>
You shouldn't be meddling with children's data from the parent. That would be a tight coupling. Try the "dumb component" style, where your inputs are only display components & accept props & emit data to the parent. This way you can validate the data in the parent, in any way you want.
A bit more complex solution would be to create the error-handlers inside your "dumb components", and only emit back "error" if they detect errors in themselves. Another style could be to create the validation functions in your fields dataset & just pass down the functions themselves (more of a React than a Vue approach - totally working & valid JS, just not really Vue-style).
In the snippet below I just added value & error to the field items, so they are fully present in the parent component.
Vue.component("InputPassword", {
props: ['field'],
computed: {
value: {
get() {
return this.field.value
},
set(value) {
this.$emit("update:value", {
value
})
}
},
},
template: `
<div>
<label>{{ field.name }}: <input type="password" v-model="value" /></label>
<span
v-if="field.error"
>
THIS FIELD HAS ERRORS
</span>
</div>
`
})
Vue.component("InputText", {
props: ['field'],
computed: {
value: {
get() {
return this.field.value
},
set(value) {
this.$emit("update:value", {
value
})
}
},
},
template: `
<div>
<label>{{ field.name }}: <input type="text" v-model="value" /></label>
<span
v-if="field.error"
>
THIS FIELD HAS ERRORS
</span>
</div>
`
})
new Vue({
el: "#app",
data() {
return {
fields: [{
"name": "nombreDeUsuario",
"label": "Nombre de Usuario",
"type": "text",
"placeholder": "Ingresa Usuario",
"value": null,
"error": null,
},
{
"name": "passwordUsuario",
"label": "Password",
"type": "password",
"placeholder": "Contraseña",
"value": null,
"error": null,
},
]
}
},
computed: {
isFormValid() {
return this.fields.every(({
error
}) => error == false) ? "YES" : "NO"
}
},
methods: {
handleValidateForm() {
this.fields = this.fields.map(field => {
return {
...field,
error: !(!!field.value) // valid if not empty
}
})
},
},
template: `
<div>
<form>
<component
v-for="field in fields"
v-bind:is="'input-' + field.type"
v-bind="{
field
}"
v-on="{
'update:value': ({ value }) => {
field.value = value
},
}"
></component>
</form>
<div
v-for="(field, i) in fields"
:key="'display-field-' + i"
>
field name: {{ field.name }}<br />
field value: {{ field.value }}
</div>
<hr />
<button
#click="handleValidateForm"
>
VALIDATE
</button>
IS FORM VALID? {{ isFormValid }}
</div>
`
})
<script src="https://cdn.jsdelivr.net/npm/vue#2.6.12"></script>
<div id="app"></div>

vuetify treeview every objects was opened, but i did not set "open-all" option

i'm setting up vuetify's treeview component for my project.
i clicked treeview's folder objects, every objects opened.
but i did not set 'open-all' option.
my project is based vue cli 3 and my ESLint configs is 'airbnb'.
TEMPLATE :
<v-card class="mx-auto">
<v-sheet class="pa-1 tree__resources lighten-2">
<v-treeview
:active.sync="active"
:open.sync="open"
:items="items"
item-key="id"
activatable
active-class="primary--text"
open-on-click
return-object>
<template v-slot:prepend="{ item, open }">
<v-icon v-if="item.type == 'directory'">
{{ open ? 'mdi-folder-open' : 'mdi-folder' }}
</v-icon>
<v-icon v-else>
{{ files[item.type] }}
</v-icon>
</template>
</v-treeview>
</v-sheet>
</v-card>
Scripts :
export default {
data: () => ({
active: [],
open: [],
items: ["JSON DATA"],
files: {
html: 'mdi-language-html5',
js: 'mdi-nodejs',
json: 'mdi-json',
md: 'mdi-markdown',
pdf: 'mdi-file-pdf',
png: 'mdi-file-image',
txt: 'mdi-file-document-outline',
jpg: 'mdi-file-image',
jpeg: 'mdi-file-image',
gif: 'mdi-file-image',
arsc: 'mdi-file-document',
RSA: 'mdi-key-variant',
SF: 'mdi-file-document',
MF: 'mdi-file-document',
xml: 'mdi-file-xml',
dex: 'mdi-android',
java: 'mdi-code-braces',
kt: 'mdi-android',
css: 'mdi-language-css3',
android: 'mdi-android',
properties: 'mdi-file-document',
version: 'mdi-file-document',
so: 'mdi-file-document',
provider: 'mdi-file-document',
providers: 'mdi-file-document',
},
filename: '',
filepath: '',
filetype: '',
},
}),
computed: {
selected() {
if( !this.active.length ) return undefined;
const selected = this.active[0];
console.log(selected);
},
},
methods: {
.
.
.
JSON DATA SAMPLE :
[
{
"name":"sources",
"type":"directory",
"children":[
{
"name":"android",
"type":"directory",
"children":[
{
"name":"support",
"type":"directory",
"children":[
{
"name":"fragment",
"type":"directory",
"children":[
{
"name":"R.java",
"id":662,
"type":"java",
"path":"sources/android/support/fragment/R.java"
},
{
"name":"BuildConfig.java",
"id":663,
"type":"java",
"path":"sources/android/support/fragment/BuildConfig.java"
}
]
},
{
"name":"mediacompat",
"type":"directory",
"children":[
{
"name":"R.java",
"id":664,
"type":"java",
"path":"sources/android/support/mediacompat/R.java"
},
{
"name":"BuildConfig.java",
"id":665,
"type":"java",
"path":"sources/android/support/mediacompat/BuildConfig.java"
}
]
}
]
}
]
},
{
"name":"ntouch",
"type":"directory",
"children":[
{
"name":"ntouch_apk_01",
"type":"directory",
"children":[
{
"name":"R.java",
"id":666,
"type":"java",
"path":"sources/ntouch/ntouch_apk_01/R.java"
},
{
"name":"BuildConfig.java",
"id":667,
"type":"java",
"path":"sources/ntouch/ntouch_apk_01/BuildConfig.java"
},
{
"name":"Ntouch_apk_01.java",
"id":668,
"type":"java",
"path":"sources/ntouch/ntouch_apk_01/Ntouch_apk_01.java"
},
{
"name":"SendDateToServer.java",
"id":669,
"type":"java",
"path":"sources/ntouch/ntouch_apk_01/SendDateToServer.java"
}
]
}
]
}
]
},
{
"name":"resources",
"type":"directory",
"children":[
{
"name":"META-INF",
"type":"directory",
"children":[
{
"name":"MANIFEST.MF",
"id":670,
"type":"MF",
"path":"resources/META-INF/MANIFEST.MF"
},
{
"name":"CERT.RSA",
"id":671,
"type":"RSA",
"path":"resources/META-INF/CERT.RSA"
},
{
"name":"CERT.SF",
"id":672,
"type":"SF",
"path":"resources/META-INF/CERT.SF"
}
]
},
{
"name":"classes.dex",
"id":673,
"type":"dex",
"path":"resources/classes.dex"
},
{
"name":"AndroidManifest.xml",
"id":674,
"type":"xml",
"path":"resources/AndroidManifest.xml"
},
]
}
]
when i clicked "resources" folder object, expect the output this:
https://imgur.com/OjU4hjx
but the actual output this: EVERYTHING OPEN IMMEDIATELY
https://imgur.com/7ytQ3mX
Your item-key is id, which is undefined in some node. You should have id for each node includes parent node, something like:
[
{
"name":"sources",
"id": 1,
"type":"directory",
"children":[
{
"name":"android",
"id": 2
"type":"directory",
"children":[]
}
....
}
]
or change item-key to name. I expected name is unique in your structure (which might not be possible)
<v-treeview
:active.sync="active"
:open.sync="open"
:items="items"
item-key="name"
activatable
active-class="primary--text"
open-on-click
return-object>
<template v-slot:prepend="{ item, open }">
<v-icon v-if="item.type == 'directory'">
{{ open ? 'mdi-folder-open' : 'mdi-folder' }}
</v-icon>
<v-icon v-else>
{{ files[item.type] }}
</v-icon>
</template>
</v-treeview>

Categories