Vuetify treeview - select parent without selecting children - treeview

When using the treeview component from Vuetify, I am attempting to be able to select a parent without having it also select all of the descendants (children). I've tried various combinations of selectable, activatable, etc... but can't seem to find the appropriate combination.
Anyone have any pointers for achieving this desired result?

I had the same problem, just add the selection-type="independent" attribute to your treeview component
UPD: Example on CodePen https://codepen.io/anon/pen/LwOJRE
Documentation https://vuetifyjs.com/en/components/treeview#selection-type

I've put together a jsFiddle to achieve this result: https://jsfiddle.net/g50odsmy/
Vue.use(Vuetify);
var vm = new Vue({
el: "#app",
computed: {
treeParents: function() {
let tree = [...this.tree];
// Filter tree with only parents of selections
tree = tree.filter(elem => {
for (let i = 0; i < tree.length; i++) {
// Skip current element
if (tree[i].id === elem.id) continue;
// Check only elements with childrens
if (tree[i].children) {
let item = this.findTreeItem([tree[i]], elem.id);
// If current element is a children of another element, exclude from result
if (item) {
return false;
}
}
}
return true;
});
return tree;
}
},
methods: {
findTreeItem(items, id) {
if (!items) {
return;
}
for (const item of items) {
// Test current object
if (item.id === id) {
return item;
}
// Test children recursively
const child = this.findTreeItem(item.children, id);
if (child) {
return child;
}
}
}
},
data: () => ({
open: ["public"],
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",
xls: "mdi-file-excel"
},
tree: [],
items: [
{
id: ".git",
name: ".git"
},
{
id: "node_modules",
name: "node_modules"
},
{
id: "public",
name: "public",
children: [
{
id: "static",
name: "static",
children: [
{
id: "logo.png",
name: "logo.png",
file: "png"
}
]
},
{
id: "favicon.ico",
name: "favicon.ico",
file: "png"
},
{
id: "index.html",
name: "index.html",
file: "html"
}
]
},
{
id: ".gitignore",
name: ".gitignore",
file: "txt"
},
{
id: "babel.config.js",
name: "babel.config.js",
file: "js"
},
{
id: "package.json",
name: "package.json",
file: "json"
},
{
id: "README.md",
name: "README.md",
file: "md"
},
{
id: "vue.config.js",
name: "vue.config.js",
file: "js"
},
{
id: "yarn.lock",
name: "yarn.lock",
file: "txt"
}
]
}),
});
<link href="https://unpkg.com/vuetify#1.5.6/dist/vuetify.min.css" rel="stylesheet"/>
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.17/vue.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/vuetify/1.5.6/vuetify.min.js"></script>
<link href='https://fonts.googleapis.com/css?family=Roboto:300,400,500,700|Material+Icons' rel="stylesheet" type="text/css">
<div id="app">
<v-app>
<v-layout row wrap>
<v-flex>
<v-treeview
v-model="tree"
:items="items"
activatable
active-class="grey lighten-4 indigo--text"
selected-color="indigo"
open-on-click
selectable
transition
return-object
hoverable
></v-treeview>
</v-flex>
</v-layout>
<v-layout row wrap>
<v-flex xs6 style="border: 2px solid red"><v-chip>Current mode:</v-chip> {{tree}}</v-flex>
<v-flex xs6 style="border: 2px solid green"><v-chip>Only parents:</v-chip> {{treeParents}}</v-flex>
<v-flex xs6 style="border: 2px solid red"># selected: {{tree.length}}</v-flex>
<v-flex xs6 style="border: 2px solid green"># selected: {{ treeParents.length}}</v-flex>
</v-layout>
</v-app>
</div>
In treeParents variable i filter out all the children if a parent is selected.
This solution forces you to save also the original tree to reload the same data later, but i've opened a Feature Request on Vuetify GitHub project page https://github.com/vuetifyjs/vuetify/issues/6759 and hope to have time to explore the component better to see if i can make a pull request.

Related

Implement vue draggable in a table. Laravel

