Mapping XML child elements to a Kendo UI DataSource - kendo-ui

I'm struggling with mapping and displaying a list of child elements on a XML datasource, using Kendo UI Mobile.
Consider the following straightforward XML structure:
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<topics>
<topic id="1" title="Weather">
<header>Great weather today!</header>
<smallicon>foo_bar.png</smallicon>
<items>
<item>It's great weather</item>
<item>Sunny feeling</item>
<item>Raining like a dog</item>
</items>
</topic>
<topic id="2" title="Politics">
<header>Left or right, take your pick!</header>
<smallicon>whatever.png</smallicon>
<items>
<item>Making budget cuts</item>
<item>How important is healthcare?</item>
</items>
</topic>
</topics>
Reading each single topic, and binding it to a view template, is in fact quite easy. Like so:
var template = kendo.template($("#home-tpl").html());
// hook up to the datasource "change" event; for auto-population
dataSource.bind("change", function(e) {
$("#home-menu").html(kendo.render(template, this.view()));
});
var dataSource = new kendo.data.DataSource({
transport: {
read: {
url: "topics.xml",
dataType: "xml"
}
},
schema: {
type: "xml",
data: "/topics/topic",
model: {
fields: {
id: "#id",
title: "#title",
header: "header/text()",
smallicon: "smallicon/text()",
// > QUESTION: HOW TO MAP THIS?
items: "???"
}
}
}
dataSource.read();
This seems to blend just fine for the attributes and elements on the top level. I get a list of topics and I can bind them to a template using something like #: data.title #. This blends and no questions here.
However, I want to map the child elements for each <topic> as well. In the XML example this means the list of <items>. It's the "how-to-map-this-schema" part I'm puzzled on.
The eventual goal is to display a list of these sub-items, like for example in: #: data.items[0] #.
Also, I've tried a HierarchicalDataSource, but the regular DataSource seems to work just fine. Perhaps this would be a better fit? The Kendo API documentation doesn't seem to provide XML samples that have a nested hierarchy.
Any suggestions on how I can accomplish this?

After some trial and error I came up with the following solution:
schema: {
type: "xml",
data: "/topics/topic",
model: {
fields: {
id: "#id",
title: "#title",
header: "header/text()",
smallicon: "smallicon/text()",
// > ANWER: THIS IS HOW
children: "items"
},
hasChildren: "items"
}
}
Now there are two changes in this snippet in comparison to my original question:
The children: "items" node is added to the schema
The hasChildren: "items" property
With this in place, everything worked out well and the hierarchical structure was mapped just nicely. As a bonus, I'm now able to do the following:
// fetch the one, single topic from the datasource
topic = dataSource.Topics.get(topicId);
// read the inner contents, e.g. text, from a single <items> node
log(topic.children.item[0]["#text"]);
Perhaps it's of some help to others in the future...

Related

How to extend the NavigationListItem control in SAPUI5?

I want to extend the NavigationListItem control to accept sap.ui.core.Control objects in it's aggregation. I have created a separate file to extend the control and added a new aggregation called 'rows' with type sap.ui.core.Control in the metadata section. The extended control is getting called in the view, but it is not displaying any child controls which were added to the new aggregation 'rows'. Please advise, if I need to add anything more to the extension file.
Extension code:
sap.ui.define(["sap/ui/core/Control",
"sap/tnt/NavigationListItem"],
function(Control, NavigationListItem) {
"use strict";
return NavigationListItem.extend("ajacontrolExt.control.NavigationCustomListItem", {
metadata: {
properties: {
},
defaultAggregation: "rows",
aggregations: {
rows: {
type: "sap.ui.core.Control",
multiple: true,
singularName: "row"
}
}
}
});
});

Graphql relay array field

I am starting to develop for relay coming from Apollo.
I have a dumb server running on a SQLITE3 database just for testing while I am refactoring.
Using graphql-relay on the backen.
Currently I have something like this:
{
root: {
allFoo: [FooType]
}
}
I was wondering how I would add a new FooType item to the allFoo list.
On the getConfigs the RANGE_ADD only acts upon connections.
So do I need to make my allFoo type a connection instead of a GraphqlList(FooType) ? Or can I use FIELD_CHANGE somehow?
Take a look at this example:
https://github.com/bfwg/relay-gallery/blob/master/frontend/src/app/mutation/AddImageMutation.js#L47
The below example is a demo on how to add an image to the image list.
getConfigs() {
return [{
type: 'RANGE_ADD',
parentName: 'User',
parentID: this.props.images.id,
connectionName: 'images',
edgeName: 'newImageEdge',
rangeBehaviors: {
'': 'prepend',
},
}];
}
Hope this helps!

Kendo dropdownlist datatsource sorting

I have a kendo dropdown in my page using the following list as data source
[{FieldOne:'def',FieldTwo:'p',FieldThree:14},
{FieldOne:'ijk',FieldTwo:'p',FieldThree:14},
{FieldOne:'lmn',FieldTwo:'p',FieldThree:14},
{FieldOne:'bcd',FieldTwo:'',FieldThree:14}]
I need to sort this data source based on FieldOne ascending and FieldTwo descending
I'm using the following code to sort the data source
var dropdownlist = $("#dropdown").data("kendoDropDownList");
dropdownlist.dataSource.sort({ field: 'FieldOne', dir: 'asc' });
dropdownlist.dataSource.sort({ field: 'FieldTwo', dir: 'desc' });
Its working fine initially but if I add another object like {FieldOne:'abc',FieldTwo:'p',FieldThree:14} to the data source I'm getting the following result
[{FieldOne:'def',FieldTwo:'p',FieldThree:14},
{FieldOne:'ijk',FieldTwo:'p',FieldThree:14},
{FieldOne:'lmn',FieldTwo:'p',FieldThree:14},
{FieldOne:'abc',FieldTwo:'p',FieldThree:14},
{FieldOne:'bcd',FieldTwo:'',FieldThree:14}]
but I want the datasource sorted as
[{FieldOne:'abc',FieldTwo:'p',FieldThree:14},
{FieldOne:'def',FieldTwo:'p',FieldThree:14},
{FieldOne:'ijk',FieldTwo:'p',FieldThree:14},
{FieldOne:'lmn',FieldTwo:'p',FieldThree:14},
{FieldOne:'bcd',FieldTwo:'',FieldThree:14}]
I don't know how to achieve this I tried almost everything I know but still no result. Is there anyway that I can get the desired result???
You shoud define sorting in dataSource:
<input id="dropdownlist" />
<script>
var dropdownlist = $("#dropdownlist").kendoDropDownList({
dataSource:{
data:[{FieldOne:'def',FieldTwo:'p',FieldThree:14},
{FieldOne:'ijk',FieldTwo:'p',FieldThree:14},
{FieldOne:'lmn',FieldTwo:'p',FieldThree:14},
{FieldOne:'bcd',FieldTwo:'',FieldThree:14}],
sort: [
{ field: "FieldOne", dir: "asc" },
{ field: "FieldTwo", dir: "desc" }
]
},
dataTextField: "FieldOne",
dataValueField: "FieldTwo"
}).data('kendoDropDownList');
dropdownlist.dataSource.add({FieldOne:'abc',FieldTwo:'p',FieldThree:14});
</script>
check http://docs.telerik.com/kendo-ui/api/javascript/data/datasource#configuration-sort for more info

What is column syntax for odata linked table

I have a normal WCF service where there is a product that belongs to a department.
The URL to get a department of a given product would look like this:
http://localhost:49168/RapDataService.svc/Products(1000)/Department/Name
Using Razor I can have the foreignkey column that does what I want, but I can't find the syntax for JavaScript. I would assume something like this:
columns: [
{
field: "Department#Name",
filterable: false
},
But I can't find the correct syntax anywhere.
Found answer, just have to expand the datasource
type: "odata",
transport: {
read: {
url: "http://localhost:49168/RapDataService.svc/Products",
data: { $expand: "Department"}
}
},
And then the syntax I was looking for was the dot.
{
field: "Department.Name",
title: "Afdeling",
filterable: true
},

Translating JSON into custom dijit objects

I am looking for an example where JSON constructed from the server side is used to represent objects that are then translated into customized widgets in dojo. The JSON would have to be very specific in its structure, so it would not be a very general solution. Could someone point me to an example of this. It would essentially be the reverse of this
http://docs.dojocampus.org/dojo/formToJson
First of all let me point out that JSON produced by dojo.formToJson() is not enough to recreate the original widgets:
{"field1": "value1", "field2": "value2"}
field1 can be literally anything: a checkbox, a radio button, a select, a text area, a text box, or anything else. You have to be more specific what widgets to use to represent fields. And I am not even touching the whole UI presentation layer: placement, styling, and so on.
But it is possible to a certain degree.
If we want to use Dojo widgets (Dijits), we can leverage the fact that they all are created uniformly:
var myDijit = new dijit.form.DijitName(props, node);
In this line:
dijit.form.DijitName is a dijit's class.
props is a dijit-specific properties.
node is an anchor node where to place this dijit. It is optional, and you don't need to specify it, but at some point you have to insert your dijit manually.
So let's encode this information as a JSON string taking this dijit snippet as an example:
var myDijit = new dijit.form.DropDownSelect({
options: [
{ label: 'foo', value: 'foo', selected: true },
{ label: 'bar', value: 'bar' }
]
}, "myNode");
The corresponding JSON can be something like that:
{
type: "DropDownSelect",
props: {
options: [
{ label: 'foo', value: 'foo', selected: true },
{ label: 'bar', value: 'bar' }
]
},
node: "myNode"
}
And the code to parse it:
function createDijit(json){
if(!json.type){
throw new Error("type is missing!");
}
var cls = dojo.getObject(json.type, false, dijit.form);
if(!cls){
// we couldn't find the type in dijit.form
// dojox widget? custom widget? let's try the global scope
cls = dojo.getObject(json.type, false);
}
if(!cls){
throw new Error("cannot find your widget type!");
}
var myDijit = new cls(json.props, json.node);
return myDijit;
}
That's it. This snippet correctly handles the dot notation in types, and it is smart enough to check the global scope too, so you can use JSON like that for your custom dijits:
{
type: "my.form.Box",
props: {
label: "The answer is:",
value: 42
},
node: "answer"
}
You can treat DOM elements the same way by wrapping dojo.create() function, which unifies the creation of DOM elements:
var myWidget = dojo.create("input", {
type: "text",
value: "42"
}, "myNode", "replace");
Obviously you can specify any placement option, or no placement at all.
Now let's repeat the familiar procedure and create our JSON sample:
{
tag: "input",
props: {
type: "text",
value: 42
},
node: "myNode",
pos: "replace"
}
And the code to parse it is straightforward:
function createNode(json){
if(!json.tag){
throw new Error("tag is missing!");
}
var myNode = dojo.create(json.tag, json.props, json.node, json.pos);
return myNode;
}
You can even categorize JSON items dynamically:
function create(json){
if("tag" in json){
// this is a node definition
return createNode(json);
}
// otherwise it is a dijit definition
return createDijit(json);
}
You can represent your form as an array of JSON snippets we defined earlier and go over it creating your widgets:
function createForm(array){
dojo.forEach(array, create);
}
All functions are trivial and essentially one-liners — just how I like it ;-)
I hope it'll give you something to build on your own custom solution.

Resources