Reactive object in 2 arrays - javascript

I have an array of objects (array 1), that can be toggled to another array (array 2). When added the user has the option to type in a text field for each option. The toggling works fine and is reactive on the initial creation. But if I have data that already exists in array 2, the item is no longer reactive.
I have made a quick jsfiddle to demonstrate: Event 1 and 3 are reactive, but event 2 no longer is as it already exists in the newEvents array. Is there anyway to get this connected to the original event?
new Vue({
el: "#app",
data: {
events: [
{ id: 1, text: "Event 1"},
{ id: 2, text: "Event 2"},
{ id: 3, text: "Event 3"}
],
savedEvents: [
{ id: 2, text: "Event 2", notes: 'Event Notes'}
]
},
methods: {
toggleEvent: function(event){
let index = this.savedEvents.findIndex(e => e.id == event.id);
if (index != -1) {
this.savedEvents.splice(index, 1);
} else {
this.savedEvents.push(event);
}
},
inArray: function(id) {
return this.savedEvents.some(obj => obj.id == id);
}
}
})
body {
background: #20262E;
padding: 20px;
font-family: Helvetica;
}
#app {
background: #fff;
border-radius: 4px;
padding: 20px;
transition: all 0.2s;
}
li {
margin: 8px 0;
}
h2 {
font-weight: bold;
margin-bottom: 15px;
}
.btn {
display: inline-block;
padding: 5px;
border: 1px solid #666;
border-radius: 3px;
margin-bottom: 5px;
cursor: pointer;
}
input[type=text]{
padding: 5px;
}
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.17/vue.js"></script>
<div id="app">
<h2>Events:</h2>
<ol>
<li v-for="event in events">
<span class="btn" #click="toggleEvent(event)">
{{ event.text }}
</span>
<input type="text" placeholder="Type your note here..." v-model="event.notes" v-if="inArray(event.id)">
</li>
</ol>
<h2>
Saved Events:
</h2>
<ul>
<li v-for="event in savedEvents">
<strong>{{ event.text }}</strong> {{ event.notes }}
</li>
</ul>
</div>

The problem here is nothing to do with reactivity.
When you add an event to newEvents by clicking the button it's using the same object that's in events. As there's only one object for each event everything work fine.
In the case of Event 2 you're starting with two separate objects representing the same event, one in events and the other in newEvents. Changes to one will not change the other.
It's difficult to say what the appropriate solution is here without knowing your motivation for choosing these data structures but the example below ensures that both arrays contain the same object for Event 2.
The only thing I've changed from your original code is the data function.
new Vue({
el: "#app",
data () {
const data = {
events: [
{ id: 1, text: "Event 1"},
{ id: 2, text: "Event 2", notes: 'Event Notes'},
{ id: 3, text: "Event 3"}
],
newEvents: []
}
data.newEvents.push(data.events[1])
return data
},
methods: {
toggleEvent: function(event){
let index = this.newEvents.findIndex(e => e.id == event.id);
if (index != -1) {
this.newEvents.splice(index, 1);
} else {
this.newEvents.push(event);
}
},
inArray: function(id) {
return this.newEvents.some(obj => obj.id == id);
}
}
})
body {
background: #20262E;
padding: 20px;
font-family: Helvetica;
}
#app {
background: #fff;
border-radius: 4px;
padding: 20px;
transition: all 0.2s;
}
li {
margin: 8px 0;
}
h2 {
font-weight: bold;
margin-bottom: 15px;
}
.btn {
display: inline-block;
padding: 5px;
border: 1px solid #666;
border-radius: 3px;
margin-bottom: 5px;
cursor: pointer;
}
input[type=text]{
padding: 5px;
}
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.17/vue.js"></script>
<div id="app">
<h2>Events:</h2>
<ol>
<li v-for="event in events">
<span class="btn" #click="toggleEvent(event)">
{{ event.text }}
</span>
<input type="text" placeholder="Type your note here..." v-model="event.notes" v-if="inArray(event.id)">
</li>
</ol>
<h2>
New Events:
</h2>
<ul>
<li v-for="event in newEvents">
<strong>{{ event.text }}</strong> {{ event.notes }}
</li>
</ul>
</div>
There are various ways you could represent this data other than by using two lists of the same objects. You might use a boolean flag within the objects. Or you could use a separate object to hold the notes, keyed by event id. It's difficult to know what would work best for your scenario.
Update:
Based on the comments, you could do something like this to use the objects in events as canonical versions when loading savedEvents:
loadSavedEvents () {
// Grab events from the server
someServerCall().then(savedEvents => {
// Build a map so that the objects can be grabbed by id
const eventMap = {}
for (const event of this.events) {
eventMap[event.id] = event
}
// Build the list of server events using the objects in events
this.savedEvents = savedEvents.map(savedEvent => {
const event = eventMap[savedEvent.id]
this.$set(event, 'notes', savedEvent.notes)
return event
})
})
}