I'm trying to create a survey with the option to order the questions that are displayed in a table when creating the survey.
I'm using vue draggable and the example works but I don't know how to use this with a table and still get the rows to be draggable
Example
<draggable v-model="section" #start="drag=true" #end="drag=false">
<div v-for="section in surveySections" :key="section.id">{{section.title}}</div
</draggable>
This is the table
<el-table
:data="form.question_id"
border>
<draggable v-model="surveyQuestions" #start="drag=true" #end="drag=false">
<el-table-column prop="title" label="Pregunta"></el-table-column>
<el-col :xs="5">
<el-table-column fixed="right" label="Operaciones">
<template slot-scope="scope">
<el-button
#click.native.prevent="deleteRow(scope.$index, form.question_id)"
type="text" size="small">
<span class="icon-create">Eliminar</span>
<i class="el-icon-delete-solid"></i>
</el-button>
</template>
</el-table-column>
</el-col>
</draggable>
</el-table>
How can I get this to work?
I must have 50 reputation to comment!
SO.
you can see elementUI Table组件实现拖拽效果
e.g
npm install sortablejs --save
// Element table must specify row-key . Otherwise, the order will be wrong
import Sortable from 'sortablejs'
<template>
<div style="width:800px">
<el-table :data="tableData"
border
row-key="id"
align="left">
<el-table-column v-for="(item, index) in col"
:key="`col_${index}`"
:prop="dropCol[index].prop"
:label="item.label">
</el-table-column>
</el-table>
<pre style="text-align: left">
{{dropCol}}
</pre>
<hr>
<pre style="text-align: left">
{{tableData}}
</pre>
</div>
</template>
<script>
import Sortable from 'sortablejs'
export default {
data() {
return {
col: [
{
label: '日期',
prop: 'date'
},
{
label: '姓名',
prop: 'name'
},
{
label: '地址',
prop: 'address'
}
],
dropCol: [
{
label: '日期',
prop: 'date'
},
{
label: '姓名',
prop: 'name'
},
{
label: '地址',
prop: 'address'
}
],
tableData: [
{
id: '1',
date: '2016-05-02',
name: '王小虎1',
address: '上海市普陀区金沙江路 100 弄'
},
{
id: '2',
date: '2016-05-04',
name: '王小虎2',
address: '上海市普陀区金沙江路 200 弄'
},
{
id: '3',
date: '2016-05-01',
name: '王小虎3',
address: '上海市普陀区金沙江路 300 弄'
},
{
id: '4',
date: '2016-05-03',
name: '王小虎4',
address: '上海市普陀区金沙江路 400 弄'
}
]
}
},
mounted() {
this.rowDrop()
this.columnDrop()
},
methods: {
//行拖拽
rowDrop() {
const tbody = document.querySelector('.el-table__body-wrapper tbody')
const _this = this
Sortable.create(tbody, {
onEnd({ newIndex, oldIndex }) {
const currRow = _this.tableData.splice(oldIndex, 1)[0]
_this.tableData.splice(newIndex, 0, currRow)
}
})
},
//列拖拽
columnDrop() {
const wrapperTr = document.querySelector('.el-table__header-wrapper tr')
this.sortable = Sortable.create(wrapperTr, {
animation: 180,
delay: 0,
onEnd: evt => {
const oldItem = this.dropCol[evt.oldIndex]
this.dropCol.splice(evt.oldIndex, 1)
this.dropCol.splice(evt.newIndex, 0, oldItem)
}
})
}
}
}
</script>
<style scoped>
</style>
element ui table Sortable.js

Auto select rows in v-data-table

I would like to programmatically checkmark a row in a v-data-table when an external listener notifies me of a particular value.
As an example, here is a Vuetify selectable table: https://vuetifyjs.com/en/components/data-tables#selectable-rows
In the example, If I were passed the value of 'Gingerbread' after the table and its data have already been instantiated, how would I programmatically select that corresponding row?
You can do this by pushing your values in your model like this:
HTML:
<div id="app">
<v-app id="inspire">
<v-btn #click="select">button</v-btn>
<v-data-table
v-model="selected"
:headers="headers"
:items="desserts"
:single-select="singleSelect"
item-key="name"
show-select
class="elevation-1"
>
<template v-slot:top>
<v-switch v-model="singleSelect" label="Single select" class="pa-3"></v-switch>
</template>
</v-data-table>
</v-app>
</div>
VueJS:
new Vue({
el: '#app',
vuetify: new Vuetify(),
methods: {
select: function() {
let result = this.desserts.find((dessert) => {
return dessert.name == 'Gingerbread'
})
this.selected.push(result)
}
},
data () {
return {
singleSelect: false,
selected: [],
headers: [
{ text: 'Dessert (100g serving)', value: 'name' },
{ text: 'Calories', value: 'calories' },
],
desserts: [
{ name: 'Gingerbread', calories: 356 },
{ name: 'Jelly bean', calories: 375 }
],
}
},
})
The v-data-table component has a property called filteredItems which you can use to add items to the selected array
<v-data-table v-model="selected":items="itemsArray" ref="table"></v-data-table>
function selectAll() {
this.$refs.table.filteredItems.map(item => {
if(...some condition...) {
this.selected.push(item)
}
})
}

