Adding Data Bound Properties: Power BI Custom Visual Development Fundamentals #16

Learn how to add data properties to your Power BI Custom Visual.
This video is part of a course, please check the playlist below for the set.

Playlist @ https://goo.gl/qbHDSq
Code @ https://goo.gl/7C4LeM
Dataset @ https://goo.gl/DEaZYY
D3 v3.x Docs @ https://goo.gl/xHZQ18

Transcript

Hello again and welcome back to this series on developing Power BI Custom Visuals.

Today I’ll show you how to add data bound settings to the properties pane of your visual.

These data bound properties allow the user to configure aspects of your visual that are related to a specific bit of data, for example, a specific series or a specific category.

In this video, such a property will enable users to choose a specific colour for each specific category bar.

So, let’s get to it, right now.

Just like in the last video about simple properties, we can begin by going to the capabilities.json file so we can declare another property group.

        "dataColors":{
            "displayName": "Data Colors",
            "properties": {
                "fill":{
                    "displayName": "Color",
                    "type": {
                        "fill": {
                            "solid": {
                                "color": true
                            }
                        }
                    }
                }
            }
        }

This time, we’re adding a new property group called data colors, that contains a single property called fill and that the user will see as color.

The type of this property is a bit out of the ordinary but it basically tells Power BI to render a color picker for this property.

The value you get from that color picker is a typical hexadecimal color code, the same format that you can use in html.

Now from the point of view of the capabilities file, there is no real difference between a simple property and a data bound property.

What makes that difference is the way we handle the property in code.

In this case, instead of a single setting that we read and write, we want to get and persist a different color value per each data category that we have on-screen, regardless of how many categories there are.

To make that happen, we need to treat this property in a different way than before.

Instead of just adding a new simple setting, what we want to do is to associate a color value to each of data points.

Luckily, we have already done that.

The color property here is already what allows each bar to have its own color, so that’s one step less.

We can therefore go right ahead and populate these colors in the properties pane.

And you guess it, we do this in the unnecessarily named enumerate object instances function.

Now that’s a mouthful every single time.

 case "dataColors":

                    break;

But anyway, in here we can add another block to handle rendering of the data colors property group.

This block needs to be somewhat different though, as again, instead of rendering a single property on the pane, we need to render one unique property per category in our data.

The thing is, those categories and those colors currently live in the view model object, which is only populated at update time.

However, this function runs outside of the update cycle, so it doesn’t have access to the view model to begin with.

Well, that’s something we need to fix.

We need to make the view model available at the visual instance level.

private viewModel: ViewModel;

We can do this by adding a new instance variable here called viewmodel.

this.viewModel = this.getViewModel(options);

And then in the update function, we change the local viewmodel variable to the instance variable.

This will break things here a bit, so let’s just fix them before we move on.

And there you go, the view model is now accessible everywhere in the visual.

                    if (this.viewModel) {
                        for (let dp of this.viewModel.dataPoints) {
                            properties.push({
                                objectName: propertyGroupName,
                                displayName: dp.category,
                                properties: {
                                    fill: dp.colour
                                },
                                selector: dp.identity.getSelector()
                            })
                        }
                    }

This mean we can go back to the enumerate something something function and add the remaining code.

So there are a couple of things different here.

First, instead of just pushing a single property to power bi, we’re instead pushing one property per each live data point that we have.

Our data points do map to categories in this visual, so this is about right.

Second, for each property that we push, we are defining a custom display name for it, using the category name.

If we didn’t do this, we would see the default display name of color in every single property and that would be quite useless.

Third, we are now providing this selector value as well.

This is where the magic happens.

The selector is a bit of information that allows Power BI to store the color value we provide here against the specific slice of data that this data point identifies.

This is how Power BI can know that a certain property value belongs to a certain category as opposed to being a simple property with a single value.

In this way, Power BI can store multiple values for this property and will be able to return them to you, associated with each category, every time you ask.

In fact, let’s see this in action.

So now we can see a list of all the categories here, and we can select a color for each.

Thing is, it’s not applying any color other than the default and that’s because we’re not yet reading the color that the user is manually selecting.

But that’s fine, before we fix this, I want to show you how Power BI provides that manual color to you.

So if you switch here to data view mode, and you expand the metadata view, you’ll see exactly nothing.

This is a consequence of using that selector when populating the properties.

When you do that, what Power BI does is, instead of showing your property in the metadata view, it attaches it directly to the appropriate place in whatever data view you are requesting.

In our case, we are requesting a categorical data view and we are saying, by using the selectorId, that each value maps to a single category.

Meaning, we are literally data binding that setting.

Based on that, Power BI has enough information to bring in the user selected color right next to the category itself.

Or well, at least, in this objects array, in the same index position of the equivalent category values array.

It’s kind of the same thing anyway.

So, this whole thing means that we can read these data bound settings at the same time as we’re reading the rest of the view model data.

let objects = categories.objects;

First we get that objects array from the category array.

Now we need to keep in mind this objects array may or may not be there to begin with.

We need to check for its existence every time we want to access it.

colour: objects && objects[i] && DataViewObjects.getFillColor(objects[i], {
                        objectName: "dataColors",
                        propertyName: "fill"
                    }, null) || this.host.colorPalette.getColor(categories.values[i]).value,

And that’s precisely what we’re going to do right now.

