How to loop nested data in alpine? dynamic - alpine.js

I have here sample families data which has nested children. My problem is I don't know how many x-for should I write since the nested is dynamic (from Database). Would appreciate any answer.
Here's my sample data in the snippet below:
$families = [
{
id: 1,
name: 'Lupe',
children: [
{
id: 3,
name: 'Bertha',
children: [
{
id: 5,
name: 'Hollywood',
children: [
{
id: 6,
root: 5,
children: [],
},
]
},
],
},
{
id: 9,
name: 'Pinnick',
children: [
{
id: 10,
name: 'Andy',
children: []
},
]
},
]
},
{
id: 2,
name: 'Smitty',
child: [
{
id: 4,
name: 'Vito',
children: []
},
],
}
];
Here is the expected output
Draft implementation:
<tbody>
<template x-for="(family, familyKey) in families">
<template x-for="(parent, parentIndex) in family">
<tr :class="rowClass(scenarioKey, itemIndex)">
<h1 x-text="parent.name"></h1>
</tr>
</template>
</template>
</tbody>

You need a recursive rendering function using x-html directive so make sure that the content is safe, otherwise you can put XSS vulnerabilities in your code. Based on this we can write a recursive list generation function that follows the family graph and renders each level:
<div x-data="familyTree">
<ul>
<template x-for="(level, i) in families">
<li x-html="renderLevel(level, i)"></li>
</template>
</ul>
</div>
<script>
document.addEventListener('alpine:init', () => {
Alpine.data('familyTree', () => ({
families: [
{
id: 1,
name: 'Lupe',
children: [
{
id: 3,
name: 'Bertha',
children: [
{
id: 5,
name: 'Hollywood',
},
],
},
{
id: 9,
name: 'Pinnick',
children: [
{
id: 10,
name: 'Andy',
children: []
},
]
},
]
},
{
id: 2,
name: 'Smitty',
children: [
{
id: 4,
name: 'Vito',
children: []
},
],
}
],
renderLevel(obj, i) {
let has_children = obj.children && obj.children.length
let html = `<span style="padding-left: 10px;"
x-html="'${has_children ? '> ' : '- '}' + level.name"></span>`;
if (obj.children) {
html += `<ul style="padding-left: 10px; padding-bottom: 10px;">
<template x-for='(level,i) in level.children'>
<li x-html="renderLevel(level,i)"></li>
</template>
</ul>`;
}
return html;
},
}))
})
</script>
Output:
> Lupe
> Bertha
- Hollywood
> Pinnick
- Andy
> Smitty
- Vito

Related

Vuetify - Don't trigger input on treeview when selection is synced

On the vuetify treeview component the #input event is triggered when the selected array is initialised, I don't want this behaviour, I need the input event to be triggered only when the user manually selects/deselects an item
<v-treeview
selectable
selected-color="red"
:value="selected"
:items="items"
#input="input"
></v-treeview>
new Vue({
el: '#app',
vuetify: new Vuetify(),
data: () => ({
selected: [],
items: [
{
id: 1,
name: 'Applications :',
children: [
{ id: 2, name: 'Calendar : app' },
{ id: 3, name: 'Chrome : app' },
{ id: 4, name: 'Webstorm : app' },
],
},
{
id: 5,
name: 'Documents :',
children: [
{
id: 6,
name: 'vuetify :',
children: [
{
id: 7,
name: 'src :',
children: [
{ id: 8, name: 'index : ts' },
{ id: 9, name: 'bootstrap : ts' },
],
},
],
},
{
id: 10,
name: 'material2 :',
children: [
{
id: 11,
name: 'src :',
children: [
{ id: 12, name: 'v-btn : ts' },
{ id: 13, name: 'v-card : ts' },
{ id: 14, name: 'v-window : ts' },
],
},
],
},
],
},
{
id: 15,
name: 'Downloads :',
children: [
{ id: 16, name: 'October : pdf' },
{ id: 17, name: 'November : pdf' },
{ id: 18, name: 'Tutorial : html' },
],
},
{
id: 19,
name: 'Videos :',
children: [
{
id: 20,
name: 'Tutorials :',
children: [
{ id: 21, name: 'Basic layouts : mp4' },
{ id: 22, name: 'Advanced techniques : mp4' },
{ id: 23, name: 'All about app : dir' },
],
},
{ id: 24, name: 'Intro : mov' },
{ id: 25, name: 'Conference introduction : avi' },
],
},
],
}),
mounted() {
this.selected = [1]
},
methods: {
input() {
alert("input should not be triggered")// I don't want this to be triggered unless the user explicitly selects/deselects an item
}
}
})
Here is a codepen with the problem - https://codepen.io/amaieras/pen/yLPLyvZ?editors=1011
See that the alert is shown even if I don't trigger any selection, can I avoid this from happening?