As pointed out by #skirtle the object from the list array needs to be pushed into the second array for it to be reactive. I have solved this by looping through and matching the id and then pushing this object into the second array. Not sure if this is the best / most efficient way to do this but it works now.
new Vue({
el: "#app",
data: {
eventsList: [
{ id: 1, text: "Event 1"},
{ id: 2, text: "Event 2"},
{ id: 3, text: "Event 3"}
],
savedEvents: [
{ id: 2, text: "Event 2", notes: 'Event Notes'}
]
},
mounted() {
this.init();
},
methods: {
init: function() {
let _temp = this.savedEvents;
this.savedEvents = [];
_temp.forEach(event => {
this.eventsList.forEach(x => {
if (event.id == x.id) {
this.$set(x, "notes", event.notes);
this.savedEvents.push(x);
}
});
});
},
toggleEvent: function(event){
let index = this.savedEvents.findIndex(e => e.id == event.id);
if (index != -1) {
this.savedEvents.splice(index, 1);
} else {
this.savedEvents.push(event);
}
},
inArray: function(id) {
return this.savedEvents.some(obj => obj.id == id);
}
}
})
body {
background: #20262E;
padding: 20px;
font-family: Helvetica;
}
#app {
background: #fff;
border-radius: 4px;
padding: 20px;
transition: all 0.2s;
}
li {
margin: 8px 0;
}
h2 {
font-weight: bold;
margin-bottom: 15px;
}
.btn {
display: inline-block;
padding: 5px;
border: 1px solid #666;
border-radius: 3px;
margin-bottom: 5px;
cursor: pointer;
}
input[type=text] {
padding: 5px;
}
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.17/vue.js"></script>
<div id="app">
<h2>Events:</h2>
<ul>
<li v-for="event in eventsList">
<span class="btn" #click="toggleEvent(event)">
{{ event.text }}
</span>
<input type="text" placeholder="Type your note here..." v-model="event.notes" v-if="inArray(event.id)">
</li>
</ul>
<h2>
Saved Events:
</h2>
<ul>
<li v-for="event in savedEvents">
<strong>{{ event.text }}</strong> {{ event.notes }}
</li>
</ul>
</div>

Try using $set and $delete for avoiding reactivity lost
https://v2.vuejs.org/v2/api/?#Vue-set
https://v2.vuejs.org/v2/guide/reactivity.html

Related

how to open definite element of tree