So we’re doing something similar here to what we did in the updatesettings function.

The only difference is that instead of grabbing properties from the metadata view, we’re now grabbing them straight from the categories view, via that object array.

Of course, we’re checking whether the objects array is there to begin with and if it isn’t, or if it is but is has no relevant data, then we just default to the palette color we had before.

So let’s see what this did.

And there you have it, you can select the colors in the chart to your heart’s content.

You can also revert the color back if you’re not happy with it and use the palette color instead.

That’s it for today.

If you have any questions, let me know and I’ll get back to you.

In the next video in this series, you will learn how to add some nice little data tooltips to this visual.

Until then, take care and see you next time.

Adding Simple Properties: Power BI Custom Visual Development Fundamentals #15

Learn how to add simple properties to your Power BI Custom Visual.
This video is part of a course, please check the playlist below for the set.

Playlist @ https://goo.gl/qbHDSq
Code @ https://goo.gl/7C4LeM
Dataset @ https://goo.gl/DEaZYY
D3 v3.x Docs @ https://goo.gl/xHZQ18

Transcript

Hello again and welcome back to this series on developing Power BI Custom Visuals.

Today you will learn how to add simple settings to the properties pane of your visual.

These properties allow the user to change certain aspects of your visual, such as font sizes and colours and whether you’re drawing puppies or kittens or whatever else you want really.

In this example, you will use them to hide and show the axis that you added in the previous video.

So let’s get to it, right now.

So the first thing is, we’re going to is install a new helper package via npm.

This is completely optional, but doing so will make our lives easier down the line.

The package in question is the power bi data view utilities package and like the name suggests, it contains a few utility classes that help us extract information out from the data views.

Before we install this though, we need to stop the pbiviz utility for a minute using control-c, then y.

Now we can run the npm command to install this package.

This is now installed so we just need to reference a couple of new files on the project configuration.

"node_modules/powerbi-visuals-utils-dataviewutils/lib/index.d.ts"

First get let’s go to the tsconfig project file and add the package’s typescript declaration file.

This file has no executable code if you check it, it just declares what types exist in the compiled code file.

Which is exactly what we’re going to add to the pbiviz file list.

So here we add the javascript file from the package that contains the compiled javascript code.

"node_modules/powerbi-visuals-utils-dataviewutils/lib/index.js"

Now you will notice this pattern a lot when adding more and more packages.

This is not always the case, but a lot of times, developers package their final product onto two files.

One is a compiled and minified javascript file that you can use anywhere, regardless if you’re using typescript or any other javascript superset.

And the other one, in our case, is a typescript definition file that simply declares what new types can the typescript compiler assume to exist, so that you can use them in your typescript code while keeping the benefits of static typing.

Remember, static typing is a typescript thing, not a javascript one.

Anyway, we’ve added the new files so we can run the pbiviz utility again and see if everything works.

There you go, everything is still working.

Now let’s get those properties going.

Switch to Visual Studio Code, visual.ts

So the first thing we need to, is to go over to the capabilities.json file.

Again, this is where you tell Power BI what your visual can do.

"objects": {
},

We’re now going to add an entry called objects.

This entry will contain a list of all the custom properties in your visual, grouped by a property header.

        "xAxis": {
            "displayName": "X Axis",
            "properties": {
                "show": {
                    "displayName": "Show X Axis",
                    "type": { "bool": true }
                }
            }
        },

For starters, we’re going to add a property to the visual that allows the user to enable or disable the X Axis.

So there are a few elements to this.

The “xAxis” element refers to the internal name of the property group.

This is the identifier that we can use, when we want to access this property group in code.

And indeed, that’s something that we will do in just a minute.

The display name on the other hand is the label that the end-user sees for this property group.

You can put here whatever makes sense to show on the interface.

Now each property group can have one or more properties in it, and we define that group with this “properties” element.

For now, we will start with a single property to allow the user to enable or disable the X Axis.

As you can probably guess, this “show” is the internal identifier of that property.

Likewise the displayname is the visible label in the properties pane itself.

What is new here is the type of the property.

Depending on this type, Power BI will render the property differently on-screen and you can play around with this if you want.

For the show x-axis property, we’re defining it as a boolean.

This will mean we’re either showing the axis or not showing the axis, and for this Power BI will render a neat toggle button that you can just click on.

Now, just defining this property in the capabilities file does not make it show up in the properties pane.

Power BI instead uses this property list to later inquire your visual whether you actually want to display each property or keep them to yourself and if you do want to display a certain property, then what’s the current value for it that you want the user to see.

So we need to make these decisions in the visual instead.

Let’s go back to the visual code and look at the settings object we created in the last video.

This is where we defined some defaults for the border and padding.

We’re going to add some new settings to reflect the need for showing and hiding the axes but we’re going to make things a little different now.

        private settings = {
            axis: {
                x: {
                    padding: {
                        default: 50,
                        value: 50
                    },
                },
                y: {
                    padding: {
                        default: 50,
                        value: 50
                    }
                }
            },
            border: {
                top: {
                    default: 10,
                    value: 10
                }
            }
        }

Before that though, I’m going to refactor this code into something a bit more useful.

What I’m doing here is splitting each setting into a default value and a current value.

This simple pattern will let us assign new values to each setting without worrying about losing the defaults.