Vue.JS Bootstrap-vue Tables - API Laravel - Relationship

I am using b-table (Bootstrap-vue) with API Laravel.
This is the vue code:
<TableCli
:items="items"
:fields="fields"
:rows="rows"
:perPage="perPage">
</TableCli>
.
data: function () {
return {
mode: "save",
item: {},
items: [],
paises: [{}],
checked: 1,
page: 1,
perPage: 10,
fields: [
{ key: "id", label: "Código", sortable: true },
{ key: "name", label: "Name", sortable: true },
{ key: "tva", label: "TVA", sortable: true },
{ key: "country_id", label: "Country", sortable: true},
{ key: "cp", label: "CP", sortable: true },
{ key: "city", label: "City", sortable: true },
{ key: "address", label: "Address", sortable: true },
{
key: "status",
label: "Status",
sortable: true,
formatter: (value) => (value ? "Yes" : "No"),
},
{ key: "actions", label: "Actions" }
],
};
Methods:
methods: {
loadCli() {
const url = `${baseApiUrl}/api/cli`;
axios.get(url).then((res) => {
this.items = res.data;
});
},
loadCountrys() {
const url = `${baseApiUrl}/api/country`;
axios.get(url).then(res => {
this.country = res.data
})
},
My table returns the country id, how do I return the country name?
Another question, how do I add an action button in the Actions column?
The button to edit and delete.
I think this snippet answers your questions. The main idea is using slot for b-table.
new Vue({
el: '#app',
data() {
return {
table_fields: [
{
key: "title",
label: "Title"
},
{
key: "user",
label: "User"
},
{
key: "id",
label: "Detail Button"
}
],
page_contents: [
{
id: "title-id-1",
title: "title 1",
user: {
id: "user-id-1",
name: "name 1",
},
}
]
}
},
methods: {
do_sth(id) {
console.log(id)
}
}
})
<link type="text/css" rel="stylesheet" href="https://unpkg.com/bootstrap#4.5.3/dist/css/bootstrap.min.css" />
<script src="https://unpkg.com/vue#2.6.12/dist/vue.min.js"></script>
<script src="https://unpkg.com/bootstrap-vue#2.21.2/dist/bootstrap-vue.min.js"></script>
<div id="app">
<b-table-lite striped small hover :items="page_contents" :fields="table_fields">
<template #cell(id)="data">
<b-button size="sm" #click='do_sth(data.value)'>Detail Button</b-button>
</template>
<template #cell(user)="data">
<span> {{ data.value.id }} </span>
<span> || </span>
<span> {{ data.value.name }} </span>
</template>
</b-table-lite>
</div>

Vuetify tree data-table

how can I embed a v-tree-view component in v-data-table ?
I have tried so many times but without any result .
with no vuetify v-tree-table forexample , here comes the question , how to integrate the v-tree-view with v-data-table ?
You can define a slot to customize any column. So there you can insert your treeview. An example:
<template>
<v-data-table
:headers="headers"
:items="cosas"
>
<template v-slot:[`item.description`]>
<td>
<template>
<v-treeview :items="treeItems"></v-treeview>
</template>
</td>
</template>
</v-data-table>
</template>
<script>
export default {
name: 'MyComponent',
data () {
return {
headers:[
{ text: 'Name', value: 'name'},
{ text: 'Description', value: 'description'},
],
cosas:[
{ name: 'Item1', description: 'description1'},
{ name: 'Item2', description: 'description2'},
],
treeItems: [
{
id: 1,
name: 'Applications :',
children: [
{ id: 2, name: 'Calendar : app' },
{ id: 3, name: 'Chrome : app' },
{ id: 4, name: 'Webstorm : app' },
],
},
{
id: 5,
name: 'Documents :',
children: [
{
id: 6,
name: 'vuetify :',
children: [
{
id: 7,
name: 'src :',
children: [
{ id: 8, name: 'index : ts' },
{ id: 9, name: 'bootstrap : ts' },
],
},
],
},
{
id: 10,
name: 'material2 :',
children: [
{
id: 11,
name: 'src :',
children: [
{ id: 12, name: 'v-btn : ts' },
{ id: 13, name: 'v-card : ts' },
{ id: 14, name: 'v-window : ts' },
],
},
],
},
],
},
{
id: 15,
name: 'Downloads :',
children: [
{ id: 16, name: 'October : pdf' },
{ id: 17, name: 'November : pdf' },
{ id: 18, name: 'Tutorial : html' },
],
},
{
id: 19,
name: 'Videos :',
children: [
{
id: 20,
name: 'Tutorials :',
children: [
{ id: 21, name: 'Basic layouts : mp4' },
{ id: 22, name: 'Advanced techniques : mp4' },
{ id: 23, name: 'All about app : dir' },
],
},
{ id: 24, name: 'Intro : mov' },
{ id: 25, name: 'Conference introduction : avi' },
],
},
],
}
}
}
</script>
Set your filters in the template to insert it only in those cells that you need it.
i try with many libraries but i feel very hard dificult, because i want to use crud in nodes child, i created solucion: i used a table of type expanded and this expended area insert other table for child. you can read in official page about expand table:
https://vuetifyjs.com/en/components/data-tables/#expandable-rows
my code:
<v-data-table
item-key="_id"
:headers="headers"
:items="items"
:single-expand="singleExpand"
:expanded.sync="expanded"
show-expand
height="670"
fixed-header
sort-by="_id"
class="elevation-1 mt-sm-5 mt-md-5 mt-lg-5 mt-xl-5"
>
<template v-slot:expanded-item="{ headers, item }">
<td :colspan="headers.length" style="background-color:#C0D0D0">
<v-card class="mx-auto my-1" max-width="900" outlined>
<v-card-text>
<div>
<span><strong> Delivery {{item.name_driver}}</strong></span>
</div>
<v-divider></v-divider>
<div>
<v-data-table
:headers="headersChild"
:items="item.children"
:items-per-page="5"
class="elevation-1"
>
</v-data-table>
</div>
</v-card-text>
</v-card>
</td>
</template>
</v-data-table>

How I can add function on data to add images

Hi, I can't add my images on my view by from a array
I use Vue.JS on Laravel project !
I use spell my component and data function:
const routes = [
{
path: '/',
component: Home,
data: () => {
return {
products
}
}
} ];
my array "product":
const products = [
{
id: 1,
description: 'Montre Tissot noire',
price: 12,
img: './resources/img/tissot-noire.jpg'
},
{
id: 2,
description: 'Montre Samor noire',
price: 16,
img: './resources/img/samor.jpg'
}];
on my HomeComponent.vue:
<div v-for="product in products">
<img v-bind:src="product.img" v-bind:alt="product.description">
</div>

Initializing cytoscape

being new to cytoscapeweb 2, i am following one of the provided example in order to learn how to use the API.
i cant make it work and firebug reports the following message:
Component returned failure code: 0x80004005 (NS_ERROR_FAILURE)
var n = this.nodesGroup.getBBox();
in my html document, the cytoscapeweb div is embedded in a jquery tab widget (just to provide context)
somewhere in my.html
<div id="tabs">
<ul>
<li>Interactors Selection</li>
<li>Interactome Builder</li>
</ul>
<div id="tabs-1">
<p>Tab 1 content</p>
</div>
<div id="tabs-2">
<p>Tab 2 content</p>
<div id="cy"></div>
</div>
</div>
and in foo.js
function cytoscapeInit() {
alert ("intializing cytoscape");
// create a mapper for node size
var nodeSizeMapper = {
continuousMapper: {
attr: {
name: "weight",
min: 0,
max: 100
},
mapped: {
min: 15,
max: 30
}
}
};
// call cytoscape web on the `cy` div
$("#cy").cytoscapeweb({
// define the elements in the graph
elements: {
nodes: [
{ data: { id: "a", weight: 43 }, classes: "foo" },
{ data: { id: "b", weight: 2 }, classes: "bar" },
{ data: { id: "c", weight: 88 }, classes: "foo bar" }
],
edges: [
{ data: { id: "ab", source: "a", target: "b", weight: 32 }, classes: "foo" },
{ data: { id: "bc", source: "b", target: "c", weight: 12 }, classes: "bar baz" },
{ data: { id: "ca", source: "c", target: "a", weight: 96 }, classes: "baz foo" },
{ data: { id: "ac", source: "a", target: "c", weight: 65 }, classes: "bar" }
]
},
// define the layout to use
layout: {
name: "preset",
positions: {
"a": { x: 30, y: 30 },
"b": { x: 125, y: 131 },
"c": { x: 200, y: 50 }
},
fit: false,
stop: function(){
cy.reset();
cy.center();
}
},
// define the visual style (like css) of the graph
style: {
selectors: {
"node":{
shape: "ellipse",
fillColor: "#888",
height: nodeSizeMapper,
width: nodeSizeMapper,
labelText: {
passthroughMapper: "id"
}
},
".yay": {
fillColor: "red",
lineColor: "red",
targetArrowColor: "red"
},
"edge": {
lineColor: "#ccc",
targetArrowColor: "#ccc",
width: {
continuousMapper: {
attr: {
name: "weight"
},
mapped: {
min: 2,
max: 5
}
}
},
targetArrowShape: "triangle"
},
"node:selected": {
fillColor: "#333"
},
"edge:selected":{
lineColor: "#666",
targetArrowColor: "#666"
}
}
},
// define the callback for when cytoscape web is ready
ready: function( cy ){
window.cy = cy;
}
});
Did i miss something obvious?
If so, all apologies.
(1) Don't put alerts in your code like that even when you're testing. It can break asynchronous code, like initialising Cytoscape Web or doing an AJAX call. Use console.log() instead.
(2) You're probably hiding the Cytoscape Web div, cy, with the tabs. You shouldn't be using display: none;, because the Cytoscape Web viewport will then be 0x0 px. Try something like position: absolute; left: -9999px; or similiar for hiding. This entails modifying whatever class name jQuery uses for hidden tabs (probably .ui-state-hidden or something similar).
(3) I'll look into making the renderer code more tolerant of hidden Cytoscape Web divs.
I have a better solution to this problem:
Execute jquery tab just after all of the graphs are loaded.
http://cytoscape.github.io/cytoscape.js/#core/events/cy.ready
for(var t = 0, tot=pageList.length; t<tot; t++) //draw graph for all pages
{
var currPage = pageList[t];
getData(currPage);
}
function getData(currPage)
{
//initiate graph, do stuff...
//graph ready
window[currPage + "Container"].ready(function(e)
{
if (currPage.indexOf(lastPage) != -1)//executetabsafterlastgraphisdrawn
{
setTabs();
}
});
}

Resources