Binding a v-data-table to a props property in a template

I have a vue component which calls a load method returning a multi-part json object. The template of this vue is made up of several sub-vue components where I assign :data="some_object".
This works in all templates except for the one with a v-data-table in that the v-for process (or the building/rendering of the v-data-table) seems to kick-in before the "data" property is loaded.
With an npm dev server if I make a subtle change to the project which triggers a refresh the data-table then loads the data as I expect.
Tried various events to try and assign a local property to the one passed in via "props[]". Interestingly if I do a dummy v-for to iterate through or simply access the data[...] property the subsequent v-data-table loads. But I need to bind in other rules based on columns in the same row and that doesn't work.
Parent/main vue component:
...
<v-flex xs6 class="my-2">
<ShipViaForm :data="freight"></ShipViaForm>
</v-flex>
<OrderHeaderForm :data="orderheader"></OrderHeaderForm>
<v-flex xs12>
<DetailsForm :data="orderdet" :onSubmit="submit"></DetailsForm>
</v-flex>
...
So in the above the :data property is assigned from the result below for each sub component.
...
methods: {
load(id) {
API.getPickingDetails(id).then((result) => {
this.picking = result.picking;
this.freight = this.picking.freight;
this.orderheader = this.picking.orderheader;
this.orderdet = this.picking.orderdet;
});
},
...
DetailsForm.vue
<template lang="html">
<v-card>
<v-card-title>
<!-- the next div is a dummy one to force the 'data' property to load before v-data-table -->
<div v-show="false">
<div class="hide" v-for='header in headers' v-bind:key='header.product_code'>
{{ data[0][header.value] }}
</div>
</div>
<v-data-table
:headers='headers'
:items='data'
disable-initial-sort
hide-actions
>
<template slot='items' slot-scope='props'>
<td v-for='header in headers' v-bind:key='header.product_code'>
<v-text-field v-if="header.input"
label=""
v-bind:type="header.type"
v-bind:max="props.item[header.max]"
v-model="props.item[header.value]">
</v-text-field>
<span v-else>{{ props.item[header.value] }}</span>
</td>
</template>
</v-data-table>
</v-card-title>
</v-card>
</template>
<script>
import API from '#/lib/API';
export default {
props: ['data'],
data() {
return {
valid: false,
order_id: '',
headers: [
{ text: 'Order Qty', value: 'ord_qty', input: false },
{ text: 'B/O Qty', value: 'bo_qty', input: false },
{ text: 'EDP Code', value: 'product_code', input: false },
{ text: 'Description', value: 'product_desc', input: false },
{ text: 'Location', value: 'location', input: false },
{ text: 'Pick Qty', value: 'pick_qty', input: true, type: 'number', max: ['ord_qty'] },
{ text: 'UM', value: 'unit_measure', input: false },
{ text: 'Net Price', value: 'net_price', input: false },
],
};
},
mounted() {
const { id } = this.$route.params;
this.order_id = id;
},
methods: {
submit() {
if (this.valid) {
API.updateOrder(this.order_id, this.data).then((result) => {
console.log(result);
this.$router.push({
name: 'Orders',
});
});
}
},
clear() {
this.$refs.form.reset();
},
},
};
</script>
Hopefully this will help someone else who can't see the forest for the trees...
When I declared the data() { ... } properties in the parent form I initialised orderdet as {} instead of [].

Set iron-ajax parameter from a shadow root

I would like to create an element which changes the parameters of an iron-ajax element located in another root (its parent)
Parent:
<iron-ajax id="ajaxService"
auto
url="http://..."
method="GET"
handle-as="json"
params="{{ajaxParams}}"
last-response="{{datas}}"></iron-ajax>
<template is="dom-repeat" items="[[datas]]">
<div>[[item]]</div>
</template>
and the child:
<dom-module id="my-pagination">
<template>
<style include="shared-styles">
:host {
display: block;
}
.disabledButton
{
color: #DDDDDD;
}
</style>
<div class="horizontal center layout">
<div class="flex"></div>
<span>
<span id="rangeStart"></span>-<span id="rangeEnd"></span> sur <span>[[data.total_count]]</span>
</span>
<paper-icon-button id="prevPage" class="disabledButton" icon="mdi:chevron-left" on-tap="prevPage" disabled></paper-icon-button>
<paper-icon-button id="nextPage" icon="mdi:chevron-right" on-tap="nextPage"></paper-icon-button>
</div>
Polymer({
is: 'my-pagination',
properties: {
ajaxId: String,
offset: {
type: Number,
value: 0
},
limit: {
type: Number,
value: 6
},
usersAjaxParams: {
type: String,
computed: "processParams(offset, limit)"
}
},
processParams: function (offset, limit) {
return {
offset: offset,
limit: limit,
};
},
computeRange: function (offset, limit, nodeRangeStart, nodeRangeEnd) {
nodeRangeStart.innerText = offset;
nodeRangeEnd.innerText = offset + limit;
},
prevPage: function () {
this.offset = this.offset - this.limit;
this.computeRange(this.offset, this.limit, this.$.rangeStart, this.$.rangeEnd);
};
},
nextPage: function () {
// pretty much same code as above, but for the "next" page
}
},
attached: function () {
this.computeRange(this.offset, this.limit, this.$.rangeStart, this.$.rangeEnd);
}
});
So what I am trying to achieve here is to create a '' element which i could use by providing the ajax node ID as property, and the pagination element would change the ajax parameters whenever I click the "next" or "previous" page button.
And while we're at it, how I can pass datas to the child as weel? (I will need '[[data.total_count]]' later on)
Thanks in advance!!