Now by doing this, we’ve broken the code miserably, so let’s go ahead and fix it.

There you go, all working now.

                   show: {
                        default: true,
                        value: true
                    }

Let’s get back to the settings object and add a new settings to show x the axis.

This will have a default value of true and a current value of true as well although this doesn’t matter very much, as we will update this value upon loading the visual.

However, before we can get a setting from the user, we have to expose these settings in the properties pane, so that the user can change them in the first place.

The Power BI API has a peculiar way of doing this.

        public enumerateObjectInstances(options: EnumerateVisualObjectInstancesOptions): VisualObjectInstanceEnumeration {

            return null;
        }

Let’s add a new function to the end of the visual code.

enumerateObjectInstances.

That’s a mouth full.

Now this fella is an API function that Power BI calls as if by magic, every single time it needs to display or update a property in the properties pane.

The driver for those calls is in fact, the list of property groups that we define in the capabilities.json file.

So what this mean is that, every single time Power BI needs to render the properties pane, it will go through your declared property groups in capabilities.json and then call this function one time per each of those property groups.

It then expects this function to return the list of properties that you actually want to display, along with their current values and any extra configuration, such as minimum as maximum value restrictions.

Now this whole thing happens outside of the update loop.

It’s perfectly possible to see the main update function running once and then see this function running as many times as needed to keep the properties pane updated.

So now that you know that, let’s add some code to this function.

            let propertyGroupName = options.objectName;
            let properties: VisualObjectInstance[] = [];

            switch (propertyGroupName) {
                case "xAxis":
                    properties.push({
                        objectName: propertyGroupName,
                        properties: {
                            show: this.settings.axis.x.show.value
                        },
                        selector: null
                    });
		break;
            };

            return properties;

So we’re doing a couple of things here.

As I said, this function gets called once per property group, so we need to check for what exact property group this function is getting called each time.

Then, based on that, we fill up this array of Visual Object Instances with the property information that we want to show to the user.

Right now, we only want to make one property visible and that’s the “show” property, so, we’re returning that, along with the current value of that property.

So this VisualObjectInstance array is literally the mini view model that Power BI uses to render those properties on-screen, in the properties pane.

We’re also setting this selector setting to null.

The selector setting relates to data bound properties, which is, by the way, the subject of the next video, but for now, leave it as null and don’t worry about it.

So let’s see what this did.

Okay, so we now have a new property group on the properties pane called X Axis.

We also have a new push slider here, which curiously is at the property group level, instead of being inside the property group.

This seems odd, but there is nothing wrong about this.

The properties pane has this convention, where, if the first property that you define is a boolean type and it’s called show, then, instead of popping inside the property group, it will in fact show in the header of the property and you when enable or disable it, it will automatically enable or disable all the other properties in the pane.

This is the same behaviour as all other properties you see in the visual pane.

This is a neat convention, which we happen to be using here as our first property is indeed a boolean called show.

This is all good, however our show property doesn’t seem to be working very well yet.

That’s because we are pushing the property value to the properties pane here, yet nowhere in our code are we getting the new value back from power bi when the user sets it.

So the way Power BI provides the new setting value to your visual, is by forcing an update every single time something changes and providing all the new property values in the metadata view.

This is cool as it means, you don’t have to worry about forcing updates yourself if anything changes, as power bi will just piggy back on the whole update cycle for you.

The only thing we need to do here then, is to improve our update function so it can read these settings as they become available.

So let’s get that done.

Let’s get back to the visual code.

import DataViewObjects = powerbi.extensibility.utils.dataview.DataViewObjects;

Now let’s add an import statement up top so we can use a helper object from that library we installed in the beginning of this video.

That package does not export its types by default so we need to import them on a case by case basis.

        private updateSettings(options: VisualUpdateOptions) {

        }

Now let’s go to the bottom and add a new function.

This updatesettings function will grab those new settings from the metadata as they come and update our internal settings object as appropriate.

            this.settings.axis.x.show.value = DataViewObjects.getValue(
                options.dataViews[0].metadata.objects, {
                    objectName: "xAxis",
                    propertyName: "show"
                },
                this.settings.axis.x.show.default);

To do that, we can use the DataViewObjects helper we just imported.

This helper does the grunt work of inspecting the metadata view for a specific property and then getting its value back for you.

And if the property is not there to begin with, it can also return a default value for you instead.

All that’s left is to wire this up in the update function.

this.updateSettings(options);

We’ll add this right in the top, so we’re sure that the settings are always up-to-date every time the update code runs.

So let’s see what this did.

And there you go, the property now holds its value.

So now that we have a working property to show or hide the X axis, we can go back to the code and make some use it.

let xAxisPadding = this.settings.axis.x.show.value ? this.settings.axis.x.padding.value : 0;

So what we’re going to do here, is to take into account both the show property and the internal padding setting, to decide whether to take into account the axis or not.

If the user wants to see the axis, we will retain the padding value.

If not, we use a padding of value of zero just for this update, which will make the axis render off-screen, while everything else will adapt like magic.

Now let’s replace all the uses of the x padding value from settings with this local variable instead.

Let’s see what this did.

And there you go, now the chart adapts accordingly.

Cool, isn’t it.

And that’s it, now you’ve learned how to add a simple property to let the user setup the visual to their own taste.

