Article Content

  1. Introduction
  2. Creating your first App
  3. Formatting Code
  4. Exercises
    1. Hello World
    2. Place Highcharts Element
    3. Target different layouts with different screens and layouts
    4. Show important figures with a KPI visualizer
    5. Propagate filters between different datasources and elements with SyncSets
    6. Page Navigation
    7. Add elements to the background of other elements


Introduction

We are very happy that you wanna start out and play with the ONE DATA Apps module.

This page should serve as an introduction with a step by step tutorial on how to get started. The detailed technical documentation can be found at the ONE DATA Markup Language Documentation.


Creating your first App

To create a new App, you need to click the "Create new App" tile. You will have a preset app configuration there, which you can replace completely with the respective configs of this guide. The following gif provides a small intro:



The way you will build your apps in the future is by writing it as JSON configuration. Every possible option is documented in the ODML documentation which is linked above.

In this article we will walk you through all currently implemented concepts and explain what they are for. Although we will not cover every detail it should make you able to work on your own with the help of the documentation.

The basic structure looks as follows:

  {
    "version": "0.1", // defines the ODML version, fixed as "0.1" at the moment
    "pages": [],
    "screens": [],
    "layouts": [],
    "elements": [],
    "datasources": []
  }


These are the basic configuration sections that you can use to build your app. The idea is to separate concerns and make the parts itself reusable within the app as well as make it easy to transfer the configuration via simple copy & paste to separate apps.

The sections are responsible for different purposes as follows:

Pages

Pages are used to add different Pages and a navigation to your app. Pages contain different screens, layouts and children to allow you and the users of your app to show something like details of elements.


Screens

Screens are used to target specific end user devices based on their screen size. The layout that should be visible to the user can be selected based on screen width, height and/or aspect ratio. The first screen that matches the conditions is selected. If no screen matches, the first layout is rendered as a fallback.


Layouts

In previous ONE DATA you had only one report layout available. This worked with dividing the available viewport in equally sized units depending on the amount of units you wanted to have. In ONE DATA Markup Language this will be further enhanced to give the possibility to chose from different layout systems ("layout types"). To be able to migrate old reports as good as possible, we already implemented a layout type which behaves exactly like the old layout used to. This layout type is called "fixed_grid". This is also the only one fully implemented and "battle tested" as of now. Throughout this guide we will always use this one. A layout defines content-agnostic containers which serve as slots where you can place an arbitrary element.


Elements

Elements are the actual visual thing that you want to show on the screen. In ONE DATA reports this was referred to as "report container". Elements have neither a fixed position, size nor layout directly assigned. They serve as a pool of visualization which you can add and configure on their own independently from their actual use in the App. To place an element in the UI you have to add it in the respective layout. This decoupling allows us to use an element in different layouts without having to configure it for every layout. Also it is possible to use the same element multiple times in a single layout.


Datasources

Datasources are the definitions of data or how to retrieve data (e.g. from data tables) which should be visualized by the before mentioned elements. The same datasource can be connected to multiple elements and reused over different layouts targeting different end user devices.


Formatting Code

To format your code, you can now use the editors built in functionality to format the document via right-click and click on "Format Document":



Exercises

1) Hello World

To build a very simple app which only shows the text "Hello World!" we need to define an element which holds the text as HTML, a layout with a container and place the element in this container.

Let's start by defining the element. According to the documentation for the HTML element, the definition looks like the following:

  {
    "id": "myElementId",
    "type": "html",
    "config": {
      "html": "<h1>Hello World!</h1>"
    }
  }



The id key is used to identify the element when we will later place it in the container. Let's give it a more sensible name ("helloWorld") for our case and add it to the elements section in our app configuration skeleton.

The result should look as follows:


  {
    "version": "0.1",
    "screens": [],
    "layouts": [],
    "elements": [
      {
        "id": "helloWorld",
        "type": "html",
        "config": {
          "html": "<h1>Hello World!</h1>"
        }
      }
    ],
    "datasources": []
  }


Now we are set for the element definition and need to setup a layout which will display our element.

We will start from the sample fixed grid layout shown in the documentation:


