How to dynamically render a tree of data in svelte? - javascript

I have a tree of data that i'm using to represent a directory of the file system. It look something like this.
{
isRoot: true
path: "/path/to/dir"
fileName: "dir",
isDirectory: true,
parent: null,
children: [
{
isRoot: false
path: "/path/to/dir/file1.txt"
fileName: "file1.txt",
isDirectory: true,
parent: {...spread parent node here},
children: null
},
{
isRoot: false
path: "/path/to/dir/subdir"
fileName: "subdir",
isDirectory: true,
parent: {...spread parent node here},
children: [
{
isRoot: false
path: "/path/to/dir/subdir/file2.txt"
fileName: "file2.txt",
isDirectory: false,
parent: {...spread parent node here},
children: null
}
]
}
]
}
I want to take each node and turn it into this.
<Node branch={some parent from that tree} >
<Node branch={a child of that parent} />
<Node branch={another child of that parent>
<Node branch={a child of the new parent />
</Node>
</Node
I know that svelte allows you to loop through arrays and render content based on each item however there is no way of knowing what the tree looks like.
Is there any way to dynamically render this tree in svelte?

This unanswered question came up in a search two years after being asked, so I'll go ahead and answer it while I'm here. The comments on the question just needed to be filled out to become a proper answer.
As the comments to your question have already explained, you'll need to use the recursive <svelte:self> component. It represents the component that is being defined in that particular module. In other words, if you are defining a <Folder> component and want to have other folders inside it, you would refer to them as <svelte:self> inside the definition of <Folder>. This is because a module cannot import itself.
The Svelte Tutorial has a specific module on this exact scenario, so everyone who writes code in Svelte should go through the entire tutorial (it's not long) and learn the basics of Svelte. You would want to do something quite similar to the example.
Here is the example shown in the tutorial:
App.svelte
<script>
import Folder from './Folder.svelte';
let root = [
{
name: 'Important work stuff',
files: [
{ name: 'quarterly-results.xlsx' }
]
},
{
name: 'Animal GIFs',
files: [
{
name: 'Dogs',
files: [
{ name: 'treadmill.gif' },
{ name: 'rope-jumping.gif' }
]
},
{
name: 'Goats',
files: [
{ name: 'parkour.gif' },
{ name: 'rampage.gif' }
]
},
{ name: 'cat-roomba.gif' },
{ name: 'duck-shuffle.gif' },
{ name: 'monkey-on-a-pig.gif' }
]
},
{ name: 'TODO.md' }
];
</script>
<Folder name="Home" files={root} expanded/>
File.svelte
<script>
export let name;
$: type = name.slice(name.lastIndexOf('.') + 1);
</script>
<span style="background-image: url(/tutorial/icons/{type}.svg)">{name}</span>
<style>
span {
padding: 0 0 0 1.5em;
background: 0 0.1em no-repeat;
background-size: 1em 1em;
}
</style>
And finally, Folder.svelte:
<script>
import File from './File.svelte';
export let expanded = false;
export let name;
export let files;
function toggle() {
expanded = !expanded;
}
</script>
<span class:expanded on:click={toggle}>{name}</span>
{#if expanded}
<ul>
{#each files as file}
<li>
{#if file.files}
<svelte:self {...file}/>
{:else}
<File {...file}/>
{/if}
</li>
{/each}
</ul>
{/if}
<style>
span {
padding: 0 0 0 1.5em;
background: url(/tutorial/icons/folder.svg) 0 0.1em no-repeat;
background-size: 1em 1em;
font-weight: bold;
cursor: pointer;
}
.expanded {
background-image: url(/tutorial/icons/folder-open.svg);
}
ul {
padding: 0.2em 0 0 0.5em;
margin: 0 0 0 0.5em;
list-style: none;
border-left: 1px solid #eee;
}
li {
padding: 0.2em 0;
}
</style>

Related

Using vue-virtual-scroller not showing

I'm trying out https://github.com/Akryum/vue-virtual-scroller but I cannot get it to work. What am I doing wrong?
I use pug / jade for server-side HTML rendering. I don't receive any error messages but nothing gets rendered...
Pug / Jade
#test
recyclescroller.scroller(:items='active_projects' :item-size='32' key-field='id' v-slot='{ item }')
.user
| {{ item.name }}
CSS
.user {
height: 32%;
padding: 0 12px;
display: flex;
align-items: center;
}
javascript
Vue.component('RecycleScroller', VueVirtualScroller.RecycleScroller)
test = new Vue({
el: '#test',
data: {
active_projects : [{name : 'test'}]
}
})
When you use Recyclescroller you should inside each item has id
Because the uniqueness of the item is done by using the id
Your list should look like this :
data () {
return {
active_projects: [
{
name: 'test', id: 1
},
{
name: 'test', id: 2
},
{
name: 'test', id: 3
},
{
name: 'test', id: 4
},
]
}
}
Doc - Important notes
If the items are objects, the scroller needs to be able to identify them. By default it will look for an id field on the items. This can be configured with the keyField prop if you are using another field name.

How to detect if an element is outside of its container?

So I've created the the following codesandbox. I got a webapp that relies heavily on user input. For demonstration purposes I've kept it simple by displaying a bunch of authors on a a4 formatted page. The page and font-size both use vw unit to make it responsive.
As you can see in the codesandbox, the last few authors are forced off the page because it no longer fits inside the container. Ideally I'd like to detect the content that doesn't fit on the page anymore, and generate a second identical a4 page to display that particular content.
Currently in my webapp I've just added overflow: scroll; to the page div where all the content is placed in, so that it at least looks somewhat 'ok'. But it isn't a very good User Experience and I'd like to improve it.
I don't have a clue where to start so any help in the right direction would be very much appreciated.
Thanks in advance.
CSS
#app {
-webkit-font-smoothing: antialiased;
font: 12pt "Tahoma";
}
.book {
margin: 0;
padding: 0;
background-color: #FAFAFA;
font: 3vw "Tahoma";
}
* {
box-sizing: border-box;
-moz-box-sizing: border-box;
}
.page {
/* overflow: scroll; */
display: block;
width: calc(100 / 23 * 21vw);
height: calc(100 / 23 * 29.7vw);
margin: calc(100 / 23 * 1vw) auto;
border: 1px #D3D3D3 solid;
border-radius: 5px;
background: white;
box-shadow: 0 0 5px rgba(0, 0, 0, 0.1);
}
.subpage {
margin: calc(100 / 23 * 1vw);
width: calc(100 / 23 * 19vw);
height: calc(100 / 23 * 27.7vw);
line-height: 2;
border: 1px solid red;
outline: 0cm #FAFAFA solid;
}
.subpage-content {
height: 100%;
}
Javascript
export default {
name: "App",
data() {
return {
authors: [
{ id: 1, name: "Smith" },
{ id: 2, name: "Johnson" },
{ id: 3, name: "Williams" },
{ id: 4, name: "Jones" },
{ id: 5, name: "Brown" },
{ id: 6, name: "Davis" },
{ id: 7, name: "Miller" },
{ id: 8, name: "Wilson" },
{ id: 9, name: "Moore" },
{ id: 10, name: "Taylor" },
{ id: 11, name: "Anderson" },
{ id: 12, name: "Thomas" },
{ id: 13, name: "Jackson" },
{ id: 14, name: "White" },
{ id: 15, name: "Harris" },
{ id: 16, name: "Martin" },
{ id: 17, name: "Thomspson" },
{ id: 18, name: "Garcia" },
{ id: 19, name: "Martinez" },
{ id: 20, name: "Robinson" },
{ id: 21, name: "Clark" },
{ id: 22, name: "Rodeiquez" },
{ id: 23, name: "Lewis" },
{ id: 24, name: "Lee" }
]
};
}
};
HTML
<template>
<div id="app">
<div class="container-fluid">
<div class="book">
<div class="page">HEADER
<div class="subpage" id="editor-container">Authors:
<!-- <div class="subpage-content">The real content</div> -->
<div v-for="item in authors" :key="item.id">{{ item.name }}</div>
</div>
</div>
</div>
</div>
</div>
</template>
You can view a fork of your code sandbox here.
I changed the data structure (and template) to have a pages array in which each page has an authors array, instead of a single one. Initially, the first page holds all the authors.
data() {
return {
pages: [
{
authors: [
{ id: 1, name: "Smith" },
...
]
}
]
}
}
<div class="page" v-for="(page, pageIndex) in pages" :key="pageIndex">HEADER
<div class="subpage" id="editor-container">
<template v-if="pageIndex < 1">Authors:</template>
<!-- <div class="subpage-content">The real content</div> -->
<div v-for="item in page.authors" :key="item.id" class="author">{{ item.name }}</div>
</div>
</div>
I then created a method recalcPages that gets called when the component is mounted:
methods: {
recalcPages() {
let pageElements = this.$el.querySelectorAll(".page");
Array.from(pageElements).some((p, pi) => {
let authors = p.querySelectorAll(".author");
if (authors.length) {
return Array.from(authors).some((a, ai) => {
let offPage = a.offsetTop + a.offsetHeight > p.offsetHeight;
if (offPage) {
let currentAuthors = this.pages[pi].authors;
var p1 = currentAuthors.slice(0, ai);
var p2 = currentAuthors.slice(ai);
this.pages[pi].authors = p1;
this.pages.push({ authors: p2 });
}
return offPage;
});
}
return false;
});
}
},
It iterates the actual DOM nodes and uses offsetTop + offsetHeight to calculate whether an author is off the page or not. As soon as an element leaves the page, it and all following elements are split from the current page's authors and a second page is inserted.
You'll also need to call this.recalcPages() after updating the contents deleting all pages and set a new authors array on the first one to be split up automatically again, unless you're only adding to the last page. You could also try to use the updated hook to achieve this automatically, I haven't tried that.
Of course it's quite a heavy operation, as it renders the component just to trigger re-rendering again by modifying the data. But unless you don't know the exact height of every element, there's no way around it (at least none I'm aware of).
By the way (although your final data will probably look different, but just for the sake of completeness of this demonstration) I also wrapped your Authors: headline in <template v-if="pageIndex < 1">Authors:</template> in order to display it only on the first page.

How can I create dynamic filtering in Vue from a graphql dataset?

How do I create a dynamical filter using a computed property from vue when the dataset is from a graphql-query?
I've looked at several articles that all use the array.filter()-method, but I can't get it to work on my dataset (dummy data below):
books: [{
node: {
title: 'Elon Musk',
by:'Ashlee Vance',
},
node: {
title: 'Steve Jobs',
by:'George Llian',
},
node: {
title: 'Face of Facebook',
by: 'Sandip Paul',
},
node: {
title: 'Tim Cook',
by:'Andy Atkins',
url:'http://www.voidcanvas.com/'
},
node: {
title: 'Abdul Kalam',
by:'Arun Tiwari',
},
node: {
title: 'Story of Elon Musk',
by:'BP John',
},
node: {
title: 'Story of Bill Gates',
by:'Russel Crook',
},
node: {
title: 'Becoming Steve Jobs',
by:'Andrew Russel',
}
}]
Method:
computed: {
filteredBooks: function () {
var books_array = this.books,
searchString = this.searchString;
if(!searchString) {
return books_array;
}
searchString = searchString.trim().toLowerCase();
books_array = books_array.filter(function(item) {
if(item.node.title.toLowerCase().indexOf(searchString) !== -1) {
return item;
}
});
return books_array;
}
HTML:
<div id="app">
<input type="text" v-model="searchString" placeholder="search" />
<ul style="list-style: none;">
<li v-for="book in filteredBooks">
<p>{{book.title}} -by- {{book.by}}</p>
</li>
</ul>
</div>
This is my first coding project since early 2000, so please feel free to point me in the right direction if this is the wrong forum for this question.
I set up a jsfiddle to play with the case.
Here is the code with some modifications:
var app = new Vue({
el: '#app',
data: {
searchString: '',
books: [{
title: 'Elon Musk',
by: 'Ashlee Vance'
},
{
title: 'Steve Jobs',
by: 'George Llian'
},
{
title: 'Face of Facebook',
by: 'Sandip Paul'
},
{
title: 'Tim Cook',
by: 'Andy Atkins',
url: 'http://www.voidcanvas.com/'
},
{
title: 'Abdul Kalam',
by: 'Arun Tiwari'
},
{
title: 'Story of Elon Musk',
by: 'BP John'
},
{
title: 'Story of Bill Gates',
by: 'Russel Crook'
},
{
title: 'Becoming Steve Jobs',
by: 'Andrew Russel'
}
]
},
computed: {
filteredBooks: function() {
return this.books.filter(e => this.searchString === '' ? false : e.title.toLowerCase().indexOf(this.searchString.toLowerCase()) !== -1 ? true : false);
}
}
});
body {
background-color: #dbd8d8;
padding: 20px;
}
input {
width: 300px;
height: 30px;
padding: 0.2rem;
}
.design {}
p {
position: relative;
display: block;
padding: .4em .4em .4em 2em;
margin: .5em 0;
border: 3px solid white;
background: #FC756F;
color: #444;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.17/vue.js"></script>
<div id="app">
<input type="text" v-model="searchString" placeholder="search" />
<ul style="list-style: none;">
<li v-for="book in filteredBooks">
<p>{{book.title}} -by- {{book.by}}</p>
</li>
</ul>
</div>
Remove the node: from before the objects in the books data array - books array should contain a bunch of plain objects. If you put node: before each object, then you "say" that the every node is the key of key-value pair of an object (so the keynames will be identical - node!!!)
Simplify filteredBooks computed - no need to store all the variables. This function (filteredBooks) doesn't change the inputs, so you can use this here. The filter() functions doesn't change the array it filters - rather it returns a new array, containing only values that the iteratee function "saw" as true
You check for !searchString and that's never the case - searchString is always going to be true as you initialize it with searchString: '' (an empty value - but a value), so I changed it checking for the empty value in the filteredBooks computed.
I modified the code so that it compares lowercase to lowercase. With your code if someone typed a search string in uppercase, then there'd have been no match.

How to create a favorite list with Vue js. Allowing the user to select maximun 5 options

First of all, I'm new to programming and new to this channel so I'm sorry if the structure of my question is not correct. I can provide more details if needed. Thanks in advance for your help.
Context: I have a view with multiple options (buttons) where the user can select up to 5 of them. Once the user selects the fifth button all the other buttons should get disabled. If the user clicks again on one of the selected buttons, all the other buttons should get enabled again. How can I implement this logic?
I'm using vue js and vuetify.
It's probably not the best solution but I once the user clicks on a button I change the class of that button so it looks as if it was active. Then I count the amount of buttons that have been clicked to disable the rest of the buttons (this is not working).
<v-layout>
<v-flex row wrap>
<v-card class="interests__content">
<!-- looping through interests array -->
<!-- with :class i'm binding classes to change the color of the buttons, with #click="increase()" i'm calling the increase method -->
<v-tooltip color="primary" top v-for="interest in interests" :key="interest.name">
<v-btn
v-model="selected"
flat
slot="activator"
:class="{blue:interest.checked, interest__btn: !interest.checked}"
#click="increase(interest)"
:disabled="disabled"
:checked="checked"
>{{interest.name}}</v-btn>
<span>{{interest.name}}</span>
</v-tooltip>
</v-card>
</v-flex>
</v-layout>
</v-container>
</template>
<script>
export default {
data() {
return {
checked: false,
selected: "",
count: 0,
disabled: false,
interests: [
{ name: "Interest1", checked: false },
{ name: "Interest2", checked: false },
{ name: "Interest3", checked: false },
{ name: "Interest4", checked: false },
{ name: "Interest5", checked: false },
{ name: "Interest6", checked: false },
{ name: "Interest7", checked: false },
{ name: "Interest8", checked: false },
{ name: "Interest9", checked: false },
]
};
},
methods: {
increase: function(interest) {
this.count += 1;
//changing the value of checked to add the blue class, with this class I add the background color to the button as if it was active.
interest.checked = !interest.checked;
if (this.count > 4) {
this.disabled = !this.disabled;
}
},
// I'm trying to check if the button has a class to implement the logic
checkIfClass: function(interest) {
interest.checked = !interest.checked;
if (interest.classList.contains("blue"));
}
},
computed: {}
};
</script>
<style scoped>
.interest__btn {
font-family: Arial;
font-size: 1em;
background: white;
color: #333333;
border: 1px solid #0091da;
text-transform: none;
}
.interest__btn:hover {
color: black;
background-color: rgba(172, 196, 221, 0.7);
}
.interests__content {
padding: 1.7em;
}
.blue {
background-color: #0091da;
color: white !important;
text-transform: none;
}
</style>
The Vue way of doing this is to have all the state of your application in js (your interests array), then to make everything that handles the display of this state reactive. In practice this means using computed rather than methods for everything that turns state into pixels on screen.
In da old days, we would have looped through interests to count the number checked. In 2019 we'll use reduce(), so...
computed:{
numChecked(){
return this.interests.reduce((acc,curr)=>acc += curr.checked?1:0,0)
}
}
Then in your template...
:disabled="(numChecked > 5 && !interest.checked)?'disabled':''"
I finally came up with a solution, basically using a watcher to know when the counter was greater than 5 and adding another value-pair to handle the disable property:
`
{{interest.name}}
<span>{{interest.name}}</span>
</v-tooltip>
</v-card>
</v-flex>
</v-layout>
</v-container>
</template>
<script>
export default {
data() {
return {
checked: false,
selected: "",
count: 0,
disabled: false,
interests: [
{ name: "Interest1", checked: false, disabled: false },
{ name: "Interest2", checked: false, disabled: false },
{ name: "Interest3", checked: false, disabled: false },
{ name: "Interest4", checked: false, disabled: false },
{ name: "Interest5", checked: false, disabled: false },
{ name: "Interest6", checked: false, disabled: false },
{ name: "Interest7", checked: false, disabled: false },
]
};
},
methods: {
increase: function(interest) {
//changing the value of checked to add the blue class, with this class I add
the background color to the button as if it was active.
interest.checked = !interest.checked;
if (interest.checked) {
this.count++;
} else {
this.count--;``
}
}
},
watch: {
count: function(n, o) {
if (this.count > 4) {
for (let i = 0; i < this.interests.length; i++) {
if (!this.interests[i].checked) {
this.interests[i].disabled = true;
} else {`
this.interests[i].disabled = false;
}
}
} else {
for (let i = 0; i < this.interests.length; i++) {
this.interests[i].disabled = false;
}
}
}
}
};
</script>
<style scoped>
.interest__btn {
font-family: Arial;
font-size: 1em;
background: white;
color: #333333;
border: 1px solid #0091da;
text-transform: none;
}
.interest__btn:hover {
color: black;
background-color: rgba(172, 196, 221, 0.7);
}
.interests__content {
padding: 1.7em;
}
.blue {
background-color: #0091da;
color: white !important;
text-transform: none;
}
</style>