We’ll stop here today, but in the git hub code base you will also find another property for the y axis and additional properties to configure the borders of the visual as well.

Still, I do recommend you try to implement these by yourself using the steps you have learned today.

Anyway, if you have questions, let me know and I’ll get back to you.

In next video in this series, you will learn how to add data bound settings to the properties pane to let the user himself control the colours of the bars.

Until then, take care and see you next time.

Adding Axes: Power BI Custom Visual Development Fundamentals #14

Learn how to add axes (as in axis of course) to your Power BI Custom Visual.
This video is part of a course, please check the playlist below for the set.

Resources

Playlist @ https://goo.gl/qbHDSq
Code @ https://goo.gl/7C4LeM
Dataset @ https://goo.gl/DEaZYY
D3 v3.x Docs @ https://goo.gl/xHZQ18

Transcript

Hello again and welcome back to this series on developing custom visuals for Power BI.

Today, you will focus on drawing axis on a a custom visual.

Using our growing little bar chart as an example, you will see how to add a categorical axis for the bars and a continuous axis for the values.

So let’s get to it right now.

        private settings = {
            axis: {
                x: {
                    padding: 50
                }
            }
        }

The first thing to do is actually make some room in the visual to put the x-axis on.

This means padding the visual contents on the bottom side so they go up a bit and allow enough space for the axis to live in.

As I don’t like to leave magic numbers around the place, I’m going to add a new property to the visual class called settings.

This will be a simple json object that will hold a collection of general settings for the visual, starting with a padding of 50 pixels.

I may refactor this into something far more beautiful on a future video, but for now, this will do just fine.

So now that we some padding defined, we just need to push the bars up by this amount.

The way you do this in D3 is by telling the yScale object that it should start drawing the lower value data points at a higher place in the SVG canvas.

.range([height - this.settings.axis.x.padding, 0]);

Since we know by how many pixels we want to push the bars up, we just to include those pixels into the calculation of the minimum y range value.

Now if you’re thinking like, wait what is he talking about, then I recommend you go and review video 9 of this series, where I explain the principle behind D3 scales and the relationship between domain values and range values.

Otherwise, let’s take a look of what this did.

So, as you may see here, if you’re really paying attention, the bars have grown a lit bit upwards.

Okay, so it’s a bit hard to see, but we can compare and see that the bar sizes are a bit bigger than the vanilla chart, even taking the stretching here into account.

That’s because the D3 scale is now drawing the data points of these bars slightly above what they were before.

However, this does not mean that the scale is now drawing each bar 20 pixels above the previous value.

Instead, the scale is now considering that the minimum data point of zero should be drawn at exactly 20 pixels from the bottom and then scaling all the data points accordingly.

Which is great, however, the bar is still filling down the whole thing, and that’s really not the scale’s fault.

To fix this, we need to go down to the rendering code and find the bit where we are defining the height of the SVG rectangle object for each bar.

height: d => height - yScale(d.value) - this.settings.axis.x.padding,

Then, we just subtract the padding that we defined for the x-axis.

And that’s it, let’s see what this it.

There you have it, as you can see the bars are now shorter and consistent with the vanilla visual as well.

Time to go make our axis happen.

private xAxisGroup: d3.Selection<SVGElement>;

We will start by creating a placeholder SVG group object.

This will hold all the rendering bits of the x-axis and will also allow us to easily position the axis on the visual.

this.xAxisGroup = this.svg.append("g")
                .classed("x-axis", true);

Of course, we need to instantiate this, so let’s go down to the constructor and add a bit more code.

Again, same as with the bar group, this is just an empty SVG group element, it does not render anything on its own, however we are setting a CSS class of x-axis, just in case we want to style it later down the process.

With this done, let’s go down to the rendering code, right after the bit where we are defining the x scale.

let xAxis = d3.svg.axis()
                .scale(xScale)
                .orient("bottom")
		.tickSize(1);

This is the best spot to define our x-axis, and we can do this with this bit of code.

This creates a D3 axis function that is able to render an axis on-screen.

It does this all on its own and using SVG elements in the html.

That’s why this is part of the SVG namespace in D3 version 3.

We then associate the axis with a scale so it is able to infer what type of axis we want to render.

Right now, we are associating this axis with a categorical scale, so the axis object will know that it needs to render a categorical axis as opposed to a continuous axis or a time axis or any other type of axis.

It will also know the range of values it needs to show on-screen.

We also set the orientation of the axis so it renders neatly as a bottom axis.

Having said that, a bottom style axis is the default in D3 so you don’t need to define this if you don’t want to but it’s always good to make things explicit.

Finally, we set the ticksize to 1.

This affects the size of main line in the axis and any ticks that it has.

Although I’m not a fan of magic numbers, I’m happy leaving this one here for now as we rarely need to change this at all.

Now this code, by itself, does not render the x-axis anywhere on-screen.

It only sets up the D3 object that is able to render the axis for us whenever we need to.

xAxis(this.xAxisGroup);

To make the actual rendering happen, we need to run the D3 axis function over the SVG placeholder we have prepared for it.

So let’s see what this did.

Okay, well, that did something, we have an x-axis.

It’s just not on a very good spot yet.

Let’s go back to the code and fix that.

this.xAxisGroup.call(xAxis);

First, I’m going to replace the D3 call code with something that is functionally equivalent.

