Getting Current Data from KendoUI TreeView - json

I'm using a kendo UI tree with a remote data source from a JSON file.
I have a button on the tree page which gets the current data of the tree,sends it through a POST to a server and the server saves the current data to the JSON file so as the next time I reload the page,the changes I made will be kept.That's what I want to happen.
So I know the current data of the tree is in:
$("#treeview").data("kendoTreeView").dataSource.data()
Which means the data changes real time in there for example when someone drag and drops a node of the tree.
My problem starts when this data doesn't seem to change when I drag and drop nodes inside the tree,and only changes when I drag and drop a node on the root level of the tree and even then it doesn't do it correcly as the node should be moved in there as well but instead the node gets copied,leaving the past node in the tree as well...
For Example I have this tree:
If I make a drag and drop change like this:
And I send the data,save it and reload the change isn't made at all!
PS:Even when I view the current data after the change before sending it,I see that there is no change on the data at all even though I did a change visualy with a drag and drop.So it doesn't have to do with the sending,saving and the server.
On the other hand,if I make a change like this:
I can see in the current data that the moved node is added in the end of the data indeed but it is not deleted from it's initial position within the data!So if i send the current data to the server,save it and then refresh I get the result:
The code for viewing and sending the data is:
function sendData() {
var req = createRequest();
var putUrl = "rest/hello/treeData";
req.open("post", putUrl, true);
req.setRequestHeader("Content-type","application/json");
var dsdata = $("#treeview").data("kendoTreeView").dataSource.data();
alert(JSON.stringify(dsdata));
req.send(JSON.stringify(dsdata));
req.onreadystatechange = function() {
if (req.readyState != 4) {
return;
}
if (req.status != 200) {
alert("Error: " + req.status);
return;
}
alert("Sent Data Status: " + req.responseText);
}
}
Is this a Bug or am I doing something wrong?Has anyone been able to see the current data changing correctly on every drag and drop?

First and most important you have to use the latest version of KendoUI (Kendo UI Beta v2012.3.1024) still in beta but is where they have solved many problems.
Then, when you create the kendoTreeView you have to say something like:
tree = $("#treeview").kendoTreeView({
dataSource :kendo.observableHierarchy(data),
dragAndDrop:true
}).data("kendoTreeView");
Here the important is not using directly data array but wrapping it with kendo.observableHierarchy.
Then you will have the data updated with the result of drag & drops.

For me in addition to OnaBai answer I had to use the sync function on the save method. I am using Type Script.
this.treeData = new kendo.data.HierarchicalDataSource({
data: kendo.observableHierarchy([]),//Thanks OnaBai
schema: {
model: {
id: "MenuItemId",
children: "MenuItemChildren",
hasChildren: (e) => {
//this removes arrow next to items that do not have children.
return e.MenuItemChildren && e.MenuItemChildren.length > 0;
}
}
}
});
public save() {
this.treeData.sync().done(() => {
console.log("sync data");
var myType = this.treeData.view();
this.$http.post("/api/TreeViewPicker", myType)
.then((response) => {
});
});
}

Related

Is there a better way to get data from localStorage after storing the data without refreshing/reloading the page

I'm trying to make a button that could store the data to the localStorage. After storing the data, I want to get the data without refreshing/reloading the page
The way I use to fix this case is, I put 2 commands on a button. The first command is to store the data, and the second command is to get the data using $scope to make it easy to display it on the page
Here is my code
$scope.storeData = function(){
if(localStorage.getItem('value') === null){
// The value that will set to the localStorage
$scope.data = 'Selamat sore';
// To set the value on $scope.data to localStorage
localStorage.setItem('value', JSON.stringify($scope.data));
//To get the value and display it on the page
$scope.getData = JSON.parse(localStorage.getItem('value'));
}else{
$scope.getData = JSON.parse(localStorage.getItem('value'));
}
}
It's actually working, but maybe there is a better way to do this
Thanks
I find this cleaner, and simpler.
function MyController($scope) {
// if value is null a default 'Selamat sore' is set
$scope.data = localStorage.getItem('value') || 'Selamat sore';
$scope.saveData = function() {
localStorage.setItem('value', JSON.stringify($scope.data));
}
}

Tabulator - Cell.setValue creating recursive loop for custom formatter, table not saving data