How can I save the order of the tree?

It's a little bit complicated to explain. I would like to save the position on which the tree has been repositioned, and then when the user opens the page again it will appear the way the user left it (I do not want to make it only for one user but for all, because only the admin is going to have access to it anyway). it sounds ununderstandable, that's why I made an example right below:
Simplifying: 1 - Get the order of the tree's elements that the user left
2 - Send it to my server as a text file (Probably Ajax)
Thus, when I reload the page or/and clean up the cache, it will still be in that position that I left. I want the "position" to be sent as a text file using ajax to my server. Is there a way to do it?
Thanks in advance.
<!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="./tree.jquery.js"></script>
<link rel="stylesheet" href="./jqtree.css">
<script src="./jquery-cookie/src/jquery.cookie.js"></script>
<style>
body{overflow-x:hidden}
#navdata{width:auto; height:auto; flex:1; padding-bottom:1px;}
#navgrid{width:50%; height:200px; overflow-x:hidden; overflow-y:scroll; border:solid 1px #79B7E7; background-color:white;}
#header{background-color: #79B7E7; width:100%; text-align: center;border: 1px solid white;}
.jqtree-element{background-color:#DDEBF7;border: 1px solid white;height:23px;color:red;}
.jqtree-tree .jqtree-title {color: black;}
ul.jqtree-tree ul.jqtree_common {margin-left: 0px;}
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 {background: #79B7E7;}
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));}
</style>
</head>
<body>
<div id="navgrid">
<div id="header">Header</div>
<div id="tree1"></div>
</div>
</body>
<script type="text/javascript">
var ExampleData = {};
ExampleData.data = [
{
label: 'node1', id: 1,
children: [
{ label: 'child1', id: 2 },
{ label: 'child2', id: 3 }
]
},
{
label: 'node2', id: 4,
children: [
{ label: 'child3', id: 5 }
]
}
];
ExampleData.getFirstLevelData = function(nodes) {
if (! nodes) {
nodes = ExampleData.example_data;
}
var data = [];
$.each(nodes, function() {
var node = {
label: this.label,
id: this.id
};
if (this.children) {
node.load_on_demand = true;
}
data.push(node);
});
return data;
}
ExampleData.getChildrenOfNode = function(node_id) {
var result = null;
function iterate(nodes) {
$.each(nodes, function() {
if (result) {
return;
}
else {
if (this.id == node_id) {
result = this;
}
if (this.children) {
iterate(this.children);
}
}
});
}
iterate(ExampleData.example_data);
return ExampleData.getFirstLevelData(result.children);
}
$('#tree1').tree({
data: ExampleData.data,
autoOpen: false,
dragAndDrop: true
});
</script>
</html>
I think earlier i got your question wrong. This will be your answer.
$(document).ready(function(){
//var data is a dynamic json file that should be created in the backend.
var data = [
{
label: 'node1', id: 1,
children: [
{ label: 'child1', id: 2 },
{ label: 'child2', id: 3 }
]
},
{
label: 'node2', id: 4,
children: [
{ label: 'child3', id: 5 }
]
}
];
$('#tree1').tree({
data: data,
autoOpen: true,
dragAndDrop: true
});
console.log($('#tree1').tree('toJson')); //This will give you the loading jqtree structure.
$('#tree1').bind(
'tree.move',
function(event) {
event.preventDefault();
// do the move first, and _then_ POST back.
event.move_info.do_move();
console.log($(this).tree('toJson')); //this will give you the latest tree.
// $.post('your_url', {tree: $(this).tree('toJson')}); //this will post the json of the latest tree structure.
}
);
});
Updating the Code with HTML/JS/PHP with
Folder Structure
Root Folder
stackoverflow-2.html
libs/jquery/jquery.js
libs/jqtree/tree.jquery.js
libs/jqtree/jqtree.css
scripts/stackoverflow-2.js //custom script created by me
json/stackoverflow-2.json //json file output to create the nodes and children.
php/stackoverflow-2.php //php commands to write.
stackoverflow-2.html //same as your reference. Changed only mapping of the library files.
<head>
<script type="text/javascript" src="libs/jquery/jquery.min.js"></script>
<script src="libs/jqtree/tree.jquery.js"></script>
<link rel="stylesheet" href="libs/jqtree/jqtree.css">
<script src="scripts/stackoverflow-2.js"></script>
<style>
body{overflow-x:hidden}
#navdata{width:auto; height:auto; flex:1; padding-bottom:1px;}
#navgrid{width:50%; height:200px; overflow-x:hidden; overflow-y:scroll; border:solid 1px #79B7E7; background-color:white;}
#header{background-color: #79B7E7; width:100%; text-align: center;border: 1px solid white;}
.jqtree-element{background-color:#DDEBF7;border: 1px solid white;height:23px;color:red;}
.jqtree-tree .jqtree-title {color: black;}
ul.jqtree-tree ul.jqtree_common {margin-left: 0px;}
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 {background: #79B7E7;}
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));}
</style>
</head>
<body>
<div id="navgrid">
<div id="header">Header</div>
<div id="tree1"></div>
</div>
</body>
stackoverflow-2.js
$(document).ready(function(){
$.ajax({ /*Makes a ajax call and gets the contents from the json file*/
method:"get",
url:"json/stackoverflow-2.json"
}).success(function(data){
$('#tree1').tree({
data: jQuery.parseJSON(data.tree),
autoOpen: true,
dragAndDrop: true
});
});
$('#tree1').bind(
'tree.move',
function(event) {
event.preventDefault();
// do the move first, and _then_ POST back.
event.move_info.do_move();
$.post('php/stackoverflow-2.php', {tree: $(this).tree('toJson')}); //this will post the json of the latest tree structure.
}
);
});
Initial stackoverflow-2.json
{
"tree": [
{
"label": "node1",
"id": 1,
"children": [
{
"label": "child1",
"id": 2
},
{
"label": "child2",
"id": 3
}
]
},
{
"label": "node2",
"id": 4,
"children": [
{
"label": "child3",
"id": 5
}
]
}
]
}
stackoverflow-2.php
<?php
file_put_contents("../json/stackoverflow-2.json", json_encode($_POST)); //calls the file and enters the new tree structure.
Code tested in my localhost.
Referring to the jqtree documentation you can have your code like this.
var lastOpenedByAUser = 0; // make a ajax call to get this value. This value is also stored in database or any file in your server end if the last user clicked another node.
$('#tree1').tree({
data: data,
autoOpen: lastOpenedByAUser //shall be 0 for node-1, 1 for node-2
});
Make sure, you run this $('#tree') code only after the code your ajax code is completed.

Categories