Now this bit of code does exactly what the previous bit did, it’s just another way of writing the same thing.

this.xAxisGroup
                .call(xAxis)
                .attr({
                    transform: "translate(0, " + (height - this.settings.axis.x.padding) + ")"
                });

The bonus is, we can now keep calling other things on the axis group immediately after we render the axis on it.

And here we’re making use of the D3 attribute function to move the axis placeholder itself down to the bottom by applying a transform attribute.

Nothing to it, let’s see the result.

Look at that, we now have a categorical x-axis.

Thing is, it’s using default settings, so it’s a bit all over the place.

Let’s go and fix that.

.style({
                    fill: "#777777"
                })

First, that black there is somewhat strong on the eyes, so let’s make it a nice shade of grey instead.

.selectAll("text")

Now those labels were all over the place so we must do something about them.

The D3 axis function renders those labels as SVG text elements inside whatever container you give to it, so we can grab the lot by using the selectAll function.

This returns a D3 collection that lets you continue to apply attributes and style to all those text elements at the same time, just like we do when rendering the bars, for example.

So with that in mind, we just need to do a couple of things with the labels.

.attr({
                    transform: "rotate(-35)",
                })

First, we need to rotate them just a bit so that they don’t overlap and are easier to read.

                .style({
                    "text-anchor": "end",
                    "font-size": "x-small"
                });

And second, we just need to style the labels so that the text justifies to the right and the font isn’t as big as is it.

Now I’m going cheat here and use a relative size for the font, so I can get away with not creating settings for this yet, as that is in fact the theme of the next video.

So let’s see what this looks like now.

And there we go.

Now it looks like a proper axis.

Not only that, look at how it adjusts to the bars dynamically when you maximize it.

Cool, isn’t it?

So now that you understand how to add an x-axis, adding the y-axis should be a pice of cake.

Let’s go through the motions again.

y: {
                    padding: 50
                }

First, let’s get back to our settings variable and add a setting for some y-axis padding.

I’m going to use the same padding value for now, we can change it if we find we need more than this.

private yAxisGroup: d3.Selection<SVGElement>;

Now let’s go the visual variables and add a new placeholder SVG group for the y-axis as well…

this.yAxisGroup = this.svg.append("g")
                .classed("y-axis", true);

Let’s also instantiate this svg group in the constructor…

let yAxis = d3.svg.axis()
                .scale(yScale)
                .orient("left")
                .tickSize(1);

And let’s define the D3 axis function for this y-axis.

We do this almost the exact same way as we did for the x-axis.

The only difference is that we’re now telling D3 to orient this axis left instead of bottom.

this.yAxisGroup
                .call(yAxis)
                .attr({
                    transform: "translate(" + this.settings.axis.y.padding + ",0)"
                })
                .style({
                    fill: "#777777"
                })
                .selectAll("text")
                .style({
                    "text-anchor": "end",
                    "font-size": "x-small"
                });

The last thing to do is to actually render the axis on-screen.

Now for the y-axis, we will position it horizontally using the padding value we defined.

A left side axis starts rendering from the right, so this means that this will fill up all the area that we defined as padding.

So let’s see what this did.

Hmm, interesting.

So there is still an issue here that we need to address.

We still need to push and scale both the bars and the x-axis to the right so we have room for the y-axis.

We can do this is one go by affecting the xScale object in the rendering code.

.rangeRoundBands([this.settings.axis.y.padding, width], this.xPadding);

We just need to say that the data points need to start being rendered from the y-axis padding position and not from zero anymore.

Since the other D3 functions are re-using these, they will just pick this up and well, scale accordingly.

Let’s see what this did.

Almost, almost there.

Now if you notice, the top value on the y-axis is really risking getting a hair-cut there as it’s getting drawn right on the edge of the visual.

Fixing this one is straightforward, so let’s take care of this and finish the job.

border: {
                top: 10
            }

First, let’s add a border setting so we can say how many pixels of space we want to free up at the top.

Ten is fine for our purposes.

.range([height - this.settings.axis.x.padding, 0 + this.settings.border.top]);

Now let’s go to where we set the yScale target range and say that we want the maximum domain data value to render just below the top of the visual, which in screen coordinates is at zero, meaning we need to add our border value to it.

So let’s see what this did.

And there you have it.

Two nice axes on our visual.

That’s it for today.

If you have any questions, let me know and I’ll get back to you.

Next, you will learn how to add simple settings to the properties pane to let the user control whether these axis will show up or not.

Until then, take care and see you next time.

Adding Highlighting: Power BI Custom Visual Development Fundamentals #13

This video explains how to add highlighting functionality to your Power BI custom visual.

Video Transcript:

Hello again and welcome back.

Today we’ll take a look at how to add highlighting behaviour to our little custom bar chart.

If you’ve watched the previous video about interactions, you may be wondering, wait, didn’t we do that already?

Well, not quite.

What we did do was to handle clicks on our own custom visual and handle highlighting according to that.

If I click on the bars of our custom visual on the left side, its own bars will highlight themselves, and you’ll see that the vanilla bar chart in the right side will also highlight itself accordingly.

However, the opposite is not true.

If I click on some bars in the vanilla visual, our own visual will get filtered instead of highlighted.

So why the difference in behaviour?