I'll try to be as brief as possible as my situation is unique and I am a fairly new programmer.
TL:DR - Cannot get the row to update, table does not remember initial values when editing, cell.setValue() creates recursive loop, cell.getRow().update() seems to do nothing, row does not resize in the event of editing working (still does not hold on to previous values)), unsure if mutator or cellEdit() callback are potential solutions.
I have JSON data populating my table from an AJAX call to a Java backend. One of the columns can either be one item, a list of multiple items, or none at all. I am using a custom formatter to populate that cell with the list of items, followed by icons to remove those items...
Some text here X
Some other text X
And some more X
When clicking the X icon, it should remove the item, and when clicking the text, it will drop down a list to choose another option.
Some text here X
Some other text X
Added another line X <----removed the previous item and selected a new
The problem I am having is that the table does not continuously hold onto previous values nor concatenates them with new selections. I have written some code to take care of the concatenation issue, but it still does not save that info within the table. I have tried to use cell.setValue() but this creates a recursive loop as all my code is within the custom formatter. I have also tried cell.getRow().update() but this seems to do nothing.
Also, clicking into the drop down and clicking away without selecting a value will still send the previous value as though it was clicked again. I was able to get it to function once, but it only changed the most recent values and did not include the original ones and it did not resize the row to be smaller.
I am unsure if a custom mutator would solve my issue, or even how to get the mutator to interact with the formatter to update the cell every time a change has been made. I also have a cellEdited: function(){} callback that is going to be designed to take the entire row of information as a JSON object to save it within a DAO and considered that I might be able to solve the issue there?
Here is my code for that column. This currently will load the page with the initial design yet does not work with editing. "cell.setValue()" and "cell.getRow().update()" are currently not included because they weren't working properly...
title: "Groups",
field: "associatedGroups",
variableHeight: true,
headerFilter: true,
headerFilterPlaceholder: "Search by Group...",
formatter: function (cell, formatterParams, onRendered) {
var data = [];
var newValue = cell.getValue();
var oldValue = cell.getOldValue();
var newValueIsArray = $.isArray(newValue);
var oldValueIsArray = $.isArray(oldValue);
if (oldValue !== null && newValue !== null) {
if (!oldValueIsArray && newValueIsArray) {
data = data.concat(newValue);
data.unshift(oldValue);
} else if (oldValueIsArray && !newValueIsArray) {
data = data.concat(oldValue);
data.push(newValue);
} else {
data.push(oldValue);
data.push(newValue);
}
} else {
data = newValue;
}
<!-------------------Value of "data" used here to determine visual layout----------------->
},
editor: "select",
responsive:
0,
editorParams:
{
values: groupNames
}
,
sorter: "string",
width:
212
},

How to get client side data from Kendo Grid to controller

