Qlik Community

Qlik Design Blog

All about product and Qlik solutions: scripting, data modeling, visual design, extensions, best practices, etc.

Employee
Employee

Mutiple hypercubes in an extension

Recently, I needed to make an extension that required multiple hypercubes with dimensions and measures that were able to be set by the user. The following is the solution I came up with. It could be a bit more generalized, and there's some other things to consider such as making selections, but it's a good starting point and an interesting topic, so I wanted to share with you.

Step 1 - Add the hypercubes to initialProperties

The hypercubes need to be added to initialProperties. They can each be added within their own object in initialProperties, as below.

initialProperties: {

  cube1: {

    qHyperCubeDef: {

      qDimensions: [],

      qMeasures: [],

      qInitialDataFetch: [{

        qWidth: 2,

        qHeight: 5000

      }]

    }

  },

  cube2: {

    qHyperCubeDef: {

      qDimensions: [],

      qMeasures: [],

      qInitialDataFetch: [{

        qWidth: 2,

        qHeight: 5000

      }]

    }

  }

}

Step 2 - Add ability for user to define dimensions and measures in definition

Now, you'll need to add the ability for users to define the dimensions and measures for each cube into the properties panel.

definition: {

  type: "items",

  component: "accordion",

  items: {

    cube1props: {

      label: "Cube 1",

      type: "items",

      items: {

        dimension: {

          label: "Dimension",

          type: "string",

          expression: "always",

          expressionType: "dimension",

          ref: "cube1props.dimension"

        },

        measure: {

          label: "Measure",

          type: "string",

          expression: "always",

          expressionType: "measure",

          ref: "cube1props.measure"

        },

      }

    },

    cube2props: {

      label: "Cube 2",

      type: "items",

      items: {

        dimension: {

          label: "Dimension",

          type: "string",

          expression: "always",

          expressionType: "dimension",

          ref: "cube2props.dimension"

        },

        measure: {

          label: "Measure",

          type: "string",

          expression: "always",

          expressionType: "measure",

          ref: "cube2props.measure"

        }

      }

    }

  }

}

Step 3 - Updating the hypercube with user defined properties

This is where the interesting stuff happens. When the user updates one of the properties associated with a hypercube, we need to actually update the hypercube to reflect that. So in the extension's controller, we're going to watch for changes to the props for a cube, and then use the Backend API ApplyPatches method to update the cube.

//Set cube1

$scope.$watchCollection("layout.cube1props", function(props) {

  $scope.backendApi.applyPatches([

    {

      "qPath": "/cube1/qHyperCubeDef/qDimensions",

      "qOp": "replace",

      "qValue": JSON.stringify([{qDef: {qFieldDefs: [props.dimension]}}])

    },

    {

      "qPath": "/cube1/qHyperCubeDef/qMeasures",

      "qOp": "replace",

      "qValue": JSON.stringify([{qDef: {qDef: props.measure}}])

    }

  ], false);

});

//Set cube2

$scope.$watchCollection("layout.cube2props", function(props) {

  $scope.backendApi.applyPatches([

    {

      "qPath": "/cube2/qHyperCubeDef/qDimensions",

      "qOp": "replace",

      "qValue": JSON.stringify([{qDef: {qFieldDefs: [props.dimension]}}])

    },

    {

      "qPath": "/cube2/qHyperCubeDef/qMeasures",

      "qOp": "replace",

      "qValue": JSON.stringify([{qDef: {qDef: props.measure}}])

    }

  ], false);

});

That'll do it. Now the user can define a dimension and measure for each hypercube, and the hypercube will be patched accordingly. There's still some more to think about and some nice-to-have's with this approach, such as the ability to add variable numbers of dimensions and measures, allowing the user to set other properties of the hypercube, selections, and more. But, I think this pattern is a decent starting point.

Here's a link to a github repo I started around this idea, just in case you try this out and have anything cool to add.

GitHub - fkabinoff/qliksense-multicube-extension-template

4 Comments
Luminary
Luminary

Does Table API support this functionality?

0 Likes
276 Views
Employee
Employee

You mean if you want to use Table API objects instead of hypercubes? You could make it work.

So I don't think you can add Table API objects to initialProperties, so scratch that part. But you can create Table API objects with empty arrays for dimensions and measures, and then update the dimensions and measures similar to how the hypercubes are updated above. Create the table objects, then when the corresponding dimension or measure property changes, update the Table object using ApplyPatches‌ from the engine api.

So if you do var table = app.createTable([], [],{rows:200}), then you can send an rpc message to the object with Table.model.session.rpc(msg), and a msg to update a measure, for instance, would look something like

var msg = {

    "method":"ApplyPatches",

    "handle":table.model.handle,

    "params":[

        [

            {

                "qPath":"/qHyperCubeDef/qMeasures",

                "qOp":"replace",

                "qValue":JSON.stringify([{

                    qDef: {

                        qDef: props.measure

                    }

                }])

            }

        ]

    ],

    "jsonrpc":"2.0"

}

276 Views
robert_cazaciuc
New Contributor II

Hi Francis. Thanks for this post very useful. I am trying to use the same concept but in a different, simpler context. I am basically trying to use ApplyPatches to change the dimension of a graph but I am unsuccessful. This is an extrat of GetProperties of my target object:

{

    "handle": 2,

    "method": "SetProperties",

    "params": {

        "qProp": {

            "qInfo": {

                "qId": "BnRCyx",

                "qType": "barchart"

            },

            "qMetaDef": {},

            "qHyperCubeDef": {

                "qDimensions": [

                    {

                        "qDef": {

                            "qGrouping": "N",

                            "qFieldDefs": [

                                "=Month"

                            ],

I am trying to change the dimension from Month to Year and I have tried a couple of different things but none seem to work. I think the problem is with how qValue for year is defined. Do you know to correctly format the string to get the intend result?

{

  "jsonrpc": "2.0",

  "id": 2,

  "method": "ApplyPatches",

  "handle": 2,

  "params": [

    [

      {

        "qPath": "/qHyperCubeDef/qDimensions/0/qDef/qFieldDefs",

        "qOp": "replace",

        "qValue": "\"=Year\""

       

      }

    ]

  ]

}

276 Views
Employee
Employee

So the qPath you defined is to qFieldDefs, which should be an array, but your qValue is an expression. You could change your qPath to "/qHyperCubeDef/qDimensions/0/qDef/qFieldDefs/0" so it would an expect a value, or you could leave your qPath unchanged and change your qValue to an array.

Also, I've often ran into problems defining the qValue as a string instead of using JSON.stringify. I'm sure I'm just not formatting the string to be valid JSON, but it definitely makes life easy, so I suggest using that, as I do above when defining qValue.

0 Likes
276 Views