Well, this really depends on the type of interactions that we have configured in the report and how our custom visual supports them.

If I click on our custom visual and then click View Interactions, I can see that the custom visual is set to highlight the vanilla visual.

If I change this setting to filter and then select a couple of bars, you will see that the vanilla bar chart is filtered instead.

If I change this back to highlight, we get highlighted columns again.

All far so good.

The thing is, if I click the vanilla visual to edit its visual interactions, I can see that I have no highlight option at all against the custom visual.

All my interactions will the vanilla visual will result in filtering the custom visual, without any way to change its behaviour.

Well, that’s what we going to enable today.

So let’s get to it.

The first step is tell Power BI that our visual does indeed support highlighting behaviour.

For that, we need to go into the capabilities.json file.

If you recall from video number 6 about creating a new project, this file is where you declare to Power BI what your visual can be expected to support, such as what data roles does it accept.

"supportsHighlight": true,

To declare we support highlighting, we can add the supportsHighlight property right here in the top.

So let’s save the project and examine what this accomplished.

Interesting, we now have a highlighting option there.

But does this actually do?

Let’s select it and play around.

Well, at first glance it appears to do nothing.

But it in fact, it is doing two things under the covers.

For starters, clicking the bars on the vanilla visual does not filter the custom visual anymore.

The custom visual is receiving the entire dataset now at all times, regardless of other selections.

The second thing, the custom visual is now also receiving something else.

Let’s click on the debug toolbar to explore the data view.

If we take a close look, we’re now receiving an extra bit of information.

In addition to the values array, which we were already getting, we are now receiving a new highlights array as well.

The way this works is that this array will have the same length as the values array and it will be populated for every value item that Power BI believes should be highlighted.

So on the vanilla visual, we can see that we have the second bar selected.

Right now, this corresponds to the second filled in value in the highlights array right here.

Of course, this beautiful and very unrealistic example is only working because both visuals are essentially showing the same data in the same order, so it’s easy to see the relationship between the two.

In reality, and depending on how you build your report, this highlights array won’t necessarily map one-to-one with anything obvious anywhere else, so don’t take this for granted.

Another thing you may have noticed is that the highlight value here is not exactly the same as the corresponding value in the values array.

That’s strange but it is fine.

The way to interpret the highlights array is that, if a given position has any value whatsoever, then that position is meant to be highlighted in whatever way your particular visual does it.

Now, before we move on, something to consider, is that the highlight functionality is not guaranteed to work all the time and with all the visuals, even all the vanilla ones.

There are certain filtering methods in the API that completely bypass the highlighting mechanism and will outright filter everything in a report, regardless of your setting.

Therefore, when you are building highlight into your visual, and you are testing and scratching your head on why things don’t seem to work, well, before anything, test against a visual that does work against highlighting such as the vanilla bar chart or pie chart.

If your visual is getting highlight data from either of those two, but not another one, then most likely the problem is with that other visual and not with yours to begin with.

Okay, so with all that out-of-the-way, let’s actually do some highlighting.

You may have guessed what’s the first thing to do.

That’s right, back we are to the DataPoint interface, our favourite spot in this entire code base.

Let’s add a new property named highlighted of type boolean.

As usual, the compiler complains about the viewmodel code, but we’ll fix that in a bit.

Before that, we’re going to add a helper property to the viewmodel interface itself.

highlights: boolean

This property is just here to help simplify the rendering code later, you will see what I mean.

Now let’s go down the view model loading code and add some more bits.

let highlights = values.highlights;

First let’s get that highlights array we saw back there.

Do note that this highlights array is an optional property so it may or it may be defined.

Don’t assume it and always test for it.

highlighted: highlights ? highlights[i] ? true : false : false

Now let’s add some code to define the highlighted property on each data point.

This just checks if the highlights array is defined to begin with and then whether it has any value in the same position of the corresponding values array.

Again, we don’t care about the actual value, we just care that we have anything other than null in there.

viewModel.highlights = viewModel.dataPoints.filter(d => d.highlighted).length > 0;

Now let’s just fill in that helper property in the view model.

This will be true if any data point is highlighted to begin with.

highlights: false

Of course, we also need to add a default to the initialization code in the top here.

The final step is to make this render something.

Let’s go to the rending code, in the bit where we define CSS style rules for each bar.

"fill-opacity": d => viewModel.highlights ? d.highlighted ? 1.0 : 0.5 : 1.0

Now we just need to a fill opacity style rule that acts on the basis of highlighting.

The criteria here is very simple.

If we have any highlights at all in view model, then we leave the highlighted bars at full opacity and we dim all other bars.

If we don’t have any highlights to begin with, then we don’t anything with the bars and leave them at full opacity.

That’s it.

Let’s save the code and see how this works.

There you have it.

Fully working highlighting.

Neat, isn’t it?

That’s it for today.

Thanks for watching and let me know if you have any questions.

Also if you’re new here, be sure to subscribe and enable notifications, so you’re the first to know about new content.

In the next video in the series, we will look at how to add simple properties to the property pane of your custom visual.

Until then, take care and see you next time.

Adding Interaction: Power BI Custom Visual Development Fundamentals #12

This video explains how to add interactions to your Power BI custom visual.

Video Transcript:

Show power bi app on vgsales report editor

Hello again and welcome back.