I am trying to get Kendo Grid data which is hydrated from client side to a MVC controller method. My view contains several single fields like name, date of birth etc and tabular field which I hooked with a Kendo Grid. Since its a new operation I have no data in the grid ( and other fields) and user enters them from client side.
I have no idea how to proceed on this. Ideally I would like to get this data to a list in my viewmodal. So that when the user hits save, I have all other data and the grid data coming into a controller method.
I am able to successfully bind a list with kendo grid and display it. I have very little experience on JavaScript and Kendo and web programming.
If any of you can point me to the right direction, sample code would be greatly appreciated.
$("#departmet").kendoGrid({
dataSource: dataSource,
height: 250,
scrollable: true,
sortable: true,
filterable: true,
pageable: {
input: true,
numeric: false
},
columns: [
"DepartmentName",
"SubDivision"
]
});
From experience I know their documentation is not easy to navigate. It seems there is the documentation and then the API. The API is usually what you will always want to find. What you will need is the information from here https://docs.telerik.com/kendo-ui/api/javascript/ui/grid. If I understand the question correctly. There are several ways you can achieve posting. You could make use of editor templates. Click the Open in Dojo to get an idea how it looks.
https://docs.telerik.com/kendo-ui/api/javascript/ui/grid/configuration/editable.template
With this you do not have to worry about modifying the data via javascript. Assuming your grid is surrounded with a form element it will get posted when submitted. Note paging is not accounted for here. Also, this method by default can auto post after each edit. If you don't want this behavior then you will have to have advanced knowledge of the API.....Correction on that last statement. The API is different when dealing with the data all on the client side. Click the Open in Dojo to see it all on the client side. If you are not wanting to use editor templates and want to manage the data editing yourself then you need to use the grid methods provided.
Once you have your grid created. To access the data source of the grid you will need to get the dataSource.
$('#departmet').data('kendoGrid').dataSource;
https://docs.telerik.com/kendo-ui/api/javascript/data/datasource
If you need to use a different data source(or change it) you can use the setDataSource method below(grid function).
https://docs.telerik.com/kendo-ui/api/javascript/ui/grid/methods/setdatasource
To add to the data source use the add function to add a new object.
$('#departmet').data('kendoGrid').dataSource.add({ id: 2, name: 'name'});
https://docs.telerik.com/kendo-ui/api/javascript/data/datasource/methods/add
It is important with kendo to ALWAYS use the methods provided to change the data source so that the proper events can fire to update the UI accordingly. This includes if you need to set a property on a specific data item. In that case you need to use the set method on the item itself.
After you are done modifying your data. Within javascript get the data and either create DOM elements within a form
//JQuery sudo code example
var data = $("#departmet").data("kendoGrid").dataSource.data();
var dataLen = data.length;
var myForm = $('#my-form'); //Already within DOM
for (var i = 0; i < dataLen; i++) {
var item = data[i];
var idEl = $('<input type="hidden" name="userData[' + i + '].id" />');
idEl.val(item.id);
var nameEl = $('<input type="hidden" name="userData[' + i + '].name" />');
nameEl.val(item.name);
myForm.append(idEl);
myForm.append(nameEl);
}
myForm.submit();
This assumes your controller function(??) on the backend is expecting an array of objects with the property name of userData.
Alternatively, you can post it via ajax. For example, the ajax jquery function. Passing your data as the data of the ajax call.
http://api.jquery.com/jquery.ajax/
Don't want to ramble. Let me know if you need more help.
SO won't let me comment yet so have to add another answer. You will not need to define the data source within the .NET code when dealing with client only data. Just use this.
.DataSource(dataSource => dataSource
.Ajax()
.ServerOperation(false)
)
If you will have data coming from the backend then you need to use the generic-less constructor and pass in the object else keep what you have.
Html.Kendo().Grid(Model.MyList)
However, if you are preprocessing some client data on the screen that you want to initialize then you will need to do this on ready. Don't worry about the schema part of the data source. It already knows this when you used the .NET MVC wrapper because you gave it the schema(type) via the generic or the parameter provided.
var initialDS= new kendo.data.DataSource({
data: [
{ ActionName: "Some Name", ActionType: "Some Type" }
]
});
$(document).ready(function () {
$('#docworkflow').data('kendoGrid').setDataSource(initialDS);
});
As I mentioned in the other answer. Use the data source functions for adding additional data to the data source. No need to setDataSource each time you want to add. Just
//Assuming you have 2 inputs on the screen the user is entering info into
var nameEntry = $('#action-name').val();
var typeEntry = $('#action-type').val();
$('#docworkflow').data('kendoGrid').dataSource.add({ ActionName: nameEntry , ActionType: typeEntry });
So after some efforts I come up with. But I don't know where to specify the
data in the html code. Is it possible this way?
#(Html.Kendo().Grid <DockData.Action> ()
.Name("docworkflow")
.Columns(columns =>
{
columns.Bound(e => e.ActionName);
columns.Bound(e => e.ActionType);
}).DataSource( **How do I load a script variable here***)
//This script variable should be fed to the above code.
This variable is populatedwhen the user adds data from the UI which works fine.
var dataSource = new kendo.data.DataSource({
data: result,
schema: {
model: {
fields: {
ActionName: { type: "string" },
ActionType: { type: "string" }
}
}
},
pageSize: 20
});

Problems trying to display JSON data in a django-tables2 table

