Custom control Openui5 - sapui5

sap.ui.core.Element.extend("custom.barNlineChartControl", { metadata : {
properties : {
"Job" : {type : "string", group : "Misc", defaultValue : null},
"Threshold" : {type : "int", group : "Misc", defaultValue : null},
}
}});
sap.ui.core.Control.extend("control.barNlinechart", {
/* the control API */
metadata : {
aggregations : {
"items" : { type: "custom.barNlineChartControl", multiple : true, singularName : "item"}
},
events: {
"select" : {},
"selectEnd": {}
}
},
//D3 Code below:
onAfterRendering: function() {
var that = this;
/* get the Items aggregation of the control and put the data into an array */
var aItems = this.getItems();
var data = [];
for (var i=0;i<aItems.length;i++){
var oEntry = {};
for (var j in aItems[i].mProperties) {
oEntry[j]=aItems[i].mProperties[j];
}
data.push(oEntry);
}
alert(JSON.stringify(data));
Code of view & control
multiBarLineGraph = new control.barNlinechart({
layoutData: new sap.ui.layout.GridData({span: "L12 M12 S12"}),
items: {
path : "/genericData",
template : new custom.barNlineChartControl({Job:"{Job}",Threshold:"{Threshold}"}),
}
}),
var multiBarData = {
"genericData":[
{
"Job": "Doctor",
"Threshold": 45,
"Hospital1": 30,
"Hospital2": 100,
"Hospital3": 90,
},
{
"Job": "Teacher",
"Threshold": 65,
"School1": 60,
"School2": 75,
},
]};
When the alert in d3 code executes I get Job & Threshold but other data from JSON array are missing which is obvious as the properties set here only accept job and threshold. As the JSON is dynamic how to write custom control so that I can pass the complete data to control everytime no matter how dynamic the data be.

You could use type: "any" for your items and dont use the element custom.barNlineChartControl at all:
Edit: as an aggregation controls the lifetime of the aggregated objects you have to use a property in this case.
sap.ui.core.Control.extend("control.barNlinechart", {
/* the control API */
metadata : {
properties : {
"items" : { type: "any" }
},
events: {
"select" : {},
"selectEnd": {}
}
},
and then in your view:
multiBarLineGraph = new control.barNlinechart({
layoutData: new sap.ui.layout.GridData({span: "L12 M12 S12"}),
items: { path : "/genericData" }
}),
this.getItems() would return an array of whatever has been been set / bound.

Related

Group a list of providers by their type using pug

I'm trying to group a list of users based on their specialties. For example: I want to group all Family Medicine providers and display their names:
Family Medicine:
- List item
- List item
- List item
This is my js controller:
exports.provider_list = function(req, res, next) {
provider.find()
.sort([['SpecialtyName', 'ascending']])
.exec(function (err, list_providers) {
if (err) { return next(err); }
//Successful, so render
res.render('provider_list', { title: 'Provider List', list_providers: list_providers});
});
};
Pug list:
extends layout
block content
h1= title
ul.list-group
each val in list_providers
li
a(href=val.url) #{val.SpecialtyName}
| #{val.ProviderName}
else
li There are no provider.
As I understand , you want to list all provider names grouped by specialty names.
And I guess your data (list_providers) looks like that :
[{
"ProviderName": "P1",
"SpeacialyName": "S1",
"url" : "url_1"
}, {
"ProviderName": "P2",
"SpeacialyName": "S2"
}, {
"ProviderName": "P3",
"SpeacialyName": "S3"
}, {
"ProviderName": "P3",
"SpeacialyName": "S1"
}, {
"ProviderName": "P4",
"SpeacialyName": "S2"
}]
If your data is like above. You can modify your data convert it to like this :
[{
"SpeacialyName": "S1",
"url": "url_1",
"ProviderNames": ["P1", "P3"]
}, {
"SpeacialyName": "S2",
"ProviderNames": ["P2", "P4"]
}, {
"SpeacialyName": "S3",
"ProviderNames": ["P3"]
}
]
And here is convertion code for backend :
//Successful, so render
var providers = {}
for (var i = 0; i < list_providers.length; i++) {
var item = list_providers[i];
if (!providers[item.SpeacialyName]) {
providers[item.SpeacialyName] = item;
providers[item.SpeacialyName].ProviderNames = [item.ProviderName];
} else {
providers[item.SpeacialyName].ProviderNames.push(item.ProviderName)
}
delete providers[item.SpeacialyName].ProviderName;
}
//convert object to array
var providersArray = [];
for (const item in providers) {
providersArray.push(providers[item])
}
res.render('provider_list', { title: 'Provider List', list_providers: providersArray });
Finally, here is pug file to list
ul.list-group
each val in list_providers
li
a(href=val.url) #{val.SpeacialyName}
ul
each name in val.ProviderNames
li
a(href="")=name
else
li There are no speacialy.
else
li There are no provider.

dynamic data is fetched through Js-grid, but filtering is not working?

Here is my JS code for JS Grid
$(function() {
$.ajax({
type : "GET",
url : "/Final/Reports?value=2016-03-03&value2=2017-03-03"
}).done(function() {
$("#jsGrid").jsGrid({
height : "auto",
width : "100%",
filtering: true,
sorting : true,
paging : true,
autoload : true,
pageSize : 3,
controller : {
loadData : function(filter) {
return $.ajax({
type : "GET",
url : "/Final/Reports?value=2016-03-03&value2=2017-03-03",
data : filter
});
},
},
fields : [ {
name : "patientId",
type : "text",
width : 150
}, {
name : "patientName",
type : "text",
width : 150
}, {
name : "genderId",
type : "number",
width : 150
}, {
name : "mobile",
type : "number",
width : 150
}, {
type : "control"
} ]
});
});
});
I am new to JS grid and I fetched the data using servlet and it is shown in the grid. But I don't know how to filter the data.
Any ideas?
Client-side filtering and server-side filtering are completely on
shoulders of developer. Client-side filtering implemented in loadData
method of controller. Server-side apparently implemented with server
script that receives filtering parameters, and uses them to fetch
data, and pass to client.
That's why you can use client-side and
server-side filtering at the same time. Here is how your
controller.loadData method could look like in this case:
loadData: function(filter) {
var d = $.Deferred();
// server-side filtering
$.ajax({
type: "GET",
url: "/items",
data: filter,
dataType: "json"
}).done(function(result) {
// client-side filtering
result = $.grep(result, function(item) {
return item.SomeField === filter.SomeField;
});
d.resolve(result);
})
return d.promise();
}
The source issue: https://github.com/tabalinas/jsgrid/issues/32

Setting params in Kendo UI Grid when calling a rest service [Workaround]

I have a Kendo UI Grid that is calling a rest service. It works fine, as long as I do not try to use any params.
I know the the rest service is correct, as I can call it from a browser, and get correct results [depending on the param I send]. Also, when I look the server log I see that it is calling the rest service with no params.
My code is below:
document).ready( function() {
var crudServiceBaseUrl = "rsPC.xsp",
dataSource = new kendo.data.DataSource({
transport: {
read: {
url: crudServiceBaseUrl + "/PCByStatus",
filter: {field: "status", value: "2" }
dataType: "json",
update: {
url: crudServiceBaseUrl + "/PC/Update",
dataType: "json"
},
destroy: {
url: crudServiceBaseUrl + "/PC/Destroy",
dataType: "json"
},
create: {
url: crudServiceBaseUrl + "/PC/Create",
dataType: "json"
},
parameterMap: function(options, operation) {
if (operation !== "read" && options.models) {
return {models: kendo.stringify(options.models)};
}
}
},
batch: true,
pageSize: 20,
scrollable: {
virtual: true
},
height: 543,
schema: {
model: {
id: "PCId",
fields: {
PCId: {type:"string"},
serialNumber: {type: "string"},
officeLoc: {type: "string"},
unid: {type:"string"},
model: {type:"string"},
checkInDate: {type: "string"}
}
}
}
});
// Grid
grid = $("#grid").kendoGrid( {
dataSource: dataSource,
columns : [ {
field : "serialNumber",
title : "Serial Number"
}, {
field : "model",
title : "Model"
}, {
field : "officeLoc",
title : "Office Location"
}, {
field : "checkInDate",
title : "Check In Date",
template: "#= kendo.toString(kendo.parseDate(checkInDate, 'yyyy-MM-dd'), 'MM/dd/yyyy') #"
} ],
pageable: {
refresh: true,
pageSizes: true,
buttonCount: 5
},
dataBound : addExtraStylingToGrid,
reorderable : true,
filterable : true,
scrollable : true,
selectable : true,
sortable : true,
});
I still cannot get this to work and am a bit stumped.
I have two rest services, one returns all data, one takes "status" as a part and return a subset of the data that equals the parm.
The URL is:
http://localhost/scoApps/PC/PCApp.nsf/rsPC.xsp/PCByStatus?status=2
When entered into browser I get the correct number of records.
So I changed the code (see below). I have included all of the code for the CSJS:
$(document).ready( function() {
// Double Click On row
$("#grid").on(
"dblclick",
" tbody > tr",
function() {
var grid = $("#grid").data("kendoGrid");
var row = grid.dataItem($(this));
window.location.replace("xpFormPC.xsp" + "?key=" + row.unid + "target=_self");
});
// Add hover effect
addExtraStylingToGrid = function() {
$("table.k-focusable tbody tr ").hover( function() {
$(this).toggleClass("k-state-hover");
});
};
// Search
$("#search").keyup( function() {
var val = $('#search').val();
$("#grid").data("kendoGrid").dataSource.filter( {
logic : "or",
filters : [ {
field : "serialNumber",
operator : "contains",
value : val
}, {
field : "officeLoc",
operator : "contains",
value : val
}, {
field : "model",
operator : "contains",
value : val
} ]
});
});
var crudServiceBaseUrl = "rsPC.xsp",
dataSource = new kendo.data.DataSource({
transport: {
read: {
url: crudServiceBaseUrl + "/PCByStatus",
dataType: "json"
},
update: {
url: crudServiceBaseUrl + "/PC/Update",
dataType: "json"
},
destroy: {
url: crudServiceBaseUrl + "/PC/Destroy",
dataType: "json"
},
create: {
url: crudServiceBaseUrl + "/PC/Create",
dataType: "json"
},
parameterMap: function(options, operation) {
if (operation == "read"){
options.field = "status"
options.value = "2"
return options;
}
if (operation !== "read" && options.models) {
return {models: kendo.stringify(options.models)};
}
}
},
batch: true,
pageSize: 20,
scrollable: {
virtual: true
},
height: 543,
schema: {
model: {
id: "PCId",
fields: {
PCId: {type:"string"},
serialNumber: {type: "string"},
officeLoc: {type: "string"},
unid: {type:"string"},
model: {type:"string"},
checkInDate: {type: "string"}
}
}
}
});
// Grid
grid = $("#grid").kendoGrid( {
dataSource: dataSource,
columns : [ {
field : "serialNumber",
title : "Serial Number"
}, {
field : "model",
title : "Model"
}, {
field : "officeLoc",
title : "Office Location"
}, {
field : "checkInDate",
title : "Check In Date",
template: "#= kendo.toString(kendo.parseDate(checkInDate, 'yyyy-MM-dd'), 'MM/dd/yyyy') #"
} ],
pageable: {
refresh: true,
pageSizes: true,
buttonCount: 5
},
dataBound : addExtraStylingToGrid,
reorderable : true,
filterable : true,
scrollable : true,
selectable : true,
sortable : true
});
// Edit
function onEdit(e) {
}
// Change
function onChange(args) {
var model = this.dataItem(this.select());
ID = model.ID;
}
;
});
What am I doing wrong?
=========================================
I have a workaround. Or possibly this is the way it is supposed to be done.
var crudServiceBaseUrl = "rsPC.xsp", dataSource = new kendo.data.DataSource(
{
transport : {
read : {
url : crudServiceBaseUrl
+ "/PCByStatus?status=2",
dataType : "json"
},
Now I just construct the URL I want. Not so elegant I suppose, but it works.
I have a workaround. Or possibly this is the way it is supposed to be done.
var crudServiceBaseUrl = "rsPC.xsp", dataSource = new kendo.data.DataSource(
{
transport : {
read : {
url : crudServiceBaseUrl
+ "/PCByStatus?status=2",
dataType : "json"
},
Filter is used for client side data unless you set serverFiltering to true.
Here is the filter kendo documentation and the serverFiltering documentation.
I use parameterMap when I need to send parameters that are not created by filtering the control that I'm using. The kendo documentation provides an example using parameterMap.
Here is an example of how I've used it in the past:
var appsDataSource = new kendo.data.DataSource({
transport: {
read: {
url: apiUrl + "App"
},
parameterMap: function (data, action) {
if (action === "read") {
data.lobid = lobId;
data.parent = isParent;
return data;
} else {
return data;
}
}
}
});
Try changing the parameterMap:
parameterMap: function(options, operation) {
if (operation == "read"){
options.field = "status";
options.value = "2";
return options;
}
if (operation !== "read" && options.models) {
return {models: kendo.stringify(options.models)};
}
}
and update the read definition to remove filter. One thing to consider is that you are not returning anything from the read method if it doesn't meet the criteria of not being a read and options is not null. That leaves out any other combination that isn't obviously handled in your existing code.

How to build an expandable sap.m.Table in sapui5?

I am searching for a kind of TreeTable that uses sap.m.Table as base.
My first "hack" looks like this:
Experimental data model:
var oModel = new sap.ui.model.json.JSONModel({
"Items" : [
{"Name" : "A", "SubItems" : [
{"Name" : "A.1" },
{"Name" : "A.2" },
{"Name" : "A.3" },
]
},
{"Name" : "B", "SubItems" : [
{"Name" : "B.1" }
]
},
]
});
this.getView().setModel(oModel, "expand");
Experimental Table implementation:
var oContent = new sap.m.Table({
items : {
path : "expand>/Items",
template : new sap.m.ColumnListItem({
customData : [ new sap.ui.core.CustomData({
key : "SubItems",
value : "SubItems",
customData : {
path : "expand>SubItems",
template : new sap.ui.core.CustomData({
key : this.createId("subItem"),
value : new sap.m.ColumnListItem({
cells : [
new sap.m.Text({
text: "{expand>Name}",
})
]
})
})
}
})
],
type : sap.m.ListType.Active,
cells: [
new sap.m.Text({ text: "{expand>Name}" }),
],
press : [function(oEvent) {
var oRow = oEvent.getSource();
var oTable = oRow.getParent();
var oItems = oTable.getItems();
var insertIndex = -1;
var oSubItemsData = undefined;
for (var i=0;i<oItems.length;i++) {
if (oItems[i]==oRow) {
oSubItemsData = oRow.getAggregation("customData").filter(function(oData) {return oData.getProperty("key") == "SubItems";});
insertIndex = i;
}
}
var oSubItems = oSubItemsData[0].getAggregation("customData").map(function(oData) {return oData.getValue();});
for (var j=0;j<oSubItems.length;j++) {
var mShownSubItems = oItems.filter(function(oShownItem) {
return oShownItem == oSubItems[j];
});
if (mShownSubItems.length>0) {
console.log("removing"+j);
oTable = oTable.removeItem(oSubItems[j]);
} else {
console.log("adding "+(insertIndex+j+1));
oTable = oTable.insertItem(oSubItems[j],insertIndex+j+1);
}
}
}, oController]
})
},
columns : [ new sap.m.Column({}) ],
});
I figured out different problems with this "hack".
First of all the binding of the SubItems is not displayed, if I use hard coded text the text is shown.
Second problem is, that I can only insert exactly one row.
How can this be solved?
You may be interested in the Table - Breadcrumb sample in Explored. It uses sap.m.Table as a base and can display a hierarchy of data, in a tree table kind of style.
I have bumped into the same issue and I've solved it by using a sap.m.Panel as a sap.m.CustomListItem aggregation of the sap.m.List. If you wish to expand it by clicking anywhere on the row (collapsed Panel) and not only on the Panel's expand Button, you could get the aggregation go trough a factory, which can do something like this
var oUIControl = this.byId("custItem").clone(sId);
var oPanel = oUIControl.getContent()[0];
oPanel.addEventDelegate({
onclick: function (e) {
try {
if (sap.ui.getCore().byId(e.target.id).getMetadata().getElementName() !== "sap.ui.core.Icon") {
oPanel.setExpanded(!oPanel.getExpanded());
}
} catch (e) {
oPanel.setExpanded(!oPanel.getExpanded());
}
}
});
return oUIControl;
The check is needed because without it the click event is fired twice when clicking on the expand button itself. With it we trigger custom click event when the user clicks anywhere else on the panel row.

Correct way to return from mongo to datatable

I'm using mongoose and returning documents from a collection to be displayed using datatables. I'm having some issues though. The client-side code is
var table = $('#dataTables-example').DataTable( {
"bProcessing" : true,
"bServerSide" : true,
"ajax" : {
"url" : "/mongo/get/datatable",
"dataSrc": ""
},
"columnDefs": [
{
"data": null,
"defaultContent": "<button id='removeProduct'>Remove</button>",
"targets": -1
}
],
"aoColumns" : [
{ "mData" : "name" },
{ "mData" : "price" },
{ "mData" : "category" },
{ "mData" : "description" },
{ "mData" : "image" },
{ "mData" : "promoted" },
{ "mData" : null}
]
});
Then this handled on the server-side using the following
db.once('open', function callback ()
{
debug('Connection has successfully opened');
productSchema = mongoose.Schema({
name: String,
price: String,
category: String,
description: String,
image: String,
promoted: Boolean
});
Product = mongoose.model('Product', productSchema, 'products');
});
exports.getDataForDataTable = function (request, response) {
Product.dataTable(request.query, function (err, data) {
debug(data);
response.send(data);
});
};
If I use the above code the datatable fails to display the documents, claiming no matching records found BUT it does correctly display the number of docs Showing 1 to 2 of 2 entries. If I change the server side code to response with data.data instead of data, the documents are correctly populated in the table BUT the number of records is no longer found, instead saying Showing 0 to 0 of 0 entries (filtered from NaN total entries)
exports.getDataForDataTable = function (request, response) {
Product.dataTable(request.query, function (err, data) {
debug(data);
response.send(data.data);
});
The actual data being returned when querying mongo is
{ draw: '1', recordsTotal: 2, recordsFiltered: 2, data: [ { _id: 5515274643e0bf403be58fd1, name: 'camera', price: '2500', category: 'electronics', description: 'lovely', image: 'some image', promoted: true }, { _id: 551541c2e710d65547c6db15, name: 'computer', price: '10000', category: 'electronics', description: 'nice', image: 'iamge', promoted: true } ] }
The third parameter in mongoose.model sets the collection name which is pluralized and lowercased automatically so it has no effect in this case.
Assuming your Product variable has been declared early on and global, try this:
products = mongoose.model('products', productSchema);
Product = require('mongoose').model('products');
Did you try to remove the dataSrc field in the DataTable configuration:
"ajax" : {
"url" : "/mongo/get/datatable",
},