kendo customised TreeList manual adding childnode unexpected result

List item
I have a page which loads a kendo TreeList by pressing a button. The data is for the moment statically defined in a variable where it stays as a basis for the Kendo TreeList datasource.
I have a datasource definition which I mostly copied from Telerik Website.
I have a treelist with a couple of requirements in terms of CRUD.
level1 - nothing
level2 - add new childnodes only
level3 - edit and delete
Edit should be doubleclick on a level3 item
CRUD command buttons need to be icon-only (no text in the buttons)
I could not achieve this with the buildin CRUD controls unfortunately so I used a Template column where the buttons are placed based on their "Type" field.
Now this has worked but after some changes which I can't undo somehow the add function does not work anymore. It works but new childnode is only visible after a edit ordelete of another node. (as if the change event is not triggered during add). The Add button in the treeList calls a function addProduct where at the end I try to pushCreate directly to the datasource. However the Transport.create is never invoked. It only gets invoked after another Crud action triggers it
Can anybody see what's wrong and couldn't this all be achieve with much easier approach?
Here's the page:
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8" />
<title>Kendo UI Grid - CRUD operations with local data</title>
<style>
html {
font-size: 12px;
font-family: Arial, Helvetica, sans-serif;
}
</style>
<link href="styles/kendo.common.min.css" rel="stylesheet" />
<link href="styles/kendo.default.min.css" rel="stylesheet" />
<script src="Scripts/jquery-2.1.3.min.js"></script>
<!--<script src="Scripts/kendo.all.min.js"></script>-->
<script src="Scripts/kendo.all.js"></script>
</head>
<body>
<style>
.hidden {
display: none;
}
.k-grid tbody .k-button, .k-ie8 .k-grid tbody button.k-button {
min-width: 0px;
padding-left: 10px;
padding-right: 0px;
padding-bottom: 0px;
padding-top: 0px;
margin: 0px;
}
</style>
<div id="buttons">
<br />
<p>
<button name="clear" id="clear" onclick="myclear()">Clear grid</button>
<button name="load" id="load" onclick="loadLocal()">Load from local DB</button>
</p>
<br />
version 1.01<br />
<br />
</div>
<div id="treelist"></div>
<script id="btn-template" type="text/x-kendo-template">
# if (Code == "Product") { #
<a id="btnupdate" class="k-button k-button-icontext k-grid-update column hidden" title="Update product" onclick="update(this)" href="\#"><span class="k-icon k-update"></span></a>
<a id="btndelete" class="k-button k-button-icontext k-grid-delete column" title="Delete product" data-command="destroy" href="\#"><span class="k-icon k-delete"></span></a>
# } else if (Code == "Requirement") { #
<a class="k-button k-button-icontext k-grid-add column" title="Add a product to this requirement" onclick="addProduct(this)" href="\#"><span class="k-icon k-add"></span></a>
# } #
</script>
<script>
var EPDdata // For holding the data of the TreeList
function loadLocal() {
EPDdata = [
{ Id: 1, Description: "Item1", Code: "Category", parentId: null },
{ Id: 2, Description: "Item2", Code: "Requirement", parentId: 1 },
{ Id: 3, Description: "Item3", Code: "Product", parentId: 2 },
{ Id: 4, Description: "Item4", Code: "Requirement", parentId: 1 },
{ Id: 5, Description: "Item5", Code: "Product", parentId: 4 },
{ Id: 6, Description: "Item6", Code: "Product", parentId: 4 },
{ Id: 7, Description: "Item7", Code: "Requirement", parentId: 1 },
{ Id: 8, Description: "Item8", Code: "Requirement", parentId: 1 },
{ Id: 9, Description: "Item9", Code: "Product", parentId: 8 },
{ Id: 10, Description: "Item10", Code: "Product", parentId: 8 }
]
LoadTree();
};
function LoadTree() {
var EPDdataNextID = EPDdata.length + 1;
var LocaldataSource = new kendo.data.TreeListDataSource({
transport: {
read: function (e) {
// on success
e.success(EPDdata);
},
create: function (e) {
// assign an ID to the new item
e.data.Id = EPDdataNextID++;
// save data item to the original datasource
EPDdata.push(e.data);
// on success
e.success(e.data);
},
update: function (e) {
// locate item in original datasource and update it
EPDdata[getIndexById(e.data.Id)] = e.data;
// on success
e.success();
},
destroy: function (e) {
// locate item in original datasource and remove it
EPDdata.splice(getIndexById(e.data.Id), 1);
// on success
e.success();
}
},
error: function (e) {
// handle data operation error
alert("Status: " + e.status + "; Error message: " + e.errorThrown);
},
pageSize: 10,
expanded: true,
batch: false,
schema: {
model: {
id: "Id",
expanded: true,
fields: {
Id: { type: "number", editable: false, nullable: true },
Description: { type: "string", validation: { required: true } },
Code: { type: "string" },
parentId: { type: "number", editable: true, nullable: true }
}
}
}
});
$("#treelist").empty(); // only 1 treelist on the page please
$("#treelist").kendoTreeList({
dataSource: LocaldataSource,
pageable: false,
edit: onEdit,
columns: [
{ field: "Description", title: "Description", width: "400px" },
{ field: "Code", width: "120px" },
{ field: "Id", title: "ID", width: "30px" },
{ field: "parentId", title: "PID", width: "30px" },
{ width: "35px", template: $("#btn-template").html() },
{ command: ["create", "edit", "destroy"] }
],
editable: "inline"
});
var treeList = $("#treelist").on("dblclick", function (e) {
var treeList = $("#treelist").data("kendoTreeList");
var rowindex = e.target.parentNode.rowIndex; // get rowindex
var tr = $(e.target).closest("tr"); // get the current table row (tr)
var dataItem = $("#treelist").data("kendoTreeList").dataItem(tr);
if (dataItem.Code == "Product") {
$("#treelist").find(".edit").addClass("hidden");
$("#treelist").find(".edit").removeClass("edit");
$("#treelist").find(".delete").removeClass("hidden");
$("#treelist").find(".delete").removeClass("delete");
treeList.saveRow(); // first save all other rows
treeList.editRow(tr[0]);
};
}); // double click function
}; // Function CreatetreeList
function onEdit(arg) {
var tr = $(arg.container);//.closest("tr"); // get the current table row (tr)
tr.find("#btndelete").addClass("hidden"); //remove btndelete from commandcolumn
tr.find("#btndelete").addClass("delete"); //put class to select the btn later on
tr.find("#btnupdate").removeClass("hidden"); //make btnupdate visible in commandcolumn
tr.find("#btnupdate").addClass("edit"); //put class to select the btn later on
};
function update(e) { // update the edited row
var tr = $(e).closest("tr"); // get the current table row (tr)
var treeList = $("#treelist").data("kendoTreeList");
treeList.saveRow();
tr.find("#btndelete").removeClass("hidden");
tr.find("#btndelete").removeClass("delete");
tr.find("#btnupdate").addClass("hidden");
tr.find("#btnupdate").removeClass("edit");
};
function addProduct(e) {
var treeList = $("#treelist").data("kendoTreeList");
var dataSource = treeList.dataSource;
var data = dataSource.data;
var tr = $(e).closest("tr"); // get the current table row (tr)
var dataItem = treeList.dataItem(tr);
dataSource.pushCreate({ Id: 15, Description: "New", Code: "Product", parentId: dataItem.Id });
alert("Done");
};
function getIndexById(id) {
var idx,
l = EPDdata.length;
for (var j; j < l; j++) {
if (EPDdata[j].Id == id) {
return j;
}
}
return null;
}
</script>
</body>
</html>
I found the answer !!!
The datasource pagesize is set to 10 and the TreeList was set to paging: false. In all my tests I started with sample data of 10 nodes. All the time I was adding an 11th and 12th record which wasn't showing up until I deleted another node...
Do these things only happen to me?

Resources