In this video, we’re gonna look at how to add simple interactions to our little custom bar chart.

If you look at the visual as it stands, you’ll notice that you can’t really click anything at all.

It doesn’t highlight any bar and it won’t filter anything.

In fact, to prove the point and make testing easier, let’s bring in a vanilla bar visual to the side here and set it up to look more or less the same as ours.

And there we have it, a side by side comparison of our custom visual vs versus the vanilla visual from power bi itself.

So what we can notice here is that, if I click on the bars of the vanilla visual, our custom visual will get filtered by that same slice of data I’m clicking on.

Each of these bars on the vanilla visual represents a slice on that particular category so when I click one or more of the bars, the custom visual is filtered on that slice or group of slices, as you can see here.

Now note that we didn’t have to develop anything for our custom visual to accept filtering.

This is something Power BI already does for you behind the scenes by filtering the corresponding data before it reaches your custom visual.

In this way the custom visual is completely oblivious to who or what is triggering that filter.

All it needs to care is, well, I’m receiving this data, I’m going to render as best I can.

And that’s precisely what you’re seeing here.

Now this is all nice and dandy but if I start clicking on our own custom bar chart, well, the result is… absolutely nothing.

The custom visual does not react to clicks and does not affect any other visual.

Now that’s because Power BI doesn’t really know what it is that we are rendering here.

We visually see some bars but Power BI has no oversight of anything happening in the visual’s box.

Therefore, we need to give Power BI hand.

All it takes is some simple steps.

The first thing we need to is to add a property to the visual called selectionManager of type ISelectionManager.

private selectionManager: ISelectionManager;

The selection manager is one of the ways we have to notify power bi of selection changes in our custom visual.

The selection manager allows us to tell power bi that hey, the user has selected something on this visual – you may want to do something about it.

Of course, we need to instantiate it, so let’s go the constructor.

We can call upon the host to create a brand new selection manager just for us.

The selection manager does keep some important state, so it’s best to create only one at startup and call upon it as needed.

this.selectionManager = this.host.createSelectionManager();

Now that we have the basics sorted, let’s get back to the DataPoint interface yet again.

This time we’re going to add a property called identity and this will of type ISelectionId.

Now this is special interface from the Power BI API.

This SelectionId literally represents a slice of data.

We can use these SelectionIds to say that each specific DataPoint represents a specific category or a specific measure or a specific series or a combination of any of these three.

These SelectionIds allow us to send data selection state To Power BI using any combination of slices, no matter how complex.

The Selection IDs themselves are fairly complicated objects, but you don’t need to worry about that too much, as the API helps us build them up from scratch.

One last thing though, I have written the namespace powerbi.visuals there on purpose.

That’s because there are two types called SelectionId in the API.

One is on the powerbi.visuals namespace and the other one is on the powerbi.extensibility namespace.

Currently the API supports two approaches for handling interactions, one more simple and do-it-yourself, and one more heavy-weight and framework driven.

Now for some reason the development team decided to duplicate these types between these two approaches.

The problem is that these two different SelectionId types are not fully interchangeable and using one type in place of the other can cause strange runtime bugs, even when compiling just fine.

Hopefully this is a temporary situation until this bit of the API is stabilized.

For now, out of the two approaches I’m showing you the simple do-it-yourself approach and that requires the SelectionId interface that lives in the powerbi.visuals namespace.

Anyway, the next step, as you can probably imagine, is to go yet again to the view model code and define a selectionId for each data point.

Trustworthy typescript complains as usual that you’re missing the identity property, so, we may as well make it happy.

identity: this.host.createSelectionIdBuilder()
                        .withCategory(categories, i)
                        .createSelectionId()

Let’s fill in the identity property here for each data point.

So we’re doing a couple of things here in single chained statement.

First, we’re asking the Power BI host to create a SelectionId builder object for us.

This gives up a helper object that lets us create the fairly complex SelectionIds from scratch.

Note that we have to create a new builder every time we need a new selection id.

We then say that this selectionId will represent a slice over our category column, whatever that column is in the model, using the category value at position i.

This is simply a generic way of saying, I want to slice by that specific category value.

By the way, we are not limited one slice criteria.

We can add and combine slices over Measures and series and other categories as well.

It’s up to us to say what slice of data this particular DataPoint identifies and we can make it as broad or particular as we want.

Once we’re happy with our slicing, we ask the builder to give us the corresponding SelectionId and we store that in the data point.

So with this we’ve taken care of associating each of our visual bars with a specific slice of data, at least in a way that Power BI can understand.

The last thing we need to do is to make the bars react to clicking and then tell Power BI that something happened and it should go and do something about it.

So for that, let’s get back to rendering code, yet again to the spot where we are defining the behaviour of bar.

What we want to do now is to make each bar react to a mouse click or a tap on the screen.

To wire that up, we can use a D3 on-click event.

 .on("click", (d) => {

                });

This goes through each single bar and wires up its click event to trigger the inline function we define here.

When that click happens, D3 does some magic for us and passes in the data point associated with that bar to the click event handler.

This syntax sugar allows to know exactly what bar was clicked and even saves us the trouble of looking the data point.

Yet another thumbs up for D3.

Now we want to do a number of different things here, so let’s take this step by step.

The most important thing is to tell power bi that the user has selected a slice of data.

this.selectionManager.select(d.identity);

The simplest way to do that is to call upon the selection manager and tell it that the user has selected a particular selection id, which we can get from the data point.

Now this is the simplest way, but not necessarily the best, and you can tell why straight away.

Let’s save the file and get to the report.

So let’s see what happens when I click on different bars.

So far so good.

But what if I click on the same one again?

Hmm. Interesting.

So what happening here, is that the default behaviour of the selection manager is to toggle whatever selection you provide to it.

This behaviour is to make it consistent with the vanilla charts, when you’re selecting one data point at a time, so that’s something to keep in mind.

this.selectionManager.select(d.identity, true);

If instead you want to enable multiple selection, you can pass in the parameter true to select method.

As you can see, we can toggle between all of them at will.

Now if you want to really mimic the vanilla visual, you’ll have to change between selecting one item and selecting multiple items, depending on whether the user is pressing a key like control on the keyboard.

However, I’ll leave that as an exercise for you.

For now, we are dearly missing something.

I’m selecting columns left and right, but there’s no user feedback that my selection are being applied.

What we want is a an effect similar to the vanilla visual, we want to dim the colours of all columns that have not been selected and leave only the ones we clicked as being highlighted.

.then(ids => {
                            bars.style({
                                'fill-opacity': ids.length > 0 ? 0.5 : 1.0
                            });
                        });

Let’s get back to the click event handler and add a bit more code to it.

So what we’re doing here is basically dimming all columns in the visual, depending on whether any column has been selected or not.

The then function of the selection manager gives us access to all selectionids that are currently, well, selected and saves us from having to go after them.

We then set the fill opacity style on every column to either 50% if there is at least one column selected or 100% if there is no selection at all.

Let’s see how this went.

Not bad.

All that’s left is to actually highlight the selected columns to begin with.

.then(ids => {
                            bars.style({
                                'fill-opacity': ids.length > 0 ?
                                    d => ids.indexOf(d.identity) >= 0 ? 1.0 : 0.5 :
                                    1.0
                            });
                        });

Let’s change our code here to a little bit smarter.

So we’re adding another condition here.

We’re now saying that if there are any columns selected, then we’re gonna check if each column is selected or not before we decide to dim or leave it highlighted.

For that, we can just see if the selection id or identity of the data point for that column is one of the currently selected selection ids.

That’s all there is to it.

Let’s what this did.

And there you have it.

Some nice interaction between your custom visual and others in the report.

Thanks for watching and make sure you thumbs and subscribe you want to see more videos like this.

In the next video in this series we will add highlighting behaviour to your custom visual so it can react to selections in other visuals, so make sure you enable YouTube notifications to be first one to know.

Until then, take care and see you next time.

Adding Colours: Power BI Custom Visual Development Fundamentals #11

This video explains how to add colours to your custom visual for Power BI.

Video Transcript:

Okay, so in this video we’re gonna look at how to add colours to your custom bar chart.

For the sake of the example, we’re going to paint each column with its own distinct colour.

Now in a real-world scenario, you will likely use a single colour per series of data, but, to just show you how this works, a colour per is bar far more useful.

Doing this is very easy and it only takes three steps.

First, we’re going to go back and update our data point.

If you recall from previous videos, each data points in this chart, really maps to a single bar in the chart, so that’s the element we want to affect.

So to do that, we’re gonna add a new property called, well, colour.

This will be a string and it will hold your typical hexadecimal colour codes you see in any other kind of web programming.

Okay, so now we update the data point class, we need to fill the actual data point items with their respective colors.

To do that, we need to go all the way to the getViewModel function.

You’ll notice in fact, that Visual Studio is already complaining about something here.

And correct because we just added a new property to the data point, so we have to fill up here when creating a new data point object.

That’s a thumbs up for typescript.

So anyway, let’s fix this by adding the colour to each data point.

colour: this.host.colorPalette.getColor(<string>categories.values[i]).value

Okay, so what we’re doing here is asking for some help from power bi.

host.colorPallete grants us access to the color theme that power bi is currently using.

This allows us to keep our colors consistent with whatever theme the report designer has decided to use.

In addition to that, the getColor function can take any string and associate that string with a theme color.

This works pretty much a global dictionary of name-to-color and this what allows different visuals to magically use the same color for the same bit of information, across an entire report.

In this case, we are requesting power bi to give us a specific color for each specific category name we provide.

This will make the chart look very cheerful, but in a more complex visual, you will usually request a different color per series and not per single data point.

Anyway, now that the view model is all setup, all we need is to render those data points accordingly.

To do that, let’s get back to the rendering, right to the bit where we are setting attributes on each bar.

So what we want to now, is, instead of adding a regular html attribute, we want to add a CSS style rule instead.

So to do that, we can use to the style function of the D3 bars object, and provide a json object with the CSS properties we want set.

To set the fill color of a rectangle SVG object, we just need to add a fill style here, a give it whatever colour the current data point has.

fill: d => d.colour

And that’s it, nothing to it.

Let’s save the project, let pbiviz do its thing and let’s see how the visual looks like now.

And there you have it, one very cheerful bar chart.

Thank you for watching and if you found this useful, remember to thumbs and join the notification squad so you’re the first to know about new content.

In next the video in this series, we’ll look at how to make those bars clickable so you can interact and filter other visuals in the report.

See ya next time.