I'm new to JavaScript and REST, and I need to implement JSON as datasource to devextreme using knockout.js.
My problem is, that I can fetch the json, but it is not added to the datasource. I used console.log() for testing and noticed that the json is correctly loaded, but the datasource is empty (see comments in code below). How can I achieve the usage of my json as datasource?
Note: I used DevExtreme load JSON as datasource using knockout as base for getting my JSON-contents.
I have a sample JSON-File looking like this:
{
"ID":"3",
"content":
{
"ProdId":"000176491264",
"ProdDesc":"Sample 1",
"Type":"A",
}
}
And my current viewmodel looks like this:
MyApp.overview = function (params) {
"use strict";
var getData = function () {
var deferred = $.Deferred();
var xmlhttp = new XMLHttpRequest(), json;
xmlhttp.onreadystatechange = function() {
if(xmlhttp.readyState === 4 && xmlhttp.status === 200) {
json = JSON.parse(xmlhttp.responseText);
// prints needed content:
console.log(json.content);
deferred.resolve(json.content);
}
};
xmlhttp.open('GET', 'http://localhost:56253/test/3?format=json', true);
xmlhttp.send();
return deferred.promise();
};
var viewModel = {
overviewDatagridOptions: {
dataSource: getData(),
selection: {
mode: "single"
},
columns: [{
dataField: "ProdId",
caption: "ID"
}, {
dataField: "ProdDesc",
caption: "Description"
}, {
dataField: "Type",
caption: "Type"
}],
rowAlternationEnabled: true
},
// Returns {}
console.log("Datasource: " + JSON.stringify(viewModel.overviewDatagridOptions.dataSource));
return viewModel;
};
Edit: I changed my datasource to this:
dataSource: {
load: function (loadOptions) {
var d = new $.Deferred();
var params = {};
//Getting filter options
if (loadOptions.filter) {
params.filter = JSON.stringify(loadOptions.filter);
}
//Getting sort options
if (loadOptions.sort) {
params.sort = JSON.stringify(loadOptions.sort);
}
//Getting dataField option
if (loadOptions.dataField) {
params.dataField = loadOptions.dataField;
}
//If the select expression is specified
if (loadOptions.select) {
params.select= JSON.stringify(loadOptions.select);
}
//Getting group options
if (loadOptions.group) {
params.group = JSON.stringify(loadOptions.group);
}
//skip and take are used for paging
params.skip = loadOptions.skip; //A number of records that should be skipped
params.take = loadOptions.take; //A number of records that should be taken
var obj;
$.getJSON('http://localhost:56253/test/3?format=json', params).done(function (data) {
d.resolve(data);
});
//return obj;
return d.promise();
}, [...]
Based on the demo found here: https://www.devexpress.com/Support/Center/Question/Details/KA18955
Now, the output from the datasource is no longer empty, and looks like this:
Object
- load:(loadOptions)
- arguments:(...)
- caller:(...)
- length:1
- name:"load"
- prototype:Object
- __proto__:()
- [[FunctionLocation]]
- [[Scopes]]:Scopes[1]
- totalCount:(loadOptions)
- arguments:(...)
- caller:(...)
- length:1
- name:"totalCount"
- prototype:Object
- __proto__:()
- [[FunctionLocation]]
- [[Scopes]]:Scopes[1]
- __proto__:Object
Related
I have a custom SAPUI5 application that used SAPUI5 version 1.40.11.
Since this version has been removed from CDN recently, I had to use a long-term maintenance available compatible version for the app. It is - version - 1.38.55.
I'm having an issue with MessagePopoverItem as the subtitle property is not available for this version.
So I'm trying to customize the template of the MessagePopoverItem to display the subtitle field.
While I'm trying to do this via a custom control I received the following error as it looks for a renderer.js from CDN without looking at it from the local location.
Cannot load from
https://sapui5.hana.ondemand.com/1.38.55/resources/sap/m/MessagePopoverItemRenderer.js
This is my custom controller and it's renderer.js
And I followed the below link
TooltipBaseRenderer.js: 404 - NOT FOUND
sap.ui.define([
"sap/m/MessagePopoverItem",
"./CustomMessagePopupItemRender",
], function (MessagePopoverItem,CustomMessagePopupItemRender) {
"use strict";
return MessagePopoverItem.extend("demoapp.customControls.CustomMessagePopoverItem", {
renderer:CustomMessagePopupItemRender,
metadata : {
properties: {
subtitle: {
type: "string",
defaultValue: ""
}
}
},
onAfterRendering: function() {
if(MessagePopoverItem.prototype.onAfterRendering) {
MessagePopoverItem.prototype.onAfterRendering.apply(this, arguments);
}
//...check if there is a highlight and tooltip
if(this.getTitle() !== "") {
sapMLIBContent
var oHl = this.$().find(".sapMLIBContent");
var ss = oHl;
}
}
});
});
Renderer.js
sap.ui.define([
"sap/ui/core/Renderer",
], function(Renderer) {
"use strict";
return Renderer.extend("demoapp.customControls.CustomMessagePopupItemRender", {
apiVersion: 2,
render: function(renderManager, control) {
// Issue: render function is called twice.
// See: https://github.com/SAP/openui5/issues/3169
// Update: fixed since 1.88
const child = control.getAggregation("content");
if (child && child.isA("sap.ui.core.Control")) {
renderManager.openStart("div", control)
.accessibilityState(control, { role: "tooltip" })
.style("max-width", "95vw")
.style("width", control.getWidth())
.openEnd()
.renderControl(child)
.close("div");
} else {
renderManager.openStart("span", control)
.accessibilityState(control, { role: "tooltip" })
.style("max-width", "90vw")
.style("width", control.getWidth())
.style("padding", "0.5rem")
.class("sapThemeBaseBG-asBackgroundColor")
.openEnd()
.text(control.getText())
.close("span");
}
},
});
});
Is this possible to customize the item template of the MessagePopover?
TooltipBaseRenderer.js: 404 - NOT FOUND is a completely different issue than the issue in this question. The element sap.m.MessagePopoverItem which extends sap.ui.core.Item, is not a sap.ui.core.Control but an sap.ui.core.Element. It is not supposed to have a renderer by definition.
sap.ui.core.TooltipBase, on the other hand, is a sap.ui.core.Control. It's a different issue.
Instead, you'll have to:
Extend sap.m.MessagePopoverItem by the subTitle property but without a renderer since it's not a control.
Extend the control that actually renders items using the information from your extended MessagePopoverItem element.
I have managed to extend the sap.m.MessagePopoverItem as mentioned by #Boghyon Hoffmann.
Here's the answer
CustomMessagePopoverItem.js
sap.ui.define([
"sap/m/MessagePopoverItem",
], function (MessagePopoverItem) {
"use strict";
return MessagePopoverItem.extend("demoapp.customControls.CustomMessagePopoverItem", {
metadata : {
properties: {
subtitle: {
type: "string",
defaultValue: ""
}
}
}
});
});
CustomMessagePopover.js
sap.ui.define([
"sap/m/MessagePopover",
"sap/m/StandardListItem",
"sap/ui/core/IconPool",
"./CustomMessagePopoverItem"
], function (MessagePopover,StandardListItem,IconPool,CustomMessagePopoverItem) {
"use strict";
var MessagePopover = MessagePopover.extend("demoapp.customControls.CustomMessagePopover", {
metadata : {
aggregations: {
/**
* A list with message items
*/
items: {type: "demoapp.customControls.CustomMessagePopoverItem", multiple: true, singularName: "item"},
},
},
//Override following method to add the subtitle as the description of the Standard List Item
_mapItemToListItem :function (oMessagePopoverItem) {
if (!oMessagePopoverItem) {
return null;
}
var sType = oMessagePopoverItem.getType(),
oListItem = new StandardListItem({
title: oMessagePopoverItem.getTitle(),
icon: this._mapIcon(sType),
description :oMessagePopoverItem.getSubtitle(),
type: sap.m.ListType.Navigation
}).addStyleClass(CSS_CLASS + "Item").addStyleClass(CSS_CLASS + "Item" + sType);
oListItem._oMessagePopoverItem = oMessagePopoverItem;
return oListItem;
}
});
var CSS_CLASS = "sapMMsgPopover",
ICONS = {
back: IconPool.getIconURI("nav-back"),
close: IconPool.getIconURI("decline"),
information: IconPool.getIconURI("message-information"),
warning: IconPool.getIconURI("message-warning"),
error: IconPool.getIconURI("message-error"),
success: IconPool.getIconURI("message-success")
},
LIST_TYPES = ["all", "error", "warning", "success", "information"],
// Property names array
ASYNC_HANDLER_NAMES = ["asyncDescriptionHandler", "asyncURLHandler"],
// Private class variable used for static method below that sets default async handlers
DEFAULT_ASYNC_HANDLERS = {
asyncDescriptionHandler: function (config) {
var sLongTextUrl = config.item.getLongtextUrl();
if (sLongTextUrl) {
jQuery.ajax({
type: "GET",
url: sLongTextUrl,
success: function (data) {
config.item.setDescription(data);
config.promise.resolve();
},
error: function() {
var sError = "A request has failed for long text data. URL: " + sLongTextUrl;
jQuery.sap.log.error(sError);
config.promise.reject(sError);
}
});
}
}
};
});
Here I invoke the custom control from the controller
var oMessageTemplate = new demoapp.customControls.CustomMessagePopoverItem({
type: '{TYPE}',
title: 'My Title',
description: '{DESC}',
subtitle: '{DESC}'
});
var oMessagePopover2 = new demoapp.customControls.CustomMessagePopover({
items: {
path: '/Messages',
template: oMessageTemplate
}
});
I use SingleSort module with Gridx. I can't find (I already checked SingleSort documentation and found nothing) a way to react on sort event. I need info about which (and how) column is sorted. I know how get info about sorting (getSortData method) but i don't know how make react in moment sort is made. I can't made onRender-event function because after sorting i will send that info to webapi get new data and again render Grid so event will be triggered again.
define([
"dojo/Evented",
"dojo/_base/declare",
"dojo/store/Memory",
"gridx/core/model/cache/Sync",
"gridx/Grid",
"gridx/modules/ColumnResizer",
'gridx/modules/Focus',
'gridx/modules/RowHeader',
'gridx/modules/select/Row',
'gridx/modules/select/Column',
'gridx/modules/select/Cell',
"gridx/modules/SingleSort"
], function (Evented, declare, Memory, Cache, Grid, ColumnResizer, Focus, RowHeader, SelectRow, SelectColumn, SelectCell, Sort) {
return declare([Evented], {
_counter: 1,
_topOffset: 100,
constructor: function () {
},
initialize: function () {
this._initGrid();
this._resizeGridContainer();
},
clear: function () {
var store = new Memory({ data: [] });
this._grid.setStore(store);
this._counter = 1;
},
_initGrid: function () {
var _this = this;
var store = new Memory({
data: []
});
var structure = [
{
id: 'operationType', field: 'operationType', name: dojo.byId(this._operationTypeId).value
},
{
id: 'transportationType', field: 'transportationType', name: dojo.byId(this._transportationUnitTypeId).value
},
{
id: 'transportationTypeLength', field: 'transportationTypeLength', name: dojo.byId(this._transportationLengthId).value
}
]
this._grid = Grid({
id: this._gridId,
cacheClass: Cache,
store: store,
structure: structure,
modules: [
ColumnResizer,
Sort
],
selectRowTriggerOnCell: true
});
this._grid.placeAt(this._gridPlaceholderId);
this._grid.startup();
},
_resizeGridContainer: function () {
var _this = this;
var container = dojo.byId(this._gridContainerId);
var height = container.parentElement.clientHeight - this._topOffset;
require(["dojo/dom-style"], function (domStyle) {
domStyle.set(_this._gridContainerId, "height", height + "px");
})
},
});
});
you need to tap on to the gridx SingleSort module sort method like this
this._grid.connect( this._grid.Sort, 'sort', function(colId, isDescending, skipUpdateBody){
alert(colId+" "+isDescending+" "+skipUpdateBody);
});
this will trigger the event whenever a grid column is sorted. Hope this helps.
I am using the ZingChart library to graph results from an API call. When I pass in a normal array for the "values" field of the chart data object, everything works fine. However, when I pass in an array made from Object.keys(titleSet) (where titleSet is a normal Javascript object), the graph displays as follows:
Example Chart
As you can see, the x-axis is now labeled with numbers instead of the array of strings. But when I print out the the result of Object.keys(titleSet) vs. passing in a normal array, they both appear to be the same in the console. Can anyone help me figure out what I'm doing wrong?
//List of movies inputted by the user
var movieList = [];
var movieSet = {};
var IMDBData = {
"values": [],
"text": "IMDB",
};
var metascoreData = {
"values": [],
"text": "Metascore"
};
var RTMData = {
"values": [],
"text": "Rotten Tomatoes Meter"
};
var RTUData = {
"values": [],
"text": "Rotten Tomatoes User"
};
var chartData = {
"type":"bar",
"legend":{
"adjust-layout": true
},
"plotarea": {
"adjust-layout":true
},
"plot":{
"stacked": true,
"border-radius": "1px",
"tooltip": {
"text": "Rated %v by %plot-text"
},
"animation":{
"effect":"11",
"method":"3",
"sequence":"ANIMATION_BY_PLOT_AND_NODE",
"speed":10
}
},
"scale-x": {
"label":{ /* Scale Title */
"text":"Movie Title",
},
"values": Object.keys(movieSet) /* Needs to be list of movie titles */
},
"scale-y": {
"label":{ /* Scale Title */
"text":"Total Score",
}
},
"series":[metascoreData, IMDBData, RTUData, RTMData]
};
var callback = function(data)
{
var resp = JSON.parse(data);
movieSet[resp.Title] = true;
//Render
zingchart.render({
id:'chartDiv',
data:chartData,
});
};
Full Disclosure, I'm a member of the ZingChart team.
Thank you for updating your question. The problem is you have defined your variable movieSet before the variablechartData. When parsing the page, top down, it is executing Object.keys({}) on an empty object when creating the variable chartData. You should just directly assign it into your config later on chartData['scale-x']['values'] = Object.keys(moviSet).
var callback = function(data)
{
var resp = JSON.parse(data);
movieSet[resp.Title] = true;
//Render
zingchart.render({
id:'chartDiv',
data:chartData,
});
};
There is a problem with the above code as well. It seems you are calling render on the chart every time you call this API. You should have one initial zingchart.render() and then from there on out use our API. I would suggest setdata method as it replaces a whole new JSON packet or modify method.
I am making some assumptions on how you are handling data. Regardless, check out the following demo
var movieValues = {};
var myConfig = {
type: "bar",
scaleX:{
values:[]
},
series : [
{
values : [35,42,67,89,25,34,67,85]
}
]
};
zingchart.render({
id : 'myChart',
data : myConfig,
height: 300,
width: '100%'
});
var callback = function(data) {
movieValues[data.title] = true;
myConfig.scaleX.values = Object.keys(movieValues);
zingchart.exec('myChart', 'setdata', {
data:myConfig
})
}
var index = 0;
var movieNamesFromDB = ['Sausage Party', 'Animal House', 'Hot Rod', 'Blazing Saddles'];
setInterval(function() {
if (index < 4) {
callback({title:movieNamesFromDB[index++]});
}
},1000)
<!DOCTYPE html>
<html>
<head>
<!--Assets will be injected here on compile. Use the assets button above-->
<script src= "https://cdn.zingchart.com/zingchart.min.js"></script>
<script> zingchart.MODULESDIR = "https://cdn.zingchart.com/modules/";
</script>
<!--Inject End-->
</head>
<body>
<div id='myChart'></div>
</body>
</html>
If you noticed in the demo, the length of scaleX.values determines how many nodes are shown on the graph. If you change values to labels this wont happen.
I am trying to create an object that I can modify as per
Datatables - Data
I use the following code to create my object
function projectData(projid,projdesc,descdet){
this._projid = 'HTML1'
this._projdesc = 'HTML2'
for(var i=0;i<=7;i++){
this['_day'+i] = 'HTML3';
}
this.projid = function(){
return this._projid
}
this.projdesc = function(){
return this._projdesc
}
this.day0 = function(){ // More of these for day (0-7)
return this._day0
}
}
Then I use the following table initialization. (prjData is an array of New projectData objects)
var table = $('#table-ProjectHours').DataTable({
data: prjData,
"columns": [
{ data: 'projid',"visible": true},
{ data: 'projdesc',"width": "45%" },
{ data: 'day0',"orderDataType": "dom-text-numeric" },
{ data: 'day1',"orderDataType": "dom-text-numeric" },
{ data: 'day2',"orderDataType": "dom-text-numeric" },
{ data: 'day3',"orderDataType": "dom-text-numeric" },
{ data: 'day4',"orderDataType": "dom-text-numeric" },
{ data: 'day5',"orderDataType": "dom-text-numeric" },
{ data: 'day6',"orderDataType": "dom-text-numeric" },
{ data: 'day7',"orderDataType": "dom-text-numeric" }
]
});
I access my table through
var dto = $('#table-ProjectHours').DataTable().data();
I get my objects as seen here:
What I do not understand is why when I attempt to do dto[0].day0 I do not get _day0 --- I just get the function string.
I can access the data through _day0 but it seems wrong...
Try rows().data() https://datatables.net/reference/api/rows().data()
e.g.
var table = $('#example').DataTable();
var data = table
.rows()
.data();
alert( 'The table has '+data.length+' records' );
In order to set data in jquery-datatables you must use .row(index).data(obj) or .cell(selector).data(obj).
You can also use row(index).day0() and if you write a set portion of your method you can set the data like row(index).day0(thing_to_set) You must call row(index).invalidate after changing the data for it to appear correctly.
In this particular situation, .day0 must be called as .day0() since it is a method.
I have tried to reload the data populated by an ajax call but I cant get it to work, it shows the old data even after using the reload method. The thing is that if I change some variables to populate a different data and try to call the following code without refreshing the page it does not reload the updated data =/ Here is my code:
function populateDataGrid() {
$.ajaxSetup({async: false});
var gridinfo="";
$.post("lib/function.php",{activity: activity, shift: shift, date: date},
function (output){
gridinfo = JSON.parse(output);
});
$.ajaxSetup({async: true});
// INITIALIZING THE DATAGRID
var dataSource = new StaticDataSource({
columns: [
{
property: 'id',
label: '#',
sortable: true
},
{
property: 'date',
label: 'date',
sortable: true
},
....
],
formatter: function (items) {
var c=1;
$.each(items, function (index, item) {
item.select = '<input type="button" id="select'+c+'" class="select btn" value="select" onclick="">';
c=c+1;
});
},
data: gridinfo,
delay:300
});
$('#grid').datagrid({
dataSource: dataSource
});
$('#grid').datagrid('reload');
$('#modal-fast-appointment-results').modal({show:true});
}
I found a solution... I had to create a new DataSource (lets call it "AjaxDataSource") and add the ajax request functionality within the data constructor:
(function (root, factory) {
if (typeof define === 'function' && define.amd) {
define(['underscore'], factory);
} else {
root.AjaxDataSource = factory();
}
}(this, function () {
var AjaxDataSource = function (options) {
this._formatter = options.formatter;
this._columns = options.columns;
this._delay = options.delay || 0;
this._data = options.data;
};
AjaxDataSource.prototype = {
columns: function () {
return this._columns;
},
data: function (options, callback) {
var self = this;
setTimeout(function () {
var data;
$.ajax({
url: 'getdata.php',
type: 'POST',
data: 'param1:param1,param2,param2,...,paramN:paramN', // this is optional in case you have to send some params to getdata.php
dataType: 'json',
async: false,
success: function(result) {
data = result;
},
error: function(data){
//in case we want to debug and catch any possible error
// console.log(data);
}
});
// SEARCHING
if (options.search) {
data = _.filter(data, function (item) {
var match = false;
_.each(item, function (prop) {
if (_.isString(prop) || _.isFinite(prop)) {
if (prop.toString().toLowerCase().indexOf(options.search.toLowerCase()) !== -1) match = true;
}
});
return match;
});
}
var count = data.length;
// SORTING
if (options.sortProperty) {
data = _.sortBy(data, options.sortProperty);
if (options.sortDirection === 'desc') data.reverse();
}
// PAGING
var startIndex = options.pageIndex * options.pageSize;
var endIndex = startIndex + options.pageSize;
var end = (endIndex > count) ? count : endIndex;
var pages = Math.ceil(count / options.pageSize);
var page = options.pageIndex + 1;
var start = startIndex + 1;
data = data.slice(startIndex, endIndex);
if (self._formatter) self._formatter(data);
callback({ data: data, start: start, end: end, count: count, pages: pages, page: page });
}, this._delay)
}
};
return AjaxDataSource;
}));
After defining the new DataSource, we just need to create it and call the datagrid as usual:
function populateDataGrid(){
// INITIALIZING THE DATAGRID
var dataSource = new AjaxDataSource({
columns: [
{
property: 'id',
label: '#',
sortable: true
},
{
property: 'date',
label: 'date',
sortable: true
},
....
],
formatter: function (items) { // in case we want to add customized items, for example a button
var c=1;
$.each(items, function (index, item) {
item.select = '<input type="button" id="select'+c+'" class="select btn" value="select" onclick="">';
c=c+1;
});
},
delay:300
});
$('#grid').datagrid({
dataSource: dataSource
});
$('#grid').datagrid('reload');
$('#modal-results').modal({show:true});
}
So now we have our datagrid with data populated via ajax request with the ability to reload the data without refreshing the page.
Hope it helps someone!