{
  "id": "layout1",
  "type": "fixedGrid",
  "config": {
    "dim": {
      "x": 24,
      "y": 24
    },
    "containers": [
      {
        "id": "titleContainer",
        "pos": {
          "x": 7,
          "y": 5,
          "z": 1
        },
        "size": {
          "w": 8,
          "h": 4
        }
      }
    ]
  },
  "placements": [
    {
      "containerId": "titleContainer",
      "elementId": "helloWorld"
    }
  ]
}


With this layout configuration we have defined a layout which has 24 columns and 24 rows in which you can place containers. Note, that you can define containers outside this range, which would mean that the user has to scroll to see these elements. All we need to do is adjusting the elementId in the placements element that is already present in the sample to our previously defined elementId "helloWorld".

Putting all together we should end up with the following complete (copy & paste ready) app configuration, which should yield the desired result shown in the preview.


{
  "version": "0.1",
  "screens": [],
  "layouts": [
    {
      "id": "layout1",
      "type": "fixedGrid",
      "config": {
        "dim": {
          "x": 24,
          "y": 24
        },
        "containers": [
          {
            "id": "titleContainer",
            "pos": {
              "x": 7,
              "y": 5,
              "z": 1
            },
            "size": {
              "w": 8,
              "h": 4
            }
          }
        ]
      },
      "placements": [
        {
          "containerId": "titleContainer",
          "elementId": "helloWorld"
        }
      ]
    }
  ],
  "elements": [
    {
      "id": "helloWorld",
      "type": "html",
      "config": {
        "html": "<h1>Hello World!</h1>"
      }
    }
  ],
  "datasources": []
}


If everything went well, you should see the following in your app builder:

Congratulations! You have just built your first finished app. Feel free to play around with your app. Things you may try out might be:

  • changing the layout dimensions
  • change the position and/or size of your container
  • add a second container which holds the same element as the first one
  • add a second element with the content "I like it!" and place it in the second container


The last step of this first tutorial is to publish the app and switch to the app viewer. To achieve this open the "History" tab on the left hand side of the editor, and select the version you want to publish. Then click on the three dots and on "Select this version for App Viewer". After that just confirm the dialog and the app is published in the Viewer.



Also you can switch to the viewer mode of the app builder. This is the customer view on your app without the editor or rulers in the preview. You can easily switch to it via the module switcher in the very top right in the app and select "App Viewer".



2) Place Highcharts element

Now we want to get more fancy and display a highcharts element. Different to what you are used to in ONE DATA reports there is no integrated visual editor of charts. However, there is a Highharts Editor available for use on the official highcharts page. This enables you to use every feature highcharts has in a more or less advanced visual editor. You are not limited to what is currently integrated within ONE DATA. You just copy & paste the configuration this editor throws out and paste it into your app configuration. This is enough to be able to show it in your app. First, let's have a look how the element configuration looks like.

Again, we will start off with the example that is provided in the documentation:

  {
    "id": "myHighchartsElement", // again the id of the element
    "type": "highcharts", // type is now "highcharts"
    // This holds the config of the chart.
     // It has to be replaced with the output of the editor
    "config": {
       // ... highcharts configuration
    }
  }


Now let's have a quick dive in the Highcharts editor. You can find it here: http://editor.highcharts.com/full.html

At first we will load some sample data from highcharts, via their "Import" button at the bottom, switch to the "Sample Data" tab and select "Categorized, four series". The editor should immediately render a sample chart:



Now we will grab this source code from the editor and use it as our element config. You can get the config by navigating to the third button of the sidebar ("Customize Chart") in the editor and open the "Preview Options" tab:




The element configuration should look like the following in the end:


{
  "elements": [
    {
      "id": "welcomeTitle",
      "type": "html",
      "config": {
        "html": "<h1>Hello World!</h1>"
      }
    },
    {
      "id": "chartVis",
      "type": "highcharts",
      "source": "raw_testSource",
      "config": {
        "title": {
          "text": "My Chart"
        },
        "subtitle": {
          "text": "My Untitled Chart"
        },
        "exporting": {},
        "chart": {
          "type": "line",
          "polar": false
        },
        "plotOptions": {
          "series": {
            "dataLabels": {
              "enabled": true
            },
            "animation": false
          }
        },
        "series": [
          {
            "name": "Tokyo",
            "turboThreshold": 0
          },
          {
            "name": "New York",
            "turboThreshold": 0
          },
          {
            "name": "Berlin",
            "turboThreshold": 0
          },
          {
            "name": "London",
            "turboThreshold": 0
          }
        ],
        "yAxis": {
          "title": {}
        }
      }
    }
  ]
}


Note that this is not yet nicely formatted, we will show you later how you can let the app builder editor do that for you. For now this works anyways. We added the element definition now to the app config. The preview is not yet showing something though.

This is because we need to add a new container to the layout. We let it start at (5|1) coordinate and span the whole width (24) and a height of 8. This means we need to specify our container as follows:


  {
    "id": "chart1",
    "pos": {
      "x": 1,
      "y": 5
    },
    "size": {
      "w": 24,
      "h": 8
    }
  }

Also we need to add a placement:

  {
    "containerId": "chart1",
    "elementId": "chartVis"
  }


Putting this all together, we end up with a working app configuration.


You have just finished the second part of this getting started guide. Note, that your changes are only saved after the app has been saved, which is indicated in the top left of the app editor:



You are welcome to change the data and watch your visualization refresh with the updated data.


3) Target different layouts with different screens and layouts

You already got to the last section of this guide! Now we want to adapt to larger and smaller screens. 

Imagine a monitor, which has a screen width of 2560px. The App Preview is half your monitors size. We want a layout that shows two charts side-by-side if you view the app on full screen and one layout that shows the second chart below the first one on smaller screens.

Let's start by defining our screens:


  "screens": [
    {
      "layout": "largeLayout",
      "conditions": {
        "minScreenWidth": 2000
      }
    },
    {
      "layout": "defaultLayout"
    }
  ]



What we defined is our default layout (which will be the one for smaller screens) with no conditions. This is our fallback. The first screen defines our large monitor full size. It will be used once the screen width is at least 2000px. The first layout which matches our conditions will be used. This is why we need to define the default layout as the last one.

Now we only need to define the layouts accordingly. With the knowledge we already gained you should be able to do that on your own without detailed step-by-step instructions. The configuration for the layouts you should end up with is something like this.


You can test whether your layouts/screens are working by toggling the editors full screen preview and side by side view in the upper right of the editor view:



4) Show important figures with a KPI visualizer

A very common use case for apps is to show certain important figures to the consumer. Apps has you covered here with a dedicated KPI element. You can find the documentation for the KPI element in the corresponding article.

Let's have a look at the sample config provided in the documentation:


{
  "id": "kpi-test",
  "type": "kpi",
  "source": "raw_testSource2",
  "navigation": [
    {
      "to": "overview.details",
      "label": "Details"
    }
  ],
  "config": {
    "title": "Test KPI",
    "subTitle": "",
    "footer": "footer",
    "layout": {
      "orientation": "vertical",
      "large": true
    },
    "kpiName": {
      "column": ""
    },
    "color": "red",
    "unit": {
      "text": ""
    },
    "value": {
      "column": "value"
    }
  }
}


According to the documentation, this element supports showing mocked information as well as taking some or all of them from a datasource.

 

5) Propagate filters between different datasources and elements with SyncSets

SyncSets allow you and the users of your applications to slice and dice individual subsets of the data tables and take a closer look at the data that matters. You can find more information about the topic in the SyncSets article.

Let's start with a sample APP configuration:


{
  "version": "0.1",
  "screens": [
    {
      "layout": "layout1",
      "conditions": {
        "minScreenWidth": 0
      }
    }
  ],
  "layouts": [
    {
      "id": "layout1",
      "type": "fixedGrid",
      "config": {
        "dim": {
          "x": 24,
          "y": 24
        },
        "containers": [
          {
            "id": "tableContainer1",
            "pos": {
              "x": 1,
              "y": 1
            },
            "size": {
              "w": 12,
              "h": 12
            }
          },
          {
            "id": "tableContainer2",
            "pos": {
              "x": 13,
              "y": 1
            },
            "size": {
              "w": 12,
              "h": 12
            }
          },
          {
            "id": "chartContainer",
            "pos": {
              "x": 1,
              "y": 12
            },
            "size": {
              "w": 24,
              "h": 13
            }
          }
        ]
      },
      "cellPadding": "5px",
      "placements": [
        {
          "containerId": "tableContainer1",
          "elementId": "frtTableCountry"
        },
        {
          "containerId": "tableContainer2",
          "elementId": "frtTableSector"
        },
        {
          "containerId": "chartContainer",
          "elementId": "frtChart"
        }
      ]
    }
  ],
  "datasources": [
    {
      "id": "table_countries",
      "origin": "datatable",
      "config": {
        "schema": "table",
        "dataId": "13b8c145-2beb-4d72-a6a5-9102ce84b744",
        "filterOptions": {
          "enabled": true,
          "distinctValues": true
        }
      }
    },
    {
      "id": "table_sectors",
      "origin": "datatable",
      "config": {
        "schema": "table",
        "dataId": "b9c5b904-9f23-4836-a6da-449245e8fde5",
        "filterOptions": {
          "enabled": true,
          "distinctValues": true
        }
      }
    },
    {
      "id": "table_forbes",
      "origin": "datatable",
      "config": {
        "schema": "table",
        "dataId": "586e7146-34e0-40c5-b7d3-79969b7cab5d"
      }
    }
  ],
  "elements": [
    {
      "id": "frtTableSector",
      "type": "table",
      "source": "table_sectors",
      "config": {
        "columns": [
          {
            "name": "Sector"
          }
        ]
      }
    },
    {
      "id": "frtTableCountry",
      "type": "table",
      "source": "table_countries",
      "config": {
        "columns": [
          {
            "name": "Country"
          }
        ]
      }
    },
    {
      "id": "frtChart",
      "type": "highcharts",
      "source": "table_forbes",
      "config": {
        "title": {
          "text": "Top 10 Companies"
        },
        "exporting": {},
        "chart": {
          "type": "bar",
          "polar": false
        },
        "plotOptions": {
          "series": {
            "dataLabels": {
              "enabled": true
            },
            "animation": false
          }
        },
        "xAxis": {
          "columnName": "Company"
        },
        "series": [
          {
            "name": "Sales",
            "turboThreshold": 0
          }
        ]
      }
    }
  ]
}


Now your app contains two Tables and a Highchart. But if you set any filter (select one or more Countries / Sectors) to one of the tables the other table or the Highchart aren't affected.


At this point we should think about adding SyncSets and SyncMaps to our configuration.


A SyncSet contains the following properties: 

Property
Meaning
sources
The datasources involved in this SyncSet
joins
The mapping between different datasources
syncMaps
Mappings between which datasource (source) influences other datasources (targets)


So in this example we link the 'table_countries', the "table_sectors" and the "table_forbes" datasets via their "Country" columns:


  "datasources" : [
      ...Datasources
  ],
  "syncSets": [
    {
      "id": "forbes_countries",
      "sources": [
        "table_countries",
        "table_sectors",
        "table_forbes"
      ],
      "joins": [
        {
          "datasource": "table_countries",
          "columns": [
            "Country"
          ]
        },
        {
          "datasource": "table_sectors",
          "columns": [
            "Country"
          ]
        },
        {
          "datasource": "table_forbes",
          "columns": [
            "Country"
          ]
        }
      ],
      "syncMap": [
        {
          "source": "table_countries",
          "targets": [
            "table_sectors",
            "table_forbes"
          ]
        },
        {
          "source": "table_sectors",
          "targets": [
            "table_countries"
          ]
        }
      ]
    },
    {
      "id": "forbes_sectors",
      "sources": [
        "table_sectors",
        "table_forbes"
      ],
      "joins": [
        {
          "datasource": "table_sectors",
          "columns": [
            "Sector"
          ]
        },
        {
          "datasource": "table_forbes",
          "columns": [
            "Sector"
          ]
        }
      ],
      "syncMap": [
        {
          "source": "table_sectors",
          "targets": [
            "table_forbes"
          ]
        }
      ]
    }
  ],



