Hi I'm having some major issues trying to understand how to datatables to work with server side processing. For some background I'm using a service call Gamesparks to create the backend for a videogame and inside this service they have an implementation of mongodb.
I have an endpoint that fetchs all my users and I can see them in my table but the issue is that I fetch all of them, how can I achieve a pagination?. In the documentation they state that we must put serverSide to true but is not working. I really have no idea on how to proceed I need help.
Gamesparks event to fetch all users
require("LeaderboardMethods");
var playerList = Spark.runtimeCollection("playerList").find({},{"_id":0});
var finalData = [];
while(playerList.hasNext()){
var current = playerList.next();
var playerStats = Spark.runtimeCollection("playerStatistics").findOne({
"playerId":current.playerId
});
var loadedPlayer = Spark.loadPlayer(current.playerId);
var score = getScore(current.playerId);
if(loadedPlayer === null){
var toReturn = {
"playerId": current.playerId,
"displayName": current.displayName,
"email": "DELETED",
"rank": current.rank,
"coins": "DELETED",
"ban": "DELETED",
"score": score
}
finalData.push(toReturn);
} else{
var coins = loadedPlayer.getBalance("COIN");
var toReturn = {
"playerId": current.playerId,
"displayName": current.displayName,
"email": current.email,
"rank":playerStats.rank,
"coins": coins,
"ban": playerStats.isBlocked,
"score":score
}
finalData.push(toReturn);
}
}
Spark.setScriptData("playerList",finalData);
Datatables call
App.getUsers = function(){
var bodyData = {
"#class": ".LogEventRequest",
"eventKey": "GET_PLAYER_DATA",
"playerId": "MY_ID"
}
var table = $('#table1').DataTable({
"dom": "<'row be-datatable-header'<'col-sm-4'l><'col-sm-4'B><'col-sm-4'f>>" +
"<'row be-datatable-body'<'col-sm-12'tr>>" +
"<'row be-datatable-footer'<'col-sm-5'i><'col-sm-7'p>>",
"buttons": [
{
text: 'Edit',
action: function (e, dt, node, config) {
var sel_row = table.rows({
selected: true
}).data();
if (sel_row.length != 0) {
window.location.href = "edit-user.html";
localStorage.setItem("editUser", JSON.stringify(sel_row[0]));
}
}
},
{
text: 'Create',
action: function (e, dt, node, config) {
window.location.href = "create-user.html";
}
},
{
text: 'Delete',
className: 'delete-btn',
action: function (e, dt, node, config) {
var filtered = table.rows({
filter: 'applied',
selected: true
}).data();
// Only open modal when are users selected
if(filtered.length != 0){
$("#proceed-delete").prop('disabled', true)
$("#mod-danger-delete").modal();
if(filtered.length != 1) {
$('#length-users').append(document.createTextNode(filtered.length + " users"));
} else {
$('#length-users').append(document.createTextNode(filtered.length + " user"));
}
$("#delete-confirmation").change(function () {
if ($("#delete-confirmation").val() === "DELETE"){
$("#proceed-delete").prop('disabled', false)
$('#proceed-delete').on('click', function () {
if (filtered.length === 1) {
deleteUserRequest(filtered[0]);
} else {
for (let index = 0; index < filtered.length; index++) {
deleteUserRequest(filtered[index])
}
}
});
}
});
}
}
}, 'selectAll', 'selectNone'
],
"paging":true,
"pageLength":50,
"serverSide":true,
"ajax": {
"data": function (d) {
return JSON.stringify(bodyData);
},
"contentType": "application/json; charset=utf-8",
"url": config.REQUEST_API + '/rs/' + config.API_CREDENTIAL_SERVER + '/' + config.API_SERVER_SECRET + '/LogEventRequest',
"type":"POST",
"dataSrc":function(json){
console.log(json);
$('#loading-row').removeClass('be-loading-active');
return json.scriptData.playerList
},
},
"columns": [
{
data: null,
defaultContent: "<td></td>",
className: 'select-checkbox'
},
{ data: "playerId"},
{ data: "displayName" },
{ data: "email" },
{ data: "score"},
{ data: "rank" },
{ data: "isBlocked" },
{ data: "coins" },
{
"data": null,
"defaultContent": "<button class='btn btn-space btn-primary' onclick='App.click()'>View more</button>"
}
],
"select": {
style: 'multi',
selector: 'td:first-child'
},
}).on('error.dt', function(e, settings, techNote, message){
var err = settings.jqXHR.responseJSON.error;
// GS err
if(err === "UNAUTHORIZED"){
location.href = "pages-login.html";
return true;
} else{
$('#error-container-dt').show();
console.log(message);
return true;
}
});
}
Quick peek into Gamesparks SDK and found this for example:
ListTransactionsRequest
dateFrom Optional date constraint to list transactions from
dateTo Optional date constraint to list transactions to
entryCount The number of items to return in a page (default=50)
include An optional filter that limits the transaction types returned
offset The offset (page number) to start from (default=0)
Now, for paging you need entryCount and offset. First is size of one page, default 50, you can change it. Server returns 'entryCount' no of records.
Offset is the starting record. For example, initial list (1st page) does have 50 records, clicking "Next" button will send request "offset: 51" to the server. And server reply records from 50 (offset) to 100 (offset + entryCount).
var bodyData = {
"#class": ".LogEventRequest",
"eventKey": "GET_PLAYER_DATA",
"playerId": "MY_ID",
"entryCount": entryCount, // you have to send it if you dont like the default value
"offset": offset // gets his value from "NEXT" or "PREV" button
}
Thats how paging works. I'm not able to give more detailed answer as I dont use Gamesparks myself. Hope it gives you least some directon.
Related
I'm using jquery-ui to add autocomplete to an input field. I have essentially a two stage autocomplete that I'm trying to set up. Typing M will display an autocomplete of all options, selecting an option will enter that into the input EX: "machineName=", depending upon the first option selected, I then want to load a second autocomplete in the same field to show the values for that filter.
It works using static data, however the second autocomplete, is using API data so I have an AJAX call at the beginning of my script with a .then to chain together the creation of the autocomplete after the API has been hit, typing anything after machineName= results in nothing being displayed, however logging the value of the array I can see all the values in it.
var occupations = [{
value: "machineName=",
label: "machineName"
}, {
value: "ipAddress=",
label: "ipAddress"
},];
let machineNameAC = []
let switchTerm= [];
$.ajax({
url: '/api/data',
contentType: 'application/json',
dataType: 'json',
type: 'GET',
success: function(response){
console.log(response)
response.result.forEach((res) => {
machineNameAC.push(res.machineName)
console.log(machineNameAC)
})
}
}).then(() => {
$(function() {
function split(val) {
return val.split('=');
}
function extractLast(term) {
return split(term).pop();
}
$("#occupation").on("keydown", function(event) {
if (event.keyCode === $.ui.keyCode.TAB &&
$(this).autocomplete("instance").menu.active) {
event.preventDefault();
}
}).autocomplete({
minLength: 0,
source: function(request, response) {
var term = extractLast(request.term);
var results = [];
if (request.term.indexOf("=") > 0) {
var regE = /([^=]*)$/
if (request.term.endsWith('=')) {
console.log('term ',request.term)
switch (request.term){
case 'machineName=':
machineNameAC.forEach((machine)=>{
switchTerm.push(machine)
})
break;
}
}
console.log(switchTerm)
console.log(request.term)
if (parseInt(term) > 0) {
$.each(machineNameAC, function(k, v) {
console.log(k, v)
results.push(term + "" + v);
});
}
} else {
results = $.ui.autocomplete.filter(
occupations, request.term);
}
response(results);
},
focus: function() {
// prevent value inserted on focus
return false;
},
select: function(event, ui) {
var terms = split(this.value);
terms[0] = terms[0] + "=";
// remove the current input
terms.pop();
// add the selected item
terms.push(ui.item.value);
// add placeholder to get the comma-and-space at the end
terms.push("");
this.value = terms.join("");
return false;
}
});
});
})
Based on your code, your first stage determines if the user will look up a machine name versus an IP Address. Example:
machineName=descartes or ipAddress=192.168.1.112
The example data you provided was:
{
"userId": 1,
"id": 1,
"title": "delectus aut autem",
"completed": false
}
This data does not have a good relationship with the search in your initial code. I am assuming you have multiple items and you want to review the term against the title, yet this is not seen in your example. This is likely just sample data and does not represent your actual data. I must assume your data is more like:
[{
userId: 1001,
userName: "John Smith",
machineName: "jsmith-1",
ipAddress: "192.168.1.112",
siteLocation: "Shipping Bay 1"
}, {
userId: 1002,
userName: "Bettie Page",
machineName: "bpage-1",
ipAddress: "192.168.1.169",
siteLocation: "Champagne Room"
}];
If this is the case, you can forgo the prefix and make it a single state lookup that examines the term and identifies a machine name (words) versus an IP address (numbers and dots) and supplies the correct data. It's your choice to have it be two stage or single stage. You will need a filter function for both types, regardless.
Something like this:
function filterMachine(term, myData) {
var results = [];
$.each(myData, function(key, val) {
if (val.machineName.toLowerCase().indexOf(term.toLowerCase()) != -1) {
results.push($.extend(val, {
label: val.machineName,
value: val.machineName
});
}
});
return results;
}
function filterIp(term, myData) {
var results = [];
$.each(myData, function(key, val) {
if (val.ipAddress.indexOf(term) == 0) {
results.push($.eaxtend(val, {
label: val.ipAddress,
value: val.ipAddress
}));
}
});
return results;
}
It's not clear from your post what you are then doing with this data once a user Selects the item. Maybe filtering a table or updating a form. I do suggest using minLength option, something like 2. In this example, they might enter 192 or des and this should be enough to logically determin what they are seeking.
Single Stage Example: https://jsfiddle.net/Twisty/60a1eLwz/32/
Your code will be a bit different as I am using the Echo feature of Fiddle, it does not use GET. Instead you POST data and it echos it back to the request.
JavaScript
$(() => {
var sampleData = [{
userId: 1001,
userName: "John Smith",
machineName: "jsmith-1",
ipAddress: "192.168.1.112",
siteLocation: "Shipping Bay 1"
}, {
userId: 1002,
userName: "Bettie Page",
machineName: "bpage-1",
ipAddress: "192.168.1.169",
siteLocation: "Champagne Room"
}];
function filterMachine(term, myData) {
var results = [];
$.each(myData, function(key, val) {
if (val.machineName.toLowerCase().indexOf(term.toLowerCase()) != -1) {
results.push($.extend(val, {
label: val.machineName,
value: val.machineName
}));
}
});
return results;
}
var apiData;
function filterIp(term, myData) {
var results = [];
$.each(myData, function(key, val) {
if (val.ipAddress.indexOf(term) == 0) {
results.push($.extend(val, {
label: val.ipAddress,
value: val.ipAddress
}));
}
});
return results;
}
$.ajax({
url: '/echo/json',
dataType: 'json',
type: 'POST',
data: {
json: JSON.stringify(sampleData)
},
success: function(response) {
apiData = response;
console.log('API Data ', apiData);
$("#inputField").on("keydown", function(event) {
if (event.keyCode === $.ui.keyCode.TAB &&
$(this).autocomplete("instance").menu.active) {
event.preventDefault();
}
}).autocomplete({
minLength: 2,
source: function(request, response) {
if (isNaN(request.term.replace(".", ""))) {
response(filterMachine(request.term, apiData));
} else {
response(filterIp(request.term, apiData));
}
},
focus: function() {
return false;
},
select: function(event, ui) {
$("#results").empty();
$.each(ui.item, function(key, value) {
$("#results").append(key + ": " + value + "<br />");
})
return false;
}
});
}
});
});
This is more similar to your code, where it collects all the data up front and it's in a variable. You could also just call the JSON content in the source upon each request. The only benefit is if there are frequent changes to the data, you will catch more of them. If you pull all the data when the page loads, and the user sits on the page for 1 or 2 minutes, the data source could potentially be updated and the User will not get that new data.
In my opinion, this is a more friendly user interface, the User enters what they are looking for without having to search twice. They get suggests and pull up the result. With the Two Stage, they have to make an initial selection and then search. If you still want two stage, I suspect you can see where you would inject your prefix code again and then on the second stage pass the term to the correct function and append the specific detail you need as the result.
Two Stage Example: https://jsfiddle.net/Twisty/60a1eLwz/46/
JavaScript
$(() => {
var sampleData = [{
userId: 1001,
userName: "John Smith",
machineName: "jsmith-1",
ipAddress: "192.168.1.112",
siteLocation: "Shipping Bay 1"
}, {
userId: 1002,
userName: "Bettie Page",
machineName: "bpage-1",
ipAddress: "192.168.1.169",
siteLocation: "Champagne Room"
}];
function filterMachine(term, myData) {
var results = [];
$.each(myData, function(key, val) {
if (val.machineName.toLowerCase().indexOf(term.toLowerCase()) != -1) {
results.push($.extend(val, {
label: val.machineName,
value: val.machineName
}));
}
});
return results;
}
function filterIp(term, myData) {
var results = [];
$.each(myData, function(key, val) {
if (val.ipAddress.indexOf(term) == 0) {
results.push($.extend(val, {
label: val.ipAddress,
value: val.ipAddress
}));
}
});
return results;
}
$("#inputField").on("keydown", function(event) {
if (event.keyCode === $.ui.keyCode.TAB &&
$(this).autocomplete("instance").menu.active) {
event.preventDefault();
}
}).autocomplete({
minLength: 0,
source: function(request, response) {
if (request.term.length <= 2) {
response([{
label: "machineName",
value: "machineName="
}, {
label: "ipAddress",
value: "ipAddress="
}]);
} else {
var terms = request.term.split("=");
console.log(request.term, terms);
$.ajax({
url: '/echo/json',
dataType: 'json',
type: 'POST',
data: {
json: JSON.stringify(sampleData)
},
success: function(data) {
if (terms[0] == "machineName") {
response(filterMachine(terms[1], data));
} else {
response(filterIp(terms[1], data));
}
}
});
}
},
focus: function() {
return false;
},
select: function(event, ui) {
var terms = this.value.split("=");
terms[0] = terms[0] + "=";
// remove the current input
terms.pop();
// add the selected item
terms.push(ui.item.value);
// add placeholder to get the comma-and-space at the end
terms.push("");
this.value = terms.join("");
return false;
}
});
});
i am trying to retrieving data by custom searching. my code work properly, but when i try to filter some data from the input box. the process, get stuck. Please can anyone tell me what I m forgotting ?
My JQuery
//this work fine retrieving data by only if i search using "Search box"
var dataTableInstance = $("#dataTable").DataTable({
bServerSide: true,
sAjaxSource: 'AccountingMovementsService.asmx/GetAccountingMovements',
"processing": true,
sServerMethod: 'POST',
columns: [
{
'data': 'Payment'
},
{
'data': 'Account',
},
{
'data': 'customer',
}
]
});
//here I make all input box under footer columns (work fine)
$('#dataTable tfoot th').each(function () {
var title = $(this).text();
$(this).html("<input type='text' placeholder='" + title + "' />");
});
//And here i get stuck processing ... and data not come
dataTableInstance.columns().every(function() {
var dataTableColumn = this;
$(this.footer()).find('input').on('keyup change', function () {
dataTableColumn.search(this.value).draw();
});
});
If i use NOT SERVER-SIDE all work fine
My c# code is
[WebMethod]
public void GetAccountingMovements(int iDisplayLength, int iDisplayStart, int iSortCol_0, string sSortDir_0, string sSearch)
{
int displayLength = iDisplayLength;
int displayStart = iDisplayStart;
int sortCol = iSortCol_0;
string sortDir = sSortDir_0;
string search = sSearch;
int filteredCount = 0;
var accountingTransactions = new List<AccountMovement>();
string cs = ConfigurationManager.ConnectionStrings["Sg4DevMaster"].ConnectionString;
using (SqlConnection conn = new SqlConnection(cs))
{
SqlCommand cmd = new SqlCommand("spGetAccountingTransactions", conn);
cmd.CommandType = CommandType.StoredProcedure;
cmd.Parameters.Add(new SqlParameter
{
ParameterName = "DisplayLength",
Value = displayLength
});
cmd.Parameters.Add(new SqlParameter
{
ParameterName = "DisplayStart",
Value = displayStart
});
cmd.Parameters.Add(new SqlParameter
{
ParameterName = "SortCol",
Value = sortCol
});
cmd.Parameters.Add(new SqlParameter
{
ParameterName = "SortDir",
Value = sortDir
});
cmd.Parameters.Add(new SqlParameter
{
ParameterName = "Search",
Value = search
});
conn.Open();
SqlDataReader rdr = cmd.ExecuteReader();
while (rdr.Read())
{
AccountMovement am = new AccountMovement();
filteredCount = Convert.ToInt32(rdr["totalCount"]);
am.payment = Convert.ToDouble(rdr["payment"]);
am.Account = Convert.ToDouble(rdr["account"]);
am.Customer = rdr["Customer"].ToString();
accountingTransactions.Add(am);
}
}
var result = new
{
iTotalRecords = GetAccountingMovementsTotalCount(),
iTotalDisplayRecords = filteredCount,
aaData = accountingTransactions
};
JavaScriptSerializer js = new JavaScriptSerializer();
Context.Response.Write(js.Serialize(result));
}
All work fine using "the main search input box" in the DataTable Plugin, but it get stuck when put some data in one of custom input box placed in the footer
Thank for you time!
If you go to my GitHub https://github.com/bindrid/DataTablesServerSide take a look at the c# classes. That is how I take the parameters provided by DataTable and turn it into a usable c# object.
Also listed there is my web method that uses those classes.
Below is the DataTables logic, including your search stuff and it all works.
var table = $('#example').DataTable({
"processing": true,
"serverSide": true,
"rowCallback": function (row, data) {
if ($.inArray(data.employeeId, selected) !== -1) {
table.row(row).select();
}
},
"infoCallback": function (settings, start, end, max, total, pre) {
var api = this.api();
var pageInfo = api.page.info();
return 'Page '+ (pageInfo.page+1) +' of '+ pageInfo.pages + " ";
},
rowId:"employeeId",
"createdRow": function (row, data, dataIndex) {},
"columns": [
{ "data": "name" },
{ "data": "position" },
{ "data": "office" },
{ "data": "extn" },
{ "data": "start_date" },
{ "data": "salary" }
],
"select":"multi",
"lengthMenu": [5, [10, 15, 25, 50, -1], [5, 10, 15, 25, 50, "All"]],
"pageLength": 5,
"ajax": {
contentType: "application/json; charset=utf-8",
url: "wsSample.asmx/GetDTDataUnserializedObject",
type: "Post",
data: function (dtParms) {
// notice dtParameters exactly matches the web service
return JSON.stringify({ dtParameters: dtParms });
},
// Data filter is a part of ajax and lets you look at the raw
dataFilter: function (res) {
// You probably know by know that WebServices
// puts your data in an object call d. these two lines puts it
// in the form that DataTable expects
var parsed = JSON.parse(res);
return JSON.stringify(parsed.d);
},
error: function (x, y) {
debugger;
console.log(x);
}
},
order: [[0, 'asc']]
});
// add search boxes to footer
$('#example tfoot th').each(function () {
var title = $(this).text();
$(this).html("<input type='search' placeholder='" + title + "' />");
});
//And here i get stuck processing ... and data not come
table.columns().every(function () {
var dataTableColumn = this;
$(this.footer()).find('input').on('keyup change', function () {
dataTableColumn.search(this.value).draw();
});
});
Ok, here are the Versions:
Kendo UI v2014.3.1119
AngularJS v1.3.6
jQuery v#1.8.1 jquery.com
The issue is the following: I have a kendo upload that should populate a grid after a excel file is read. I'm using Kendo UI Angular Directives. Here are some key pieces of code:
Upload Html
<input name="files"
type="file"
kendo-upload
k-async="{ saveUrl: '{{dialogOptions.ImportUrl}}', autoUpload: false, batch: true }"
k-options="{localization: {uploadSelectedFiles: '{{messages.Global_Button_Import}}'}}"
k-error="onImportError"
k-select="onSelect"
k-success="onImportSuccess" k-multiple="false" />
Grid Html
<div kendo-grid="grid" k-options="gridOptions" k-ng-delay="gridOptions" k-rebind="gridOptions" >
</div>
Key pieces on the controller
angular.module('global').controller('ImportResultsController', [
'$scope', 'BaseApi', 'ImportResultsService', 'GridUtil', '$http', '$q', '$timeout',
function ($scope, BaseApi, ImportResultsService, GridUtil, $http, $q, $timeout) {
$scope.gridOptions;
$scope.gridColumns;
$scope.results = new kendo.data.ObservableArray([]);
//These columns should come from the server, right now are harcoded
$scope.getGridColumns = function () {
$scope.gridColumns = [
{ field: "Zone", width: 70, title: "Zone", template: "" },
{ field: "Aisle", width: 70, title: "Aisle", template: "" },
{ field: "Rack", width: 70, title: "Rack", template: "" },
{ field: "Shelf", width: 70, title: "Shelf", template: "" },
{ field: "Bin", width: 70, title: "Bin", template: "" },
//{ field: "DateEffectiveFrom", width: 70, title: "Date" },
{ field: "BinStatus", width: 70, title: "BinStatus", template: "" }
];
}
$scope.getClientGridOptions = function(columns, data, pageSize) {
var gridOptions = {
sortable: true,
dataSource: {
data: data,
pageSize: pageSize
},
selectable: 'row',
columns: columns,
pageable: {
pageSize: pageSize,
refresh: false,
pageSizes: [10, 20, 30],
messages: $.kendoMessages
},
};
return gridOptions
}
$scope.onImportSuccess = function (e) {
var files = e.files;
if (e.operation == "upload") {
console.log(files);
if (e.XMLHttpRequest.response != "") {
var model = $.parseJSON(e.XMLHttpRequest.response); //This step does not fail, model return is always filled
$scope.results = new kendo.data.ObservableArray([]);
for (var i = 0; i < model.Data.length; i++) {
$scope.results.push(model.Data[i]);
}
$scope.gridOptions = $scope.getClientGridOptions($scope.gridColumns, $scope.results, 10);
//$scope.grid.dataSource.data($scope.results); //This does not work
$scope.isDataReady = true;
// $("#grid").data("kendoGrid").dataSource.data($scope.results) //This does not work
// $scope.grid.refresh(); //This does not work
$scope.$apply()
}
}
}
}]);
The issues vary. Sometimes I get the data bound until the second uploaded file, and after the third time, I start receiving 'Cannot read property 'get' of undefined ' errors. When this second error happens, the bind works, but the error is present. Do you have any idea of what could it be?
In case of you need the url for the upload, here's the method. Is an MVC .net application. Since I always get the response correctly and it's a Json Array, I believe there's no issue there, but here's anyways.
dialogOptions.ImportUrl = LoadImportedBinLocations
MVC Controller
[System.Web.Http.HttpPost]
public ActionResult LoadImportedBinLocations(IEnumerable<HttpPostedFileBase> files)
{
bool success = false;
var importHistory = new PartsImportHistory();
List<dynamic> data = null;
try
{
//int divisor = 0;
//var y = 5 / divisor;
if (files != null)
{
using (var client = new ServiceLocator<IPartsService>())
{
foreach (var file in files)
{
string extension = Path.GetExtension(file.FileName);
bool isExcelFile = extension == ".xls" || extension == ".xlsx";
if (isExcelFile)
{
var filePath = UploadAttachment(file);
//importHistory = client.Service.SavePartsImportHistory(new PartsImportHistory
//{
// CreatedByName = CurrentContext.UserDisplayName,
// CreatedByUserID = CurrentContext.UserId,
// PartsImportStatus = (int)DMSModel.EnumStore.PartsImportStatus.InProgress,
// FilePath = filePath
//});
data = new List<dynamic>
{
new { Zone = "A", Aisle = "02", Rack = "06", Shelf = "20", Bin = "D", DateEffectiveFrom = DateTime.UtcNow, BinStatus = "Unblocked", IsValid= true, ImportError ="" },
new { Zone = "B", Aisle = "02", Rack = "06", Shelf = "10", Bin = "D", DateEffectiveFrom = DateTime.UtcNow, BinStatus = "Blocked", IsValid=false, ImportError="Zone does not Exist" }
};
success = true;
}
else
{
throw new Exception(WarpDMS.Globalization.Resources.PartsAdmin_ImportParts_ErrorFileFormat);
}
}
}
}
return Json(new { success = success, Data = data });
}
catch (Exception ex)
{
return Content(ex.Message ?? ex.ToString());
}
}
private string UploadAttachment(HttpPostedFileBase item)
{
string path = string.Empty;
string internalFileName = string.Empty;
string basePath = ConfigManager.ATTACHMENTS_PATH;
if (item != null && item.ContentLength > 0)
{
internalFileName = System.IO.Path.ChangeExtension(Guid.NewGuid().ToString(), System.IO.Path.GetExtension(item.FileName));
path = Path.Combine(basePath, internalFileName);
item.SaveAs(path);
}
return Path.Combine(basePath, internalFileName).Replace('\\', '/');
}
I have been reading ALL of the documentation on this and I still cannot get it to work.
I have a Web API which provides a JSON object. It's a list of 22 things. Just 22 lines of text.
I want to take these and form a TreeView. Each of these 22 strings will have items under them but I just want to get the first part working.
My first question is, how do I extract data from an API and populate a treeView with it?
On my main page, I have this:
<div id="treeView"></div>
On my JavaScript file I have this:
$("#treeView").kendoTreeView({
checkboxes: true,
dataSource: {
transport: {
read: {
url: "http://...",
dataType: "json"
}
}
}
});
When I try to run the page, I get "Request failed." [Retry]
If I open up a browser and go to this URL, data is returned fine as a JSON object.
What am I doing wrong here?
EDIT -
Code that is returning the JSON:
public List<string> getNotificationByUser(int id)
{
List<string> notificationTitles = new List<string>();
foreach (var notification in notifications)
{
notificationTitles.Add(notification.ToString());
}
return notificationTitles;
}
Ok! I've been able to reproduce your error. The question is that 22 lines of text are not a valid JSON.
Returning something like:
This
is
a
test
Is not a valid JSON.
But a valid JSON is not enough, you should return something like this:
[
{ "text": "This" },
{ "text": "is" },
{ "text": "a" },
{ "text": "test" }
]
I.e.: The result should be an array of objects where each object has a text field.
NOTE I know that it does not have to be called text but for simplicity I used it since it is the default value.
I figured out all of my answers:
function CreateNotificationTree(userId)
{
debugger;
var data = new kendo.data.HierarchicalDataSource({
transport: {
read: {
url: "../api/notifications/byuserid/" + userId,
contentType: "application/json"
}
},
schema: {
model: {
children: "notifications"
}
}
});
$("#treeview").kendoTreeView({
dataSource: data,
loadOnDemand: true,
dataUrlField: "LinksTo",
checkboxes: {
checkChildren: true
},
dataTextField: ["notificationType", "NotificationDesc"],
select: treeviewSelect
});
function treeviewSelect(e)
{
var node = this.dataItem(e.node);
window.open(node.NotificationLink, "_self");
}
}
[HttpGet]
public List<Node> getNotifications(int id)
{
var bo = new HomeBO();
var list = bo.GetNotificationsForUser(id);
var notificationTreeNodes = (from GBLNotifications n in list
where n.NotificationCount != 0
select new NotificationTreeNode(n)).ToList();
var li = notificationTreeNodes.Select(no => new Node
{
notificationType = no.NotificationNode.NotificationType + " " + "(" + no.NotificationNode.NotificationCount + ")", notifications = bo.GetNotificationsForUser(id, no.NotificationNode.NotificationTypeId).Cast<GBLNotifications>().Select(item => new Notification
{
ID = item.NotificationId, NotificationDesc = item.NotificationDescription, Params = new List<NotificationParam>
{
new NotificationParam
{
ParamName = item.Param1, ParamVal = item.ParamValue1
},
new NotificationParam
{
ParamName = item.Param2, ParamVal = item.ParamValue2
},
new NotificationParam
{
ParamName = item.Param3, ParamVal = item.ParamValue3
},
new NotificationParam
{
ParamName = item.Param4, ParamVal = item.ParamValue4
},
new NotificationParam
{
ParamName = item.Param5, ParamVal = item.ParamValue5
},
},
ActionPageName = item.ActionPageName
}).ToList()
}).ToList();
li.ForEach(i => i.notifications.ForEach(x => x.SetNotificationLink()));
return li;
}
Im new to YUI3; Im trying to poll a datasource every 10 seconds to refresh a datatable. But with the code below it says there is 'No Data To Display'... Sorry for the large amount of code...
YUI().use("datatable", "datasource-get", "datasource-jsonschema", "datatable-datasource", "datasource-polling", "datasource-function", function (Y) {
var url = "http://query.yahooapis.com/v1/public/yql?format=json" +
"&env=store%3A%2F%2Fdatatables.org%2Falltableswithkeys",
query = "&q=" + encodeURIComponent(
'select * from local.search ' +
'where zip = "94089" and query = "pizza"'),
dataSource,
table;
dataSource = new Y.DataSource.Get({ source: url });
dataSource.plug(Y.Plugin.DataSourceJSONSchema, {
schema: {
resultListLocator: "query.results.Result",
resultFields: [
"Title",
"Phone",
{
key: "Rating",
locator: "Rating.AverageRating",
parser: function (val) {
// YQL is returning "NaN" for unrated restaurants
return isNaN(val) ? -1 : +val;
}
}
]
}
});
intervalId = dataSource.setInterval(10000, {
request : query,
callback: {
success: function (e) {
table.datasource.load(e.response);
},
failure: function (e) {
}
}
});
table = new Y.DataTable({
columns: [
"Title",
"Phone",
{
key: "Rating",
formatter: function (o) {
if (o.value === -1) {
o.value = '(none)';
}
}
}
],
summary: "Pizza places near 98089",
caption: "Table with JSON data from YQL"
});
table.plug(Y.Plugin.DataTableDataSource, { datasource: dataSource });
// This line works (but it doesnt poll)
//table.datasource.load({ request: query });
table.render("#pizza");
});
The line I am not sure about is...
success: function (e) {
table.datasource.load(e.response);
},
The following should fit your needs: http://developer.yahoo.com/yui/examples/datatable/dt_polling.html