I am creating a project using javascript with kendo. In my project there is a requirement to implement hierarchical chart. I research this type of chart in kendo and could not get the exact result. Here is the demo code:
function localDataSource(options) {
var id = options.schema.model.id;
var data = options.data;
var newId = -1;
var created, updated, deleted;
var dataSource = new kendo.data.DataSource($.extend(true, {
transport: {
read: function(e) {
created = {};
updated = {};
deleted = {};
e.success(data || []);
},
update: function(e) {
var item = e.data;
if (!created[item[id]]) {
updated[item[id]] = item;
}
e.success();
},
destroy: function(e) {
var idValue = e.data[id];
if (!created[idValue]) {
deleted[idValue] = e.data;
} else {
delete created[idValue];
}
e.success();
},
create: function(e) {
var item = e.data;
item[id] = newId--;
created[item[id]] = $.extend(true, {}, item);
e.success(item);
}
},
}, options));
dataSource.getChanges = function() {
return {
deleted: toArray(deleted),
created: toArray(created),
updated: toArray(updated)
}
};
return dataSource;
}
function toArray(changes) {
var result = [];
for (var id in changes) {
result.push(changes[id]);
}
return result;
}
function createDiagram() {
var shapesDataSource = localDataSource({
data: [{
"Id": 1,
"JobTitle": "President"
}, {
"Id": 2,
"JobTitle": "VP Finance",
"Color": "#3399cc"
}, {
"Id": 3,
"JobTitle": "VP Customer Relations",
"Color": "#3399cc"
}, {
"Id": 4,
"JobTitle": "VP Human Resources",
"Color": "#3399cc"
}],
schema: {
model: {
id: "Id",
fields: {
Id: {
type: "number",
editable: false
},
JobTitle: {
type: "string"
},
Color: {
type: "string"
}
}
}
}
});
var connectionsDataSource = localDataSource({
data: [{
"Id": 1,
"FromShapeId": 1,
"ToShapeId": 2
}, {
"Id": 2,
"FromShapeId": 1,
"ToShapeId": 3
}, {
"Id": 3,
"FromShapeId": 1,
"ToShapeId": 4
}],
schema: {
model: {
id: "Id",
fields: {
Id: {
type: "number",
editable: false
},
from: {
from: "FromShapeId",
type: "number"
},
to: {
from: "ToShapeId",
type: "number"
}
}
}
}
});
var changesViewModel = kendo.observable({
showChanges: function() {
var diagram = $("#diagram").data("kendoDiagram");
this.set("shapes", diagram.dataSource.getChanges());
this.set("connections", diagram.connectionsDataSource.getChanges());
this.set("visible", true);
},
shapes: {
deleted: [],
created: [],
updated: []
},
connections: {
deleted: [],
created: [],
updated: []
}
});
kendo.bind($("#changes"), changesViewModel);
$("#diagram").kendoDiagram({
dataSource: shapesDataSource,
connectionsDataSource: connectionsDataSource,
layout: {
type: "tree",
subtype: "tipover",
underneathHorizontalOffset: 140
},
shapeDefaults: {
visual: visualTemplate,
content: {
template: "#= dataItem.JobTitle #",
fontSize: 17
}
},
connectionDefaults: {
stroke: {
color: "#586477",
width: 2
}
},
dataBound: onDataBound
});
}
$(document).ready(createDiagram);
function visualTemplate(options) {
var dataviz = kendo.dataviz;
var g = new dataviz.diagram.Group();
var dataItem = options.dataItem;
if (dataItem.JobTitle === "President") {
g.append(new dataviz.diagram.Circle({
radius: 60,
stroke: {
width: 2,
color: dataItem.Color || "#586477"
},
fill: "#e8eff7"
}));
} else {
g.append(new dataviz.diagram.Rectangle({
width: 240,
height: 67,
stroke: {
width: 0
},
fill: "#e8eff7"
}));
g.append(new dataviz.diagram.Rectangle({
width: 8,
height: 67,
fill: dataItem.Color,
stroke: {
width: 0
}
}));
}
return g;
}
function onDataBound(e) {
this.bringIntoView(this.shapes);
}
<html>
<head>
<meta charset="utf-8" />
<title>Kendo UI Snippet</title>
<link rel="stylesheet" href="https://kendo.cdn.telerik.com/2016.1.412/styles/kendo.common.min.css" />
<link rel="stylesheet" href="https://kendo.cdn.telerik.com/2016.1.412/styles/kendo.rtl.min.css" />
<link rel="stylesheet" href="https://kendo.cdn.telerik.com/2016.1.412/styles/kendo.silver.min.css" />
<link rel="stylesheet" href="https://kendo.cdn.telerik.com/2016.1.412/styles/kendo.mobile.all.min.css" />
<script src="https://code.jquery.com/jquery-1.9.1.min.js"></script>
<script src="https://kendo.cdn.telerik.com/2016.1.412/js/kendo.all.min.js"></script>
</head>
<body>
<div id="diagram"></div>
<div id="changes">
<button type="button" class="k-button" data-bind="click:showChanges">Show changes</button>
<div data-bind="visible:visible">
Deleted Shapes:
<div data-bind="source: shapes.deleted" data-template="shapeItemTemplate">
</div>
<hr /> Created Shapes:
<div data-bind="source: shapes.created" data-template="shapeItemTemplate">
</div>
<hr /> Updated Shapes:
<div data-bind="source: shapes.updated" data-template="shapeItemTemplate">
</div>
<hr /> Deleted Connections:
<div data-bind="source: connections.deleted" data-template="connectionItemTemplate">
</div>
<hr /> Created Connections:
<div data-bind="source: connections.created" data-template="connectionItemTemplate">
</div>
<hr /> Updated Connections:
<div data-bind="source: connections.updated" data-template="connectionItemTemplate">
</div>
</div>
</div>
<script type="text/kendo" id="shapeItemTemplate">
<div>
JodTitle: #:JobTitle#
</div>
</script>
<script type="text/kendo" id="connectionItemTemplate">
<div>
#console.log(data)# #:FromShapeId# - #:ToShapeId#
</div>
</script>
</body>
</html>
But i need below type of graph with clickable circle.
Please help me to fix this problem
Related
I have a Kendo MultiColumnDropDown, that has 2 columns in it, one column is checkboxes, the other is just data.
Everytime I check a checkbox then the dropdown closes, I don't want that to happen until I close it myself and can't figure out how to stop the select event or to stop the dropdown from closing until I close it.
$(document).ready(function() {
LoadMCDD(MCDDData());
});
function LoadMCDD(ds) {
let a = ds[0].DropData;
let arr = a.split('|');
let obj = [];
for (let i = 0; i < arr.length; i++) {
obj.push({
DropData: arr[i]
});
}
$('#MCDDForPrompt').empty();
$('#MCDDForPrompt').kendoMultiColumnComboBox({
placeholder: "Select...",
dataTextField: "DropData",
dataValueField: "DropData",
height: 300,
autoClose: false,
columns: [{
template: "<center><input type='checkbox' class='checkbox' /></center>",
width: "30px"
},
{
field: "DropData",
title: "Data",
width: 200
}
],
dataSource: {
data: obj,
},
change: function(e) {
//console.log(e);
},
select: function(e) {
//console.log(e);
}
});
}
function MCDDData() {
let ds = [{
Answer: "",
DefaultAnswer: "",
DropData: "Check 1|Check 2|Check 3|Check 4",
}];
return ds;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<script src="https://kendo.cdn.telerik.com/2018.3.1017/js/kendo.all.min.js"></script>
<link rel="stylesheet" href="https://kendo.cdn.telerik.com/2018.3.1017/styles/kendo.common-bootstrap.min.css" />
<link rel="stylesheet" href="https://kendo.cdn.telerik.com/2018.3.1017/styles/kendo.bootstrap.min.css" />
<div id="MCDDForPrompt" style="width:400px"></div>
Try this:
select: function(e) {
e.sender.element.data('fromSelect', true);
},
close: function(e) {
if (e.sender.element.data('fromSelect')) {
e.preventDefault();
e.sender.element.data('fromSelect', null);
}
}
$(document).ready(function() {
LoadMCDD(MCDDData());
});
function LoadMCDD(ds) {
let a = ds[0].DropData;
let arr = a.split('|');
let obj = [];
for (let i = 0; i < arr.length; i++) {
obj.push({
DropData: arr[i]
});
}
$('#MCDDForPrompt').empty();
$('#MCDDForPrompt').kendoMultiColumnComboBox({
placeholder: "Select...",
dataTextField: "DropData",
dataValueField: "DropData",
height: 300,
autoClose: false,
columns: [{
template: "<center><input type='checkbox' class='checkbox' /></center>",
width: "30px"
},
{
field: "DropData",
title: "Data",
width: 200
}
],
dataSource: {
data: obj,
},
select: function(e) {
e.sender.element.data('fromSelect', true);
},
close: function(e) {
if (e.sender.element.data('fromSelect')) {
e.preventDefault();
e.sender.element.data('fromSelect', null);
}
}
});
}
function MCDDData() {
let ds = [{
Answer: "",
DefaultAnswer: "",
DropData: "Check 1|Check 2|Check 3|Check 4",
}];
return ds;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<script src="https://kendo.cdn.telerik.com/2018.3.1017/js/kendo.all.min.js"></script>
<link rel="stylesheet" href="https://kendo.cdn.telerik.com/2018.3.1017/styles/kendo.common-bootstrap.min.css" />
<link rel="stylesheet" href="https://kendo.cdn.telerik.com/2018.3.1017/styles/kendo.bootstrap.min.css" />
<div id="MCDDForPrompt" style="width:400px"></div>
I am using the vue wrapper for jexcel and am attempting to trigger the undo function from the toolbar computed field. I cannot seem to access the instance of the spreadsheet. it throws a this.undo is undefined error
<template lang="html">
<div class="wrapper-jexcel">
<button class="" #click="getData(jExcelObj)">Data</button>
<button class="" #click="jExcelObj.undo()">Undo</button>
<input
type="button"
value="Add new row"
#click="jExcelObj.insertRow()"
/>
<div id="spreadsheet" ref="spreadsheet"></div>
</div>
</template>
import jexcelStyle from "jexcel/dist/jexcel.css"; // eslint-disable-line no-unused-vars
import jexcel from "jexcel"; // eslint-disable-line no-unused-vars
import db from '#/firebase/init'
import firebase from 'firebase'
export default {
name: "workbook",
data() {
return {
workbookid: this.$route.params.workbookid,
myCars: [],
columns: [
{ type: "text", title: "Car", width: "120px" },
{
type: "dropdown",
title: "Make",
width: "250px",
source: ["Alfa Romeo", "Audi", "BMW", "Honda", "Porshe"]
},
{ type: "calendar", title: "Available", width: "250px" },
{ type: "image", title: "Photo", width: "120px" },
{ type: "checkbox", title: "Stock", width: "80px" },
{
type: "numeric",
title: "Price",
width: "120px",
mask: "$ #.##,00",
decimal: ","
},
{ type: "color", width: "100px", render: "square" }
]
};
},created() {
this.getworkbook()
},
methods: {
onchange(){
console.log('change');
},
insertRowc() {
console.log(this);
// this.spreadsheet.insertRow();
},
undo(){
console.log('test');
jExcelObj.undo();
},
getData(payload) {
console.log(this.myCars);
console.log(payload);
// this.myCars = payload.data
}
},
computed: {
jExcelOptions() {
var self = this;
return {
data: this.myCars,
columns: this.columns,
search: true,
//fullscreen: true,
minDimensions: [20, 40],
defaultColWidth: 100,
allowComments: true,
toolbar: [
{ type:'i', content:'undo', onclick:function() { return jExcelObj.undo(); } },
{ type:'i', content:'redo', onclick:function() { this.redo(); } },
{ type:'i', content:'save', onclick:function () { test.download(); } },
{ type:'select', k:'font-family', v:['Arial','Verdana'] },
{ type:'select', k:'font-size', v:['9px','10px','11px','12px','13px','14px','15px','16px','17px','18px','19px','20px'] },
{ type:'i', content:'format_align_left', k:'text-align', v:'left' },
{ type:'i', content:'format_align_center', k:'text-align', v:'center' },
{ type:'i', content:'format_align_right', k:'text-align', v:'right' },
{ type:'i', content:'format_bold', k:'font-weight', v:'bold' },
{ type:'color', content:'format_color_text', k:'color' },
{ type:'color', content:'format_color_fill', k:'background-color' },
]
};
}
},
mounted: function() {
//console.log(this.jExcelOptions);
//console.log(this.$refs["spreadsheet"]);
const jExcelObj = jexcel(this.$refs["spreadsheet"], this.jExcelOptions);
// Object.assign(this, jExcelObj); // pollutes component instance
Object.assign(this, { jExcelObj }); // tucks all methods under jExcelObj object in component instance
// console.log(this.jExcelObj);
}
};
should i be passing the instance into the computed method? I am struggling to understand how to manage instances of a wrapper plugin and accessing the methods.
You can get the instance using that:
var yourTableInstance = document.getElementById('spreadsheet').jexcel;
yourTableInstance.undo();
yourTableInstance.getData();
mounted: function() {
let spreadsheet = jspreadsheet(this.$el, options);
Object.assign(this, spreadsheet);
}
I am using jsgrid with checkboxes for row selection such as below
$(function() {
$("#jsGrid").jsGrid({
...
headerTemplate: function() {
return $("<input>").attr("type", "checkbox").attr("id", "selectAllCheckbox");
},
itemTemplate : function(_, item) {
return $("<input>").attr("type", "checkbox").attr("class", "singleCheckbox")
.prop("checked", $.inArray(item, selectedItems) > -1)
.on("change", function () {
$(this).is(":checked") ? selectItem(item) : unselectItem(item);
});
},
}
fields :[{
{ name: "unique_id", type: "text", width: 100, visible:false },
{ name: "some_name", type: "text", width: 100},
...
]
});
$("#selectAllCheckbox").click(selectAllCheckBox);
});
The selectAllCheckBox function given below.
var selectAllCheckBox = function(item) {
selectedItems = [];
if(this.checked) { // check select status
$('.singleCheckbox').each(function() {
this.checked = true;
selectItem($(this).parent().next().text());//line 1
});
}else {
$('.singleCheckbox').each(function() {
this.checked = false;
unselectItem(item);
});
selectedItems = [];
}
}
I would like to collect all the selected unique_ids and process it on the server side. But, as unique_id is hidden, the code at line 1 always returns the values of some_name. How can I get the values of unique_id?
In jsGrid docs, I have not found any method regarding or getting hidden column but you can do like below manually implementation to get the selected record/id.
I have taken a custom selections config inside of jsGrid object as array to store the selected row into that.
*I am pushing full object of particular record in selections array inside of jsGrid.
DEMO
$("#jsGrid").jsGrid({
width: "100%",
paging: true,
selections: [],
data: [{
ClientId: 1,
Client: "Aaaa Joseph"
}, {
ClientId: 2,
Client: "Zzzz Brad"
}, {
ClientId: 3,
Client: "Mr Nice Guy"
}],
fields: [{
headerTemplate: function() {
var grid = this._grid;
return `<input class="all" type="checkbox" ${grid.selections.length==grid.data.length?'checked':''} />`;
},
itemTemplate: function(_, item) {
return `<input class="single" value=${item.ClientId} type="checkbox" _isall=${false} ${this._grid.selections.some(ClientId=>ClientId==item.ClientId)?'checked':''} />`
}
}, {
type: "text",
width: 80,
name: 'Client'
}]
});
$('#jsGrid input[type=checkbox]').on('change', function() {
var checkbox = $(this),
grid = checkbox.closest('#jsGrid').data().JSGrid;
if (checkbox.attr('class') == 'all') {
let checked = checkbox.is(":checked");
grid.selections = checked ? grid.data : [];
$.each($('#jsGrid input[class=single]'), (i, el) => $(el).prop('checked', checked))
} else {
if (checkbox.is(":checked")) {
grid.selections.push(grid.data.find(({
ClientId
}) => ClientId == checkbox.val()));
} else {
grid.selections = grid.selections.filter(({
ClientId
}) => ClientId != checkbox.val());
}
checkbox.closest('#jsGrid').find('input.all').prop('checked', grid.selections.length == grid.data.length);
}
});
$('button.getrecord').on('click', function() {
console.clear();
console.log($('#jsGrid').data().JSGrid.selections);
});
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<link type="text/css" rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/jsgrid/1.5.3/jsgrid.min.css" />
<link type="text/css" rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/jsgrid/1.5.3/jsgrid-theme.min.css" />
<script type="text/javascript" src="https://cdnjs.cloudflare.com/ajax/libs/jsgrid/1.5.3/jsgrid.min.js"></script>
<button class="getrecord">Get Selected record</button>
<div id="jsGrid"></div>
This is not answer.. i cant add the code in above comment, that has a related question. so adding it here.. what is wrong with this code. grid not loading. I am using adminLte plugin jsgrid in
https://adminlte.io/themes/v3/pages/tables/jsgrid.html
<script>
$("#jsGrid").jsGrid({
width: "100%",
paging: true,
selections: [],
controller: {
loadData: function() {
var d = $.Deferred();
$.ajax({
type: 'GET',
url: 'https://xkcd.com/info.0.json',
dataType: "json",
success: function (data) {
d.resolve(data);
},
error: function(e) {
alert("error: " + e.responseText);
}
});
console.log("here test");
return d.promise();
}
},
fields: [{
headerTemplate: function() {
var grid = this._grid;
return `<input class="all" type="checkbox" ${grid.selections.length==grid.loadData.length?'checked':''} />`;
},
itemTemplate: function(_, item) {
return `<input class="single" value=${item.num} type="checkbox" _isall=${false} ${this._grid.selections.some(num=>num==item.num)?'checked':''} />`
},
width: 10,
align: "center"
}, { name: "year", type: "textarea", title: "year", width: 50 },
{ name: "month", type: "number", title: "month", width: 50, align: "center" },
{ name: "day", type: "number", title: "day", width: 50 , align: "center"},
{ name: "title", type: "textarea", title: "title", width: 50 , align: "center"}]
});
$('#jsGrid input[type=checkbox]').on('change', function() {
var checkbox = $(this),
grid = checkbox.closest('#jsGrid').data().JSGrid;
if (checkbox.attr('class') == 'all') {
let checked = checkbox.is(":checked");
grid.selections = checked ? grid.data : [];
$.each($('#jsGrid input[class=single]'), (i, el) => $(el).prop('checked', checked))
} else {
if (checkbox.is(":checked")) {
grid.selections.push(grid.data.find(({
feed_id
}) => feed_id == checkbox.val()));
} else {
grid.selections = grid.selections.filter(({
feed_id
}) => feed_id != checkbox.val());
}
checkbox.closest('#jsGrid').find('input.all').prop('checked', grid.selections.length == grid.data.length);
}
});
$('button.getrecord').on('click', function() {
console.clear();
console.log($('#jsGrid').data().JSGrid.selections);
});
</script>
I need to change behavior for a widget dijit/form/Select.
Widget should not allow focus (from mouse or using the keyboard) for options which have property disabled: true.
I would like to know if you can point me out how to achieve this result.
require([
'dijit/form/Select',
'dojo/_base/window',
'dojo/domReady!'
], function(Select, win) {
var select = new Select({
name: 'select2',
options: [{
label: '<i>Swedish Cars</i>',
value: '',
disabled: true
},
{
label: 'Volvo',
value: 'volvo'
},
{
label: 'Saab',
value: 'saab',
selected: true
},
{
label: '<i>German Cars</i>',
value: '',
disabled: true
},
{
label: 'Mercedes',
value: 'mercedes'
},
{
label: 'Audi',
value: 'audi'
}
]
}, 'select');
select.on('change', function(evt) {
console.log('myselect_event');
});
});
<link rel="stylesheet" href="//ajax.googleapis.com/ajax/libs/dojo/1.12.1/dijit/themes/claro/claro.css" />
<script>
window.dojoConfig = {
parseOnLoad: false,
async: true
};
</script>
<script src="//ajax.googleapis.com/ajax/libs/dojo/1.12.1/dojo/dojo.js"></script>
<body class="claro">
<div id="select"></div>
</body>
I was able to solve this issue using a monkey patch.
The solution consist into:
Adding markup in label so we can customize the look.
Adding property disabled:true in item options, so item won't fire onChange.
Disabling focus on group item when mouse and keyboard are used.
I still very interesting to know if another better/cleaner solutions exists.
require([
'dijit/form/Select',
'dojo/_base/window',
'dojo/domReady!'
], function(Select, win) {
var select = new Select({
name: 'select2',
options: [{
label: '<i>Swedish Cars</i>',
value: '',
group: true,
disabled: true
}, {
label: 'Volvo',
value: 'volvo'
}, {
label: 'Saab',
value: 'saab',
selected: true
}, {
label: '<i>German Cars</i>',
value: '',
group: true,
disabled: true
}, {
label: 'Mercedes',
value: 'mercedes'
}, {
label: 'Audi',
value: 'audi'
}]
}, 'select');
select.on('change', function(value) {
alert(value);
});
select.dropDown._onUpArrow = function() {
this.dropDown.focusPrev()
}.bind(select);
select.dropDown._onDownArrow = function() {
this.dropDown.focusNext()
}.bind(select);
select.dropDown.focusNext = function() {
var next = this.dropDown._getNextFocusableChild(this.dropDown.focusedChild, 1);
var nextSuccessive;
var nextFinal;
if (next.option.group) {
var next = this.dropDown._getNextFocusableChild(this.dropDown.focusedChild, 1);
this.dropDown.focusChild(next);
nextSuccessive = this.dropDown._getNextFocusableChild(this.dropDown.focusedChild, 1);
nextFinal = nextSuccessive;
} else {
nextFinal = next;
}
this.dropDown.focusChild(nextFinal);
}.bind(select);
select.dropDown.focusPrev = function() {
var prev = this.dropDown._getNextFocusableChild(this.dropDown.focusedChild, -1);
var prevSuccessive;
var prevFinal;
if (prev.option.group) {
var prev = this.dropDown._getNextFocusableChild(this.dropDown.focusedChild, -1);
this.dropDown.focusChild(prev);
prevSuccessive = this.dropDown._getNextFocusableChild(this.dropDown.focusedChild, -1);
prevFinal = prevSuccessive;
} else {
prevFinal = prev;
}
this.dropDown.focusChild(prevFinal, true);
}.bind(select);
select.dropDown.onItemHover = function( /*MenuItem*/ item) {
if (item.option.group) {
item._set("hovering", false);
console.log('skip hover');
return;
}
if (this.activated) {
this.set("selected", item);
if (item.popup && !item.disabled && !this.hover_timer) {
this.hover_timer = this.defer(function() {
this._openItemPopup(item);
}, this.popupDelay);
}
} else if (this.passivePopupDelay < Infinity) {
if (this.passive_hover_timer) {
this.passive_hover_timer.remove();
}
this.passive_hover_timer = this.defer(function() {
this.onItemClick(item, {
type: "click"
});
}, this.passivePopupDelay);
}
this._hoveredChild = item;
item._set("hovering", true);
}.bind(select.dropDown);
select.dropDown._onItemFocus = function( /*MenuItem*/ item) {
if (item.option.group) {
return;
}
if (this._hoveredChild && this._hoveredChild != item) {
this.onItemUnhover(this._hoveredChild);
}
this.set("selected", item);
}.bind(select.dropDown);
});
<link rel="stylesheet" href="//ajax.googleapis.com/ajax/libs/dojo/1.12.1/dijit/themes/claro/claro.css" />
<script>
window.dojoConfig = {
parseOnLoad: false,
async: true
};
</script>
<script src="//ajax.googleapis.com/ajax/libs/dojo/1.12.1/dojo/dojo.js"></script>
<body class="claro">
<div id="select"></div>
</body>
I just started out working with Vue and I'm trying to visualise a nested list.
The list-items should contain triple-state checkboxes:
When a child item is checked, the parent item's checkbox should become 'indeterminate'. When all child-checkboxes are checked, the parent checkbox should also become checked.
When a parent item checkbox is checked, all child item checkboxes (also the ones nested deeper) should be selected too.
I kind of have a working solution (check out this pen or the code below) but the checkbox-logic is still flawed. For this example, checked boxes are green, indeterminate ones are orange and unchecked ones are red.
I've run out of ideas how to fix it. Could someone shed some light on how to accomplish this in Vue?
'use strict';
Vue.component("book-chapter", Vue.extend({
name: "book-chapter",
props: ["data", "current-depth"],
data: function() {
return {
checked: this.data.checked,
indeterminate: this.data.indeterminate || false
};
},
methods: {
isChecked: function() {
return this.checked && !this.indeterminate;
},
isIndeterminate: function(){
return this.indeterminate;
},
toggleCheckbox: function(eventData) {
if (this.currentDepth > 0){
if (!this.data.children) {
this.checked != this.children
} else {
this.indeterminate = !this.indeterminate;
}
}
if (eventData) {
// fired by nested chapter
this.$emit('checked', eventData);
} else {
// fired by top level chapter
this.checked = !this.checked;
this.$emit('checked', {
data: this.data
});
}
},
isRootObject: function() {
return this.currentDepth === 0;
},
isChild: function() {
return this.currentDepth === 2;
},
isGrandChild: function() {
return this.currentDepth > 2;
}
},
template: `
<div class='book__chapters'>
<div
class='book__chapter'
v-bind:class="{ 'book__chapter--sub': isChild(), 'book__chapter--subsub': isGrandChild() }"
v-show='!isRootObject()'>
<div class='book__chapter__color'></div>
<div
class='book__chapter__content'
v-bind:class="{ 'book__chapter__content--sub': isChild(), 'book__chapter__content--subsub': isGrandChild() }">
<div class='book__chapter__title'>
<span class='book__chapter__title__text'>{{data.title}}</span>
</div>
<div class='book__chapter__checkbox triple-checkbox'>
<div class='indeterminatecheckbox'>
<div
class='icon'
#click.stop="toggleCheckbox()"
v-bind:class="{'icon--checkbox-checked': isChecked(), 'icon--checkbox-unchecked': !isChecked(), 'icon--checkbox-indeterminate': isIndeterminate()}">
</div>
</div>
</div>
</div>
</div>
<book-chapter
ref='chapter'
:current-depth='currentDepth + 1'
v-for='child in data.children'
key='child.id'
#checked='toggleCheckbox(arguments[0])'
:data='child'>
</book-chapter>
</div>
`
}));
Vue.component("book", Vue.extend({
name: "book",
props: ["data"],
template: `
<div class='book'>
<book-chapter
:data='this.data'
:currentDepth='0'>
</book-chapter>
</div>
`
}));
var parent = new Vue({
el: "#container",
data: function() {
return {
book: {}
};
},
mounted: function() {
this.book = {
"title": "Book",
"children": [{
"title": "1 First title",
"children": [{
"title": "1.1 Subtitle"
}, {
"title": "1.2 Subtitle"
}]
}, {
"title": "2 Second title",
"children": [{
"title": "2.1 Subtitle",
"children": [{
"title": "2.1.1 Sub-Sub title"
}, {
"title": "2.1.2 Another sub-sub title"
}]
}]
}]
}
}
});
Update: fixed a bug found by #PhillSlevin. See pen here
Check this pen, is it what you want to achieve?
I think you can use either eventbus or vuex to solve this problem,
if you treated every 's section as a component.
'use strict';
var bus = new Vue();
var book = {
"title": "Book",
"children": [{
"title": "1 First title",
"children": [{
"title": "1.1 Subtitle"
}, {
"title": "1.2 Subtitle"
}]
}, {
"title": "2 Second title",
"children": [{
"title": "2.1 Subtitle",
"children": [{
"title": "2.1.1 Sub-Sub title"
}, {
"title": "2.1.2 Another sub-sub title"
}]
}]
}]
};
Vue.component('book', {
template: `
<div class="book__chapter">
<p :class="'book__title ' + status" #click="clickEvent">{{title}} {{parent}}</p>
<book v-for="child in children" :key="child" :info="child"></book>
</div>
`,
props: ['info'],
data() {
return {
parent: this.info.parent,
title: this.info.title,
children: [],
status: this.info.status,
};
},
created() {
const info = this.info;
if(info.children) {
info.children.forEach(child => {
child.status = "unchecked";
// use title as ID
child.parent = info.title;
});
this.children = info.children;
}
},
mounted() {
const vm = this;
bus.$on('upside', (payload) => {
const targetArr = vm.children.filter((child) => child.title === payload.from);
if (targetArr.length === 1) {
const target = targetArr[0];
target.status = payload.status;
if (vm.children.every(ele => ele.status === 'checked')) {
vm.status = 'checked';
} else if (vm.children.every(ele => ele.status === 'unchecked')) {
vm.status = 'unchecked';
} else {
vm.status = 'indeterminate';
}
bus.$emit('upside', {
from: vm.title,
status: vm.status,
});
}
});
bus.$on('downside', (payload) => {
if (payload.from === this.parent) {
if (payload.status === 'checked') {
vm.status = 'checked';
vm.children.forEach(child => child.status = 'checked');
} else if (payload.status === 'unchecked') {
vm.status = 'unchecked';
vm.children.forEach(child => child.status = 'unchecked')
}
bus.$emit('downside', {
from: vm.title,
status: vm.status,
})
}
});
},
methods: {
clickEvent() {
if (this.status === 'checked') {
this.status = 'unchecked';
this.children.forEach(child => child.status = 'unchecked');
} else {
this.status = 'checked';
this.children.forEach(child => child.status = 'checked');
}
const vm = this;
bus.$emit('upside', {
from: vm.title,
status: vm.status,
});
bus.$emit('downside', {
from: vm.title,
status: vm.status,
});
},
}
});
var parent = new Vue({
el: "#container",
data: function() {
return {
book
};
},
});
.book__title.unchecked::after {
content: '□';
}
.book__title.indeterminate::after {
content: '△';
}
.book__title.checked::after {
content: '■';
}
.book__chapter {
display: block;
position: reletive;
margin-left: 40px;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.2.6/vue.js"></script>
<div id="container">
<book :info="book" :parent="'container'"></book>
</div>