At this point your datasources are linked. But your filters will not affect any other element at all.



So let's get to the last step of this syncSets example. You need to tell your elements about syncSets too.

Every element syncSet contains the following properties:

 

Property
Meaning
syncSetId
The syncSet to connect the elements
applyFilters
Apply filters from other elements
publishFilters
Notify other elements about filter changes


In this example, if we set a filter on the "Country" column of "frt_countries", the table with Sector and the Highchart will also be updated.

  "syncSets" : [
      ...syncSets
  ],
  "elements": [
      {
        "id": "frtTableSector",
        "type": "table",
        "source": "frt_sectors",
        "syncSets": [
          {
            "syncSetId": "forbes_sectors",
            "publishFilters": true,
            "applyFilters": false
          },
          {
            "syncSetId": "forbes_countries",
            "publishFilters": true,
            "applyFilters": true
          }
        ],
        "config": {
          "columns": [
            {
              "name": "Sector"
            }
          ]
        }
      },
      {
        "id": "frtTableCountry",
        "type": "table",
        "source": "frt_countries",
        "syncSets": [
          {
            "syncSetId": "forbes_countries",
            "publishFilters": true,
            "applyFilters": true
          }
        ],
        "config": {
          "columns": [
            {
              "name": "Country"
            }
          ]
        }
      },
      {
        "id": "frtChart",
        "type": "highcharts",
        "source": "frt_forbes",
        "syncSets": [
          {
            "syncSetId": "forbes_sectors",
            "applyFilters": true,
            "publishFilters": false
          },
          {
            "syncSetId": "forbes_countries",
            "applyFilters": true,
            "publishFilters": false
          }
        ],
        "styles": {
          "chart": {
            "xAxis": {
              "labels": {
                "color": ""
              }
            }
          }
        },
        "config": {
          "title": {
            "text": "Top 10 Companies"
          },
          "exporting": {},
          "chart": {
            "type": "bar",
            "polar": false
          },
          "plotOptions": {
            "series": {
              "dataLabels": {
                "enabled": true
              },
              "animation": false
            }
          },
          "xAxis": {
            "columnName": "Company"
          },
          "series": [
            {
              "name": "Sales",
              "turboThreshold": 0
            }
          ]
        }
      }
    ]


Now you have finished your first App with SyncSets!


Full App Configuration can be found here.



Page navigation allows you and the users of your application to create and navigate between different Pages. You can find the documentation for the Page Navigation in the ODML-Page-Navigation.

Let's start with a sample app configuration:


{
  "version": "0.1",
  "pages": [
    {
      "screens": [
        {
          "layout": "layout1"
        }
      ],
      "children": []
    }
  ],
  "layouts": [
    {
      "id": "layout1",
      "type": "fixedGrid",
      "config": {
        "dim": {
          "x": 24,
          "y": 24
        },
        "containers": [
          {
            "id": "tableContainer1",
            "pos": {
              "x": 1,
              "y": 5
            },
            "size": {
              "w": 12,
              "h": 20
            }
          },
          {
            "id": "tableContainer2",
            "pos": {
              "x": 13,
              "y": 5
            },
            "size": {
              "w": 12,
              "h": 20
            }
          }
        ]
      },
      "cellPadding": "0px",
      "placements": [
        {
          "containerId": "tableContainer1",
          "elementId": "table1"
        },
        {
          "containerId": "tableContainer2",
          "elementId": "table2"
        }
      ]
    }
  ],
  "datasources": [
    {
      "id": "testSource",
      "origin": "datatable",
      "config": {
        "filterOptions": {
          "distinctValues": true,
          "enabled": true
        },
        "schema": "table",
        "dataId": "13b8c145-2beb-4d72-a6a5-9102ce84b744"
      }
    }
  ],
  "syncSets": [
    {
      "id": "testSyncSet1",
      "sources": [
        "testSource"
      ],
      "joins": [
        {
          "datasource": "testSource",
          "columns": [
            "Country"
          ]
        }
      ],
      "syncMap": [
        {
          "source": "testSource",
          "targets": [
            "testSource"
          ]
        }
      ]
    }
  ],
  "elements": [
    {
      "id": "table1",
      "type": "table",
      "source": "testSource",
      "syncSets": [
        {
          "syncSetId": "testSyncSet1",
          "publishFilters": true,
          "applyFilters": true
        }
      ],
      "preserveNavigationSpace": true,
      "config": {
        "columns": [
          {
            "name": "Country"
          },
          {
            "name": "Sales"
          }
        ]
      }
    },
    {
      "id": "table2",
      "type": "table",
      "source": "testSource",
      "syncSets": [
        {
          "syncSetId": "testSyncSet1",
          "publishFilters": false,
          "applyFilters": true
        }
      ],
      "config": {
        "columns": [
          {
            "name": "Country"
          },
          {
            "name": "Market_Value"
          }
        ]
      }
    }
  ]
}


Now the app contains two Layouts and two synced tables and should look like this example. 


At this point we want to add a page to our app.

You can now just add the 'pages' array direct after your version and add a unique id and a label.

The label will be displayed in the breadcrumb.

We will later add a child page so please make sure you take a unique id ('home' is not the best) .


{
  "version": "0.1",
  "screens": [],
  "pages": [
    {
      "id": "home",
      "label": "Home",
      "screens": [
        {
          "layout": "layout1",
          "conditions": {
            "syncSets": {
              "testSyncSet1": {
                "Country": {
                  "present": false
                }
              }
            }
          }
        },
        {
          "layout": "layout2",
          "conditions": {
            "syncSets": {
              "testSyncSet1": {
                "Country": {
                  "present": true
                }
              }
            }
          }
        }
      ]
    }
   ],
    "layouts": [  
       // .. layouts
     ]




After this step you will notice your app now contains a breadcrumb at the top left with the label you set before.


Now we should think about 'screens' in our page. With a screen you can add layouts with conditions to the page. In this example we want to be able to apply a filter to our first table (the left one) and hide the other after a filter is set.




The last thing we will add is a 'child page'.

A child page is used to switch a layout or show details of an element in another layout. 

Setting up a child is almost the same as with pages, so you need a unique id and a label for the breadcrumb.

The difference here is you can add a seperate layout to the screen. This layout is used to be displayed after you click on the link at the bottom of the table element. Add the following code section after the screens property of the page.

         ... screens
        }
      ],
      "children": [
        {
          "id": "details",
          "label": "Details",
          "screens": [
            {
              "layout": "layout2"
            }
          ]
        }
      ]



So we mentioned a link at the bottom of the table element but at this point there is no link. So let's add it.

You need to add this to the element that should contain the link to navigate. In this example it will be the second table.

To display a navigation link we have to add 'navigation' to the element we want to link.

This is the reason why the id's we set before should be unique. You have to add the pages id ('home') and the id of your child page ('details') to the "to" string (separated by a dot) of your navigation.


Note that this will only work if both id's exactly match those defined in the page property! (Case sensitive)


    {
      "id": "table2",
      "type": "table",
      "source": "testSource1",
      "navigation": [
        {
          "to": "home.details",
          "label": "See Details"
        }
      ], 
      // .. syncSets



And here you go.



7) Add elements to the background of other elements

Now you can add elements to the background of other elements. In this tutorial we will create a kpi and a chart element and set the chart to the background of the kpi.

Let's start with a sample APP configuration with one chart and one kpi.


{
  "version": "0.1",
  "screens": [
    {
      "layout": "layout1",
      "conditions": {
        "minScreenWidth": 0
      }
    }
  ],
  "global": {
    "columnStyles": [],
    "styles": {
      "backgroundColor": "#eee",
      "chartColors": [
        "rgb(252, 3, 3)",
        "rgb(252, 219, 3)",
        "rgb(69, 252, 3)",
        "rgb(3, 36, 252)"
      ]
    }
  },
  "layouts": [
    {
      "id": "layout1",
      "type": "fixedGrid",
      "config": {
        "dim": {
          "x": 24,
          "y": 24
        },
        "containers": [
          {
            "id": "kpiContainer1",
            "pos": {
              "x": 7,
              "y": 2
            },
            "size": {
              "w": 12,
              "h": 7
            }
          },
          {
            "id": "chartContainer",
            "pos": {
              "x": 7,
              "y": 10
            },
            "size": {
              "w": 12,
              "h": 5
            }
          }
        ]
      },
      "cellPadding": "5px",
      "placements": [
        {
          "containerId": "kpiContainer1",
          "elementId": "kpi-test1"
        },
        {
          "containerId": "chartContainer",
          "elementId": "chartVis"
        }
      ]
    }
  ],
  "datasources": [
    {
      "id": "raw_testSource2",
      "origin": "raw",
      "config": {
        "schema": "table",
        "data": [
          {
            "otherValue": "218,422"
          }
        ]
      }
    },
    {
      "id": "raw_testSource",
      "origin": "raw",
      "config": {
        "schema": "table",
        "data": [
          {
            "Categories": "Jan",
            "Berlin": 1.5,
            "Tokyo": 2.2,
            "New York": 2.3,
            "London": 1.1
          },
          {
            "Categories": "Feb",
            "Berlin": 2.1,
            "Tokyo": 2.8,
            "New York": 2.9,
            "London": 3.1
          },
          {
            "Categories": "Mar",
            "Berlin": 1.2,
            "Tokyo": 2.8,
            "New York": 2.1,
            "London": 2.4
          },
          {
            "Categories": "Apr",
            "Berlin": 0.2,
            "Tokyo": 4.2,
            "New York": 3.7,
            "London": 2.5 
         }
        ]
      }
    }
  ],
  "elements": [
    {
      "id": "kpi-test1",
      "type": "kpi",
      "source": "raw_testSource2",
      "config": {
        "title": "Customer Payment Behavior",
        "footer": "can be unlocked",
        "layout": {
          "orientation": "vertical",
          "large": true
        },
        "kpiName": {
          "column": ""
        },
        "unit": {
          "text": ""
        },
        "value": {
          "column": "otherValue"
        }
      }
    },
    {
      "id": "chartVis",
      "type": "highcharts",
      "source": "raw_testSource",
      "config": {
        "xAxis": {
          "labels": false
        },
        "yAxis": {
          "labels": false,
          "title": ""
        },
        "title": "",
        "exporting": {},
        "chart": {
          "type": "column",
          "polar": false
        },
        "plotOptions": {
          "series": {
            "dataLabels": {
              "enabled": false
            },
            "animation": true
          }
        },
        "series": [
          {
            "name": "Tokyo",
            "turboThreshold": 0
          },
          {
            "name": "New York",
            "turboThreshold": 0
          },
          {
            "name": "Berlin",
            "turboThreshold": 0
          },
          {
            "name": "London",
            "turboThreshold": 0
          }
        ],
        "legend": {
          "enabled": false
        }
      }
    }
  ]
}


Now you have an app with a KPI and a chart element. Let's add additional properties to set the chart in the background of the KPI element. Let's start with the position of the elements and the z-index of the containers.


    {
      "id": "layout1",
      "type": "fixedGrid",
      "config": {
        "dim": {
          "x": 24,
          "y": 24
        },
        "containers": [
          {
            "id": "kpiContainer1",
            "pos": {
              "x": 7,
              "y": 9,
              "z": 2
            },
            "size": {
              "w": 12,
              "h": 7
            }
          }, 
         {
            "id": "chartContainer",
            "pos": {
              "x": 7,
              "y": 10
            },
            "size": {
              "w": 12,
              "h": 5
            }
          }
        ]
      },




The last thing we need to do is add the RGBA background color to the KPI element to make the chart visible.

 "elements": [
    {
      "id": "kpi-test1",
      "type": "kpi",
      "source": "raw_testSource2",
      "styles": {
        "backgroundColor": "rgba(255,255,255,0.9)"
      },
      "config": {
        "title": "Customer Payment Behavior",
        // ...


Now the app should look like this.