I have django-tables2 set up and working well. I have set my table to be able to update checkbox columns directly from the displayed list. However when my displayed table paginates and I update a value it refreshes the entire page thus sending me back to the first page and I then have to click 'next' to get back to where I was. So I thought it might be a good idea to throw knockout.js into the mix to bind my individual columns to the corresponding data in my postgres database. According to the blurb this would allow me to simply refresh the item clicked on without having to refresh the entire page. I read the tutorial for knockout.js and all seems great and exactly what I am looking for. I've modified my views and written my js file etc and I am almost there. I have the JSONResponse from my views.py returning the correct number of rows, however, my django-tables2 tables are rendering each record as a header (ie th) in my table instead of the data as a row (ie td). Feeling like I've fallen at the last hurdle, I was wondering if anyone can shed any light on how I can fix this last bit of the puzzle please.
view.py
def mydatalist(request):
data = []
user = get_current_user()
query = Q(user_fkey=user.id)
query.add(Q(deleted__isnull=True), Q.AND)
query.add(Q(master=True), Q.AND)
tasks = Task.objects.filter(query)
for task in tasks:
data.append({"code":task.code, "name":task.name, etc})
return JsonResponse(data, safe=False)
my .js file
function Task(data) {
this.code = ko.observable(data.code);
this.name = ko.observable(data.name);
etc
}
function TaskListViewModel() {
// Data
var self = this;
self.tasks = ko.observableArray([]);
$.getJSON('http://myweb.org/tasks/mydatalist/', function (data) {
if(data){
var mappedTasks = $.map(data, function (item) {
return new Task(item);
});
} else {
alert('data empty!');
}
self.tasks(mappedTasks);
});
}
ko.applyBindings(new TaskListViewModel());
my django-tables2 tables.py file
class MasterTable(ColumnShiftTable):
code = tables.Column(attrs={'th':{'class':'centered nodec'}})
name = tables.LinkColumn(attrs={'th':{'class':'centered nodec'}})
etc
class Meta:
model = Task
fields = ('code','name', etc)
template_name = 'django_tables2_column_shifter/bootstrap3.html'
attrs={'id':'masterlist', 'class': 'table table-noborder', 'data-bind': 'foreach: tasks, visible: task().length > 0'}
row_attrs={'id': lambda record: record.pk}
So basically everything is kind of working except that when rendered, my django-tables2 table is rendering 11 headers and no data rows instead of 1 header and 10 data rows.
If anyone can shed any light I really would appreciate it or alternatively if someone can suggest another way to achieve not having to refresh the entire page each time, that would be great also.

Firebase on('child_added' is adding more unwanted childs / .once returns only the last one in the array

Working on a node script to automatically call Google's pagespeed api upon giving the sites listed in a json file.
{
"1" : "https://bitbucket.org",
"2" : "https://www.catswhocode.com/blog/"
}
The aim is to add the results for the api call to firebase json database along with a top-level url node to store the firebase key for the sitestats entered. Here is example firebase data structure I need.
sites
-KrehhWxld7XlKCuFSHRY
stats { ... }
url: "https://bitbucket.org"
-KrehhXAWlYdjAOA9sd95
stats { ... }
url: "https://stackoverflow.com"
urls :
393957be871e209a76e0dc5df1f526ec : -KrehhWxld7XlKCuFSHRY
7f4c919540be6ec81cd37d9e61da6c37 : -KrehhXAWlYdjAOA9sd95
Within a promise I am adding a reference for the nodes in firebase json database.
Promise.all(allRes).then( function( values ) {
var ref = db.ref();
var sitesRef = ref.child("sites");
var urlsRef = ref.child("urls");
var psiresults = {};
SitesArray.map(function (sites, index) {
psiresults[sites] = values[index]
desktopStats = values[index].desktopStats;
mobileStats = values[index].mobileStats;
sitesRef.push({
url: sites,
stats: metricsResponse
});
sitesRef.once('child_added', function(snapshot) {
console.log(snapshot.key) //returns the last key only
});
});
When using once('child_added', ... it adds to the url top level node only for the very last item. However, if on('child_added', ... multiple copies of the same data and other childs are added. Not sure how to add the exact child key to the urls top level node when a child is added to sites node each time.
If you want to process all URLs once, use once('value' with snapshot.forEach():
sitesRef.once('value', function(snapshot) {
snapshot.forEach(function(child) {
console.log(child.key);
});
});
Finally, I had to do it using..
sitesRef.on('value', function(snapshot) {
...
});
This returns all the children and then I am able to filter the result based on my SitesArray values. I don't really find this an appropriate solution, however, I was able to make it work.