can you help me with this, i dont know how to open element of tree by vue js.i mean open definite element. if future every element of tree will be wraped by rouer-link, but right now i dont know how to trigger mechanism to open element.
tree example here
enter link description here
or here
[enter link description here][2]
let tree = {
label: 'root',
nodes: [
{
label: 'item1',
nodes: [
{
label: 'item1.1'
},
{
label: 'item1.2',
nodes: [
{
label: 'item1.2.1'
}
]
}
]
},
{
label: 'item2'
}
]
}
Vue.component('tree-menu', {
template: '#tree-menu',
props: [ 'nodes', 'label', 'depth' ],
data() {
return {
showChildren: false
}
},
computed: {
iconClasses() {
return {
'fa-plus-square-o': !this.showChildren,
'fa-minus-square-o': this.showChildren
}
},
labelClasses() {
return { 'has-children': this.nodes }
},
indent() {
return { transform: `translate(${this.depth * 50}px)` }
}
},
methods: {
toggleChildren() {
this.showChildren = !this.showChildren;
}
}
});
new Vue({
el: '#app',
data: {
tree
}
})
body {
font-family: "Open Sans", sans-serif;
font-size: 18px;
font-weight: 300;
line-height: 1em;
}
.container {
width: 300px;
margin: 0 auto;
}
.tree-menu {
.label-wrapper {
padding-bottom: 10px;
margin-bottom: 10px;
border-bottom: 1px solid #ccc;
.has-children {
cursor: pointer;
}
}
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.4.3/vue.js"></script>
<div class="container">
<h4>Vue.js Expandable Tree Menu<br/><small>(Recursive Components)</small></h4>
<div id="app">
<tree-menu
:nodes="tree.nodes"
:depth="0"
:label="tree.label"
></tree-menu>
</div>
</div>
<script type="text/x-template" id="tree-menu">
<div class="tree-menu">
<div class="label-wrapper" #click="toggleChildren">
<div :style="indent" :class="labelClasses">
<i v-if="nodes" class="fa" :class="iconClasses"></i>
{{ label }}
</div>
</div>
<tree-menu
v-if="showChildren"
v-for="node in nodes"
:nodes="node.nodes"
:label="node.label"
:depth="depth + 1"
>
</tree-menu>
</div>
</script>

Using onclick event, how to match name with multiple status by drawing lines in Vuejs?

new Vue({
el: "#app",
data: {
getQuestionAnswers: [
{
name: 'foo',
checked: false,
status: 'ok'
},
{
name: 'bar',
checked: false,
status: 'notok'
},
{
name: 'baz',
checked: false,
status: 'medium'
},
{
name: 'oo',
checked: false,
status: 'medium'
}
]
}
})
body {
background: #20262E;
padding: 20px;
font-family: Helvetica;
}
#app {
background: #fff;
border-radius: 4px;
padding: 20px;
transition: all 0.2s;
width:100%
}
.red {
color: red;
}
.bcom {
width: 100%;
display: flex;
}
.container1 {
width: 50px;
}
.container2 {
width: calc(100% - 105px);
padding: 8px 0;
height: 30px;
box-sizing: border-box;
}
.h-line {
height: 1px;
margin-bottom: 18px;
width: 100%;
background-color: black;
}
.container3{
margin-left: 5px;
width: 50px;
}
.point:hover {
width: 200px;
}
<script src="https://cdn.jsdelivr.net/npm/vue"></script>
<div id="app">
<div class="bcom"
v-for="(group, index) in getQuestionAnswers"
:key="index + group.name"
:group="group"
>
<div>
<input type="checkbox" v-model="group.checked"/>
{{ group.name }}
</div>
<div class="container2">
<div class="h-line" v-if="group.checked"></div>
</div>
<div>
<input type="checkbox"/>
{{ group.status }}
</div>
</div>
</div>
Onclick of checkbox, how to add multiple lines from one point in Vuejs?
As seen in the image, On click of the checkbox, Based on the status, I need to match from one point to three multiple status. like "ok, notok, medium"
i have taken v-model in the checkbox,to check and perfome two way data binding But not sure....what to do further. Do I need to take computed property and write condition to check and draw three multiple lines???
there are som positioning issues here, but this sample should be enough for you to get it working:
template
<div id="demo" :ref="'plane'">
<canvas :ref="'canvas'"></canvas>
<div
class="bcom"
v-for="(group, index) in getQuestionAnswers"
:key="index + group.name"
:group="group"
>
<div>
<input
type="checkbox"
v-on:click="() => onToggleCheckbox(group)"
v-model="group.checked"
:ref="'checkbox_' + group.name"
/>
<span>{{ group.name }}</span>
</div>
<div>
<span>{{ group.status }}</span>
<input type="checkbox" :ref="'status_' + group.name" />
</div>
</div>
</div>
script:
export default {
name: 'App',
data: () => ({
ctx: undefined,
draw(begin, end, stroke = 'black', width = 1) {
if (!this.ctx) {
const canvas = this.$refs['canvas'];
if (!canvas?.getContext) return;
canvas.width = canvas.offsetWidth;
canvas.height = canvas.offsetHeight;
this.ctx = canvas.getContext('2d');
}
if (stroke) {
this.ctx.strokeStyle = stroke;
}
if (width) {
this.ctx.lineWidth = width;
}
this.ctx.beginPath();
this.ctx.moveTo(...begin);
this.ctx.lineTo(...end);
this.ctx.stroke();
},
onToggleCheckbox(group) {
const planeEl = this.$refs['plane'];
const planeRect = planeEl.getBoundingClientRect();
const fromEl = this.$refs['checkbox_' + group.name];
const fromRect = fromEl.getBoundingClientRect();
const from = {
x: fromRect.right - planeRect.left,
y: fromRect.top + fromRect.height / 2 - planeRect.top,
};
const toEl = this.$refs['status_' + group.name];
const toRect = toEl.getBoundingClientRect();
const to = {
x: toRect.left - planeRect.left,
y: toRect.top + toRect.height / 2 - planeRect.top,
};
console.log(planeRect, from, to);
this.draw(
Object.values(from),
Object.values(to),
group.checked ? 'white' : 'black',
group.checked ? 3 : 2
);
},
getQuestionAnswers: [
{
name: 'foo',
checked: false,
status: 'ok',
},
{
name: 'bar',
checked: false,
status: 'notok',
},
{
name: 'baz',
checked: false,
status: 'medium',
},
{
name: 'oo',
checked: false,
status: 'medium',
},
],
}),
};
style
body {
background: #20262e;
padding: 20px;
font-family: Helvetica;
}
#demo {
position: relative;
border-radius: 4px;
padding: 20px;
transition: all 0.2s;
}
canvas {
position: absolute;
background: red;
width: 100%;
height: 100%;
left: 0;
top: 0;
background: #fff;
z-index: -1;
}
.bcom {
width: 100%;
display: flex;
justify-content: space-between;
z-index: 2;
}
this only draws one line but you could easily add the others. I figured you might change your data schema to something like:
getQuestions() {
{
name: string,
checked: boolean,
statuses: [string...],
},
getStatuses() {
{
name: string
}
but not knowing about your requirements here, I decided to post the above before making further changes. (here is the sort of refactor I was referring to: https://stackblitz.com/edit/vue-yuvsxa )
addressing first comment:
in app.vue only there is one data called[((questions))], inside question we are looping and setting the status.
this is easy to address with a bit of preprocessing:
questionsAndStatusesMixed: // such as [{...question, ...statuses}],
questions: [],
statuses: [],
mounted() {
const statusesSet = new Set()
this.questionsAndStatusesMixed.forEach(item => {
const question = {
name: item.name,
checked: item.checked,
answer: item.status // is this the answer or .. these never made sense to me,
statuses: this.statuses // assuming each question should admit all statuses/that is, draw a line to each
}
const status = {
name: item.name
}
this.questions.push(question)
statusesSet.add(status)
})
Array.from(statusesSet).forEach(item => this.statuses.push(item))
}

How to call method on click as opposed to on v-for in Vuejs

I'm trying to display this array of objects based on highest to lowest rating of each item. It works fine using the method on the v-for, but I would really like to display the list of objects in the order they appear initially and THEN call a function that displays them by highest to lowest rating.
When I have it set as v-for="item in items" and then try to call the method on a button, such as #click="rated(items)", nothing happens. Why would I be able to display the array initially on the v-for with the method attached, but not on a click event?
const items = [
{
name: "Bert",
rating: 2.25
},
{
name: "Ernie",
rating: 4.6
},
{
name: "Elmo",
rating: 8.75
},
{
name: "Rosita",
rating: 2.75
},
{
name: "Abby",
rating: 9.5
},
{
name: "Cookie Monster",
rating: 5.75
},
{
name: "Oscar",
rating: 6.75
}
]
new Vue({
el: "#app",
data: {
items: items
},
methods: {
rated: function(items) {
return items.slice().sort(function(a, b) {
return b.rating - a.rating;
});
},
sortByRating: function(items) {
return items.slice().sort(function(a, b) {
return b.rating - a.rating;
});
}
}
});
#app {
display: flex;
flex-flow: row wrap;
margin-top: 3rem;
}
.item {
flex: 1;
margin: .5rem;
background: #eee;
box-shadow: 0px 2px 4px rgba(0,0,0,.5);
padding: 1rem;
min-width: 20vw;
}
.toggle {
position: absolute;
top: 10px;
left: 10px;
padding: .5rem 1rem;
background: DarkOrchid;
color: #fff;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.17/vue.js"></script>
<div id="app">
<div v-for="item in rated(items)"
class="item">
<p>{{item.name}}</p>
<p>{{item.rating}}</p>
</div>
</div>
Try to rewrite the array for the result returned by the method, like this.
#click="items = rated(items)
and, inside v-for you can keep using items.

Is there a way to know which node was changed in a JSON tree?

I have a jqtree and I should send which nodes were changed using AJAX, to do such a thing, I've used a recursive function to read the whole JSON, the problem is, I just want to send which node was affected. the user can only change one item at a time since the tree is drag-and-drop.
Clarifying:
The page is loaded, and the structure is as well (check on firebug):
then, the user selects and drags child1 into node2:
by doing that the new JSON is generated, then, it's separated and classified into vpais (parents) and vfilhos (children)
However, as you can see, it's not necessary to send all parents and children because only one node will be changed (and it will always be, simply because only one item can be dragged at a time). Is there a way to know which node was dragged and its new parent?
Thanks for making so far :)
My code:
$(document).ready(function() {
var data = [{
label: 'node1',
id: 1,
children: [{
label: 'child1',
id: 2
}, {
label: 'child3',
id: 3
}, {
label: 'child2',
id: 4
}, {
label: 'child2',
id: 5
}]
}, {
label: 'node2',
id: 6,
children: [{
label: 'child3',
id: 7
}]
}];
$('#tree1').tree({
data: data,
autoOpen: false,
dragAndDrop: true
});
console.log("Original Structure" + $('#tree1').tree('toJson'));
$('#tree1').bind(
'tree.move',
function(event) {
data = $(this).tree('toJson');
event.preventDefault();
event.move_info.do_move();
console.log("New Structure" + $(this).tree('toJson'));
data = $(this).tree('toJson');
var dadSon = [];
var dad = [],
son = [];
var group = "";
var randomic = "";
(function printDadSon(data, parent) {
if (!data) return;
for (var i = 0; i < (data.length); i++) {
if (parent && parent != 'undefined') {
dadSon[i] = ('vpai= ' + parent + "&" + 'vfilho= ' + data[i].id + "&");
group += dadSon[i];
}
printDadSon(data[i].children, data[i].id);
}
})(JSON.parse(data));
var temp = group.length;
group = group.substring(0, temp - 1);
console.log(dadSon);
$.ajax({
type: 'POST',
url: 'sistema.agrosys.com.br',
dataType: 'json',
data: group
});
console.log("Done");
}
);
});
#navdata {
width: auto;
height: auto;
flex: 1;
padding-bottom: 1px;
}
#navgrid {
width: 50%;
height: 200px;
overflow-x: visible;
overflow-y: scroll;
border: solid 1px #79B7E7;
background-color: white;
}
#header {
background-color: #79B7E7;
width: 99.6%;
text-align: center;
border: 1px solid white;
margin: 1px;
}
.jqtree-element {
background-color: white;
border: 1px solid white;
height: 23px;
color: red;
}
.jqtree-tree .jqtree-title {
color: black;
}
ul.jqtree-tree {
margin-top: 0px;
margin-left: 1px;
}
ul.jqtree-tree,
ul.jqtree-tree ul.jqtree_common {
list-style: none outside;
margin-bottom: 0;
padding: 0;
}
ul.jqtree-tree ul.jqtree_common {
display: block;
text-align: left;
padding-left: 0px;
margin-left: 20px;
margin-right: 0;
}
ul.jqtree-tree li.jqtree-closed > ul.jqtree_common {
display: none;
}
ul.jqtree-tree li.jqtree_common {
clear: both;
list-style-type: none;
}
ul.jqtree-tree .jqtree-toggler {
color: #325D8A;
}
ul.jqtree-tree .jqtree-toggler:hover {
color: #3966df;
text-decoration: none;
}
.jqtree-tree .jqtree-title.jqtree-title-folder {
margin-left: 0;
}
span.jqtree-dragging {
color: #fff;
background: #79B7E7;
opacity: 0.8;
cursor: pointer;
padding: 2px 8px;
}
ul.jqtree-tree li.jqtree-selected > .jqtree-element,
ul.jqtree-tree li.jqtree-selected > .jqtree-element:hover {
background: -webkit-gradient(linear, left top, left bottom, from(#BEE0F5), to(#79B7E7));
}
<!DOCTYPE html>
<!-- Programa: JqTree | Envia nova estrutura JSON como v pai e vfilho -->
<!-- Autor: Calne Ricardo de Souza -->
<!-- Data: 06/07/2015 -->
<html>
<head>
<script type="text/javascript" src="http://ajax.googleapis.com/ajax/libs/jquery/1.10.2/jquery.min.js"></script>
<script src="http://sistema.agrosys.com.br/sistema/labs/tree.jquery.js"></script>
<link rel="stylesheet" href="http://sistema.agrosys.com.br/sistema/labs/jqtree.css">
<script src="http://sistema.agrosys.com.br/sistema/labs/jquery-cookie/src/jquery.cookie.js"></script>
</head>
<body>
<div id="navgrid">
<div id="header">Header</div>
<div id="tree1">
<ul class="jqtree_common jqtree-tree">
<li class="jqtree_common jqtree-folder">
<div class="jqtree-element jqtree_common">
<a class="jqtree_common jqtree-toggler">â–¼</a>
<span class="jqtree_common jqtree-title jqtree-title-folder">node1</span>
</div>
<ul class="jqtree_common ">
<li class="jqtree_common">
<div class="jqtree-element jqtree_common">
<span class="jqtree-title jqtree_common">child2</span>
</div>
</li>
<li class="jqtree_common">
<div class="jqtree-element jqtree_common">
<span class="jqtree-title jqtree_common">child1</span>
</div>
</li>
</ul>
</li>
<li class="jqtree_common jqtree-folder">
<div class="jqtree-element jqtree_common">
<a class="jqtree_common jqtree-toggler">â–¼</a>
<span class="jqtree_common jqtree-title jqtree-title-folder">node2</span>
</div>
<ul class="jqtree_common ">
<li class="jqtree_common">
<div class="jqtree-element jqtree_common">
<span class="jqtree-title jqtree_common">child3</span>
</div>
</li>
</ul>
</li>
</ul>
</div>
</div>
</body>
</html>
I think mutation observers work very well here.
if (typeof(MutationObserver) != "undefined")
{
//this is new functionality
//observer is present in firefox/chrome and IE11+
// select the target node
// create an observer instance
observer = new MutationObserver(observeJSONTree);
// configuration of the observer:
var config = { attributes: false, childList: true, characterData: false, subtree: true };
// pass in the target node, as well as the observer options
observer.observe([tree container], config);
}
function observerJSONTree(e)
{
for (eventObject in e)
{
switch(e[eventObject].type)
{
case 'characterData' :
//when text is changed
//for now do nothing
break;
case 'childList' :
//childs added or removed
if (e[eventObject].addedNodes.length > 0)
{
//childs added
//do something
}
if (e[eventObject].removedNodes.length > 0)
{
//childs removed
}
break;
}
}
}
The config object determines which events should be listened to. In this case we listen to changes to childs in the container of the tree.
When a change is detected it will fire the mutation observer call to the function specified. In the event data is stored about the edit made. For a child drag/drop these are two events. Child removal and child appending.

How can I map a json object of tree and set it in separate variables?

Really hard to explain, but I'll do my best. So I've got a JSON data used on a JqTree and I'm trying to get which are the parents and the children and put them in little blocks.
My Tree:
and its data AKA structure:
var data = [
{"name":"node1","id":1,"is_open":true,"children":[
{"name":"child2","id":2},
{"name":"child2","id":3},
{"name":"child1","id":4}
]},
{"name":"node1","id":5,"is_open":true,"children":[
{"name":"child2","id":6}
]}
];
What I want to do is to put them into variables, like these:
variables dad and son and their ids
dad = 1 son = 2
dad = 2 son = 3 and so on.
Notice that dad = 2 is also the son = 2
Thus, my tree would be:
dad = 1 son = 2
dad = 1 son = 3
dad = 1 son = 4
dad = 5 son = 6
I need it this way because I'll send it using AJAX like this:
data:dad+"&"+son, and if you check my code you will notice that this will be sent when the user changes the structure with the cursor, so it will be always one dad and one son, thus, no array needed.
Why:
I need to send it that way because I'm using a shitty and limited language on the server side (it's not mine)
My Tree explained:
The original data will in the future be sent by this shitty server like this dad = 1 & son = 2, then I will do "simply" the reverse.
My server compares the original data with the new one and gives me the difference that I should transform into that way again and send it using ajax.
Right now, as you can see, I'm sending only the "difference" POSITIONS.
$('#tree1').bind(
'tree.move',
function(event) {
data = $(this).tree('toJson');
event.preventDefault();
// do the move first, and _then_ POST back.
event.move_info.do_move();
console.log("New Structure" + $(this).tree('toJson'));
POSITIONS = $(this).tree('toJson');
POSITIONS = getDiff(data, POSITIONS);
alert("Difference\n\n" + POSITIONS);
$.post('http://localhost:8080/JqTree/Hello', {
tree: POSITIONS
});
alert("done");
}
);
Thank you so much for reading this far.
This is my code:
$(document).ready(function() {
var POSITIONS;
//Mandar o response aqui no data
var data = [{
label: 'node1',
id: 1,
children: [{
label: 'child1',
id: 2
}, {
label: 'child2',
id: 3
}, {
label: 'child2',
id: 6
}]
}, {
label: 'node2',
id: 4,
children: [{
label: 'child3',
id: 5
}]
}];
$('#tree1').tree({
data: data,
autoOpen: false,
dragAndDrop: true
});
function getDiff(a, b) {
var diff = (isArray(a) ? [] : {});
recursiveDiff(a, b, diff);
return diff;
}
function recursiveDiff(a, b, node) {
var checked = [];
for (var prop in a) {
if (typeof b[prop] == 'undefined') {
addNode(prop, '[[removed]]', node);
} else if (JSON.stringify(a[prop]) != JSON.stringify(b[prop])) {
// if value
if (typeof b[prop] != 'object' || b[prop] == null) {
addNode(prop, b[prop], node);
} else {
// if array
if (isArray(b[prop])) {
addNode(prop, [], node);
recursiveDiff(a[prop], b[prop], node[prop]);
}
// if object
else {
addNode(prop, {}, node);
recursiveDiff(a[prop], b[prop], node[prop]);
}
}
}
}
}
function addNode(prop, value, parent) {
parent[prop] = value;
}
function isArray(obj) {
return (Object.prototype.toString.call(obj) === '[object Array]');
}
console.log("Original Structure" + $('#tree1').tree('toJson'));
$('#tree1').bind(
'tree.move',
function(event) {
data = $(this).tree('toJson');
event.preventDefault();
// do the move first, and _then_ POST back.
event.move_info.do_move();
console.log("New Structure" + $(this).tree('toJson'));
POSITIONS = $(this).tree('toJson');
POSITIONS = getDiff(data, POSITIONS);
alert("Difference\n\n" + POSITIONS);
$.post('http://localhost:8080/JqTree/Hello', {
tree: POSITIONS
});
alert("done");
}
);
});
#navdata {
width: auto;
height: auto;
flex: 1;
padding-bottom: 1px;
}
#navgrid {
width: 50%;
height: 200px;
overflow-x: visible;
overflow-y: scroll;
border: solid 1px #79B7E7;
background-color: white;
}
#header {
background-color: #79B7E7;
width: 99.6%;
text-align: center;
border: 1px solid white;
margin: 1px;
}
.jqtree-element {
background-color: white
/*#DDEBF7*/
;
border: 1px solid white;
height: 23px;
color: red;
}
.jqtree-tree .jqtree-title {
color: black;
}
ul.jqtree-tree {
margin-top: 0px;
margin-left: 1px;
}
ul.jqtree-tree,
ul.jqtree-tree ul.jqtree_common {
list-style: none outside;
margin-bottom: 0;
padding: 0;
}
ul.jqtree-tree ul.jqtree_common {
display: block;
text-align: left;
padding-left: 0px;
margin-left: 20px;
margin-right: 0;
/*border-left:20px solid #DDEBF7;*/
}
ul.jqtree-tree li.jqtree-closed > ul.jqtree_common {
display: none;
}
ul.jqtree-tree li.jqtree_common {
clear: both;
list-style-type: none;
}
ul.jqtree-tree .jqtree-toggler {
color: #325D8A;
}
ul.jqtree-tree .jqtree-toggler:hover {
color: #3966df;
text-decoration: none;
}
.jqtree-tree .jqtree-title.jqtree-title-folder {
margin-left: 0;
}
span.jqtree-dragging {
color: #fff;
background: #79B7E7;
opacity: 0.8;
cursor: pointer;
padding: 2px 8px;
}
ul.jqtree-tree li.jqtree-selected > .jqtree-element,
ul.jqtree-tree li.jqtree-selected > .jqtree-element:hover {
background: -webkit-gradient(linear, left top, left bottom, from(#BEE0F5), to(#79B7E7));
}
<!DOCTYPE html>
<html>
<head>
<script type="text/javascript" src="http://ajax.googleapis.com/ajax/libs/jquery/1.10.2/jquery.min.js"></script>
<script src="http://sistema.agrosys.com.br/sistema/labs/tree.jquery.js"></script>
<link rel="stylesheet" href="http://sistema.agrosys.com.br/sistema/labs/jqtree.css">
<script src="http://sistema.agrosys.com.br/sistema/labs/jquery-cookie/src/jquery.cookie.js"></script>
</head>
<body>
<div id="navgrid">
<div id="header">Header</div>
<div id="tree1">
<ul class="jqtree_common jqtree-tree">
<li class="jqtree_common jqtree-folder">
<div class="jqtree-element jqtree_common">
<a class="jqtree_common jqtree-toggler">â–¼</a>
<span class="jqtree_common jqtree-title jqtree-title-folder">node1</span>
</div>
<ul class="jqtree_common ">
<li class="jqtree_common">
<div class="jqtree-element jqtree_common">
<span class="jqtree-title jqtree_common">child2</span>
</div>
</li>
<li class="jqtree_common">
<div class="jqtree-element jqtree_common">
<span class="jqtree-title jqtree_common">child1</span>
</div>
</li>
</ul>
</li>
<li class="jqtree_common jqtree-folder">
<div class="jqtree-element jqtree_common">
<a class="jqtree_common jqtree-toggler">â–¼</a>
<span class="jqtree_common jqtree-title jqtree-title-folder">node2</span>
</div>
<ul class="jqtree_common ">
<li class="jqtree_common">
<div class="jqtree-element jqtree_common">
<span class="jqtree-title jqtree_common">child3</span>
</div>
</li>
</ul>
</li>
</ul>
</div>
</div>
</body>
</html>

Categories

Resources