Skip to main content
cancel
Showing results for 
Search instead for 
Did you mean: 

Compete to become Power BI Data Viz World Champion! First round ends August 18th. Get started.

Reply
vkokorin
Regular Visitor

Please! Help me make my custom visual.

Guys! Help me please! I am trying and trying and trying. But I can not to do anything.

I had install develop environment. It's work. I had download this sampleBarChart.

I do everything like in tutorial and it's working.

2016-11-02 (1).png

 

 

 

 

 

 

 

 

 

 

 

 

 

And it's working in web.

2016-11-02 (2).PNG

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

Cool! But I very need to custom it. And I don't know how to do this. I have the code of this visual.

Probably I will able to do this, but first I need to undestand how this code is working. Help me please to understand it. I very, very need it. I had called to microsoft technical support, but they didn't help me. They says that they don't support developing problems.

For example, I don't understand this part of code:

            if(settings.enableAxis.show) {
                let margins = BarChart.Config.margins;
                height -= margins.bottom;
            }

            this.xAxis.style({
                'font-size': d3.min([height, width]) * BarChart.Config.xAxisFontMultiplier,
            });

            let yScale = d3.scale.linear()
                .domain([0, viewModel.dataMax])
                .range([height, 0]);

            let xScale = d3.scale.ordinal()
                .domain(viewModel.dataPoints.map(d => d.category))
                .rangeRoundBands([0, width], BarChart.Config.xScalePadding, 0.2);

            let xAxis = d3.svg.axis()
                .scale(xScale)
                .orient('top');

            this.xAxis.attr('transform', 'translate(0, ' + height + ')')
                .call(xAxis);

            let bars = this.barContainer.selectAll('.bar').data(viewModel.dataPoints);
            bars.enter()
                .append('rect')
                .classed('bar', true);

            bars.attr({
                width: xScale.rangeBand(),
                height: d => height - yScale(<number>d.value),
                y: d => yScale(<number>d.value),
                x: d => xScale(d.category),
                fill: d => d.color,
                'fill-opacity': BarChart.Config.solidOpacity,
            });

What is it doing?

Thanks a lot in advance. I need your help!

1 ACCEPTED SOLUTION
matthiasnielsen
Advocate I
Advocate I

@MawashiKid is right, the code snippet relies on how D3 v3.x works - see API documentation: https://github.com/d3/d3-3.x-api-reference/blob/master/API-Reference.md

 

D3 has somewhat of a learning curve for the uninitiated. I'll give a explanation of the steps in your code snippet, which hopefully will give you a better understanding of the D3 mechanics at work. I do not know how you want to customize the visualization, so I cannot give input on that, but hopefully this will help you getting started customizing on your own. Beforehand, please familiarize yourself with the D3 Introduction, Selections, Dynamic Properties, and Enter and Exit sections found here: https://d3js.org/ 

 

Some of the steps are D3 black magic, so those are a matter of understanding and accepting D3 conventions and functionality. 

 

 

 

            // This is for making space for ticks on the horizontal axis if the horizontal axis is toggled in Power BI
// This uses settings defined in capabilities.json and the enumerateObjectInstances() function found elsewhere in the example, to work.
// I would say ignore this for now as this is for interfacing with Power BI interface widgets and not with creating D3 style visualizations in Power BI.
if(settings.enableAxis.show) { let margins = BarChart.Config.margins; height -= margins.bottom; }
// In the constructor, this.xAxis has been initialized as a 'g' element (group element)
// that has been appended to an SVG element that again has been appended to the root element of the visualization frame.
// Therefore, this.xAxis corresponds to a D3 DOM selection, which points to the group element that contains all DOM elements for the xAxis.
// What happens here is simply that font-size CSS property (https://developer.mozilla.org/en-US/docs/Web/SVG/Attribute/font-size) is being set.
// Don't be confused by the formula, it is just to make the text size scale with the size of the visualization (i.e. 4% of the width or height depending of which is lowest)
// Would you like to have a harcoded font-size (likely you wouldn't but for the sake of a simpler example), you could enter e.g. font-size: 12 or font-size: '1em' instead. this.xAxis.style({ 'font-size': d3.min([height, width]) * BarChart.Config.xAxisFontMultiplier, });
// This part creates a linear scale that maps data values to pixel values.
// In D3 scale terminology, domain is the input and range is the output.
// Because the yScale is a linear scale, both the domain and range functions, are passed an array with two values that describe the extent (min and max values)
// See https://github.com/d3/d3-3.x-api-reference/blob/master/Quantitative-Scales.md#linear let yScale = d3.scale.linear()
// The domain() function is passed an array parameter describing the extent of the input data.
// Here from 0 to dataMax (dataMax is derived when the visualTransform() function is called in the beginning of the update() function .domain([0, viewModel.dataMax])
// The range is passed an array parameter decribing the extent of the output.
// Don't be confused why the extent is described as going from height to 0. This is because SVG elements are described from top to bottom and left to right.
// See the y and height properties in the object passed to the bars.attr() function below for how this is used. .range([height, 0]);
// Because the example makes a bar chart, the scale for the horizontal axis needs to be ordinal.
// This means that that for the xScale the domain function is passed an array of possible values (and not an array describing the extent as for the linear scale above).
// Otherwise the domain() and rangeXxxxxYyyyy() functions still describe input and output respectively. let xScale = d3.scale.ordinal()
// In this example, the viewModel.dataPoints.map(d => d.category) function returns an arrays that looks like ['inb', 'int', 'out'] .domain(viewModel.dataPoints.map(d => d.category))
// rangeRoundBands() is a nice D3 function, which evenly distributes the number of elements in the array passed to the domain() function across the extent that is passed as first parameter.
// See https://github.com/d3/d3-3.x-api-reference/blob/master/Ordinal-Scales.md#ordinal_rangeRoundBands .rangeRoundBands([0, width], BarChart.Config.xScalePadding, 0.2);
// This is a D3 convinience function for creating the visual elements for an axis (a line, ticks, and tick texts).
// However, the axis is only declared virtually in this step. Actually, it is only a function that generates said visual elements.
// Either way, it is not added to the visualization yet (that is next step).
// See https://github.com/d3/d3-3.x-api-reference/blob/master/SVG-Axes.md#axis
// Remember this part is only about specifying the visual elements of the axis. let xAxis = d3.svg.axis()
// Because the xScale is passed to the scale() function, the visual elements are inferred from the xScale (e.g. the 'inb', 'int', and 'out' text values from the input domain) .scale(xScale)
// This specifies two things: a) the axis is a horizontal axis, and b) that the tick values should be above the axis line. .orient('top');
// This line moves the group for the horizontal axis (initialized in the constructor) to the bottom of the visualization.
// Remember that this.xAxis and xAxis are two different variables. this.xAxis.attr('transform', 'translate(0, ' + height + ')')
// This line calls the above declared xAxis() function and appends the elements to the group for the horizontal axis. .call(xAxis);
// In this step a 'rect' element https://developer.mozilla.org/en-US/docs/Web/SVG/Element/rect is created for each data point in the input data (a 'rect' element is used to depict a bar in the bar chart)
// I would say, don't worry too much about how the .selectAll(someSelector).data(dataArray).enter().append(elementName) works.
// Instead just observe that a DOM element (here a 'rect') is entered for each data element.
// A reference to the selection of these 'rect' elements are stored in a variable ('bars') so they can be referenced later. let bars = this.barContainer.selectAll('.bar').data(viewModel.dataPoints); bars.enter() .append('rect') .classed('bar', true);
// Before this step the visualization contains the visual elements for the horizontal axis and an (underdefined) 'rect' element for each data element.
// In this step the positions, sizes, and styling of the bars are defined.
// To do this, the previously defined xScale and yScale are used.
// First the .attr() function is called on the the bars variable.
// Because the bars variable containes a reference to the selection of all 'rect' elements, the passed parameter is applied to all elements.
// The 'd' variable used below is the data element for the corresponding 'rect' element and is made available by D3
// If you are not familiar with the arrow function syntax, you can refer to https://developer.mozilla.org/en/docs/Web/JavaScript/Reference/Functions/Arrow_functions and http://stackoverflow.com/questions/22939130/when-should-i-use-arrow-functions-in-ecmascript-6 bars.attr({
// .rangeBand() returns the width available for each 'rect' element to distribute them evenly across the width of the visualization. width: xScale.rangeBand(),
// The height attribute of the 'rect' element is set to a height corresponding to the value property of the d variable
// The height - yScale(number) is because SVG elements are described from top to bottom. This means the 'rect' elements are drawn from the top and down to the horizontal axis. height: d => height - yScale(<number>d.value),
// The vertical point of the top-left corner of the 'rect' element is fetched from the yScale y: d => yScale(<number>d.value),
// The horizontal point of the top-left corner of the 'rect' element is fetched from the xScale x: d => xScale(d.category),
// The color of the rect is set to the color property of the d variable (this is defined in the BarChartSettings elsewhere) fill: d => d.color,
// All 'rect' elements are defined to have the same fill-opacity (defined in the static Config object elsewhere) 'fill-opacity': BarChart.Config.solidOpacity, })

 

View solution in original post

4 REPLIES 4
matthiasnielsen
Advocate I
Advocate I

@MawashiKid is right, the code snippet relies on how D3 v3.x works - see API documentation: https://github.com/d3/d3-3.x-api-reference/blob/master/API-Reference.md

 

D3 has somewhat of a learning curve for the uninitiated. I'll give a explanation of the steps in your code snippet, which hopefully will give you a better understanding of the D3 mechanics at work. I do not know how you want to customize the visualization, so I cannot give input on that, but hopefully this will help you getting started customizing on your own. Beforehand, please familiarize yourself with the D3 Introduction, Selections, Dynamic Properties, and Enter and Exit sections found here: https://d3js.org/ 

 

Some of the steps are D3 black magic, so those are a matter of understanding and accepting D3 conventions and functionality. 

 

 

 

            // This is for making space for ticks on the horizontal axis if the horizontal axis is toggled in Power BI
// This uses settings defined in capabilities.json and the enumerateObjectInstances() function found elsewhere in the example, to work.
// I would say ignore this for now as this is for interfacing with Power BI interface widgets and not with creating D3 style visualizations in Power BI.
if(settings.enableAxis.show) { let margins = BarChart.Config.margins; height -= margins.bottom; }
// In the constructor, this.xAxis has been initialized as a 'g' element (group element)
// that has been appended to an SVG element that again has been appended to the root element of the visualization frame.
// Therefore, this.xAxis corresponds to a D3 DOM selection, which points to the group element that contains all DOM elements for the xAxis.
// What happens here is simply that font-size CSS property (https://developer.mozilla.org/en-US/docs/Web/SVG/Attribute/font-size) is being set.
// Don't be confused by the formula, it is just to make the text size scale with the size of the visualization (i.e. 4% of the width or height depending of which is lowest)
// Would you like to have a harcoded font-size (likely you wouldn't but for the sake of a simpler example), you could enter e.g. font-size: 12 or font-size: '1em' instead. this.xAxis.style({ 'font-size': d3.min([height, width]) * BarChart.Config.xAxisFontMultiplier, });
// This part creates a linear scale that maps data values to pixel values.
// In D3 scale terminology, domain is the input and range is the output.
// Because the yScale is a linear scale, both the domain and range functions, are passed an array with two values that describe the extent (min and max values)
// See https://github.com/d3/d3-3.x-api-reference/blob/master/Quantitative-Scales.md#linear let yScale = d3.scale.linear()
// The domain() function is passed an array parameter describing the extent of the input data.
// Here from 0 to dataMax (dataMax is derived when the visualTransform() function is called in the beginning of the update() function .domain([0, viewModel.dataMax])
// The range is passed an array parameter decribing the extent of the output.
// Don't be confused why the extent is described as going from height to 0. This is because SVG elements are described from top to bottom and left to right.
// See the y and height properties in the object passed to the bars.attr() function below for how this is used. .range([height, 0]);
// Because the example makes a bar chart, the scale for the horizontal axis needs to be ordinal.
// This means that that for the xScale the domain function is passed an array of possible values (and not an array describing the extent as for the linear scale above).
// Otherwise the domain() and rangeXxxxxYyyyy() functions still describe input and output respectively. let xScale = d3.scale.ordinal()
// In this example, the viewModel.dataPoints.map(d => d.category) function returns an arrays that looks like ['inb', 'int', 'out'] .domain(viewModel.dataPoints.map(d => d.category))
// rangeRoundBands() is a nice D3 function, which evenly distributes the number of elements in the array passed to the domain() function across the extent that is passed as first parameter.
// See https://github.com/d3/d3-3.x-api-reference/blob/master/Ordinal-Scales.md#ordinal_rangeRoundBands .rangeRoundBands([0, width], BarChart.Config.xScalePadding, 0.2);
// This is a D3 convinience function for creating the visual elements for an axis (a line, ticks, and tick texts).
// However, the axis is only declared virtually in this step. Actually, it is only a function that generates said visual elements.
// Either way, it is not added to the visualization yet (that is next step).
// See https://github.com/d3/d3-3.x-api-reference/blob/master/SVG-Axes.md#axis
// Remember this part is only about specifying the visual elements of the axis. let xAxis = d3.svg.axis()
// Because the xScale is passed to the scale() function, the visual elements are inferred from the xScale (e.g. the 'inb', 'int', and 'out' text values from the input domain) .scale(xScale)
// This specifies two things: a) the axis is a horizontal axis, and b) that the tick values should be above the axis line. .orient('top');
// This line moves the group for the horizontal axis (initialized in the constructor) to the bottom of the visualization.
// Remember that this.xAxis and xAxis are two different variables. this.xAxis.attr('transform', 'translate(0, ' + height + ')')
// This line calls the above declared xAxis() function and appends the elements to the group for the horizontal axis. .call(xAxis);
// In this step a 'rect' element https://developer.mozilla.org/en-US/docs/Web/SVG/Element/rect is created for each data point in the input data (a 'rect' element is used to depict a bar in the bar chart)
// I would say, don't worry too much about how the .selectAll(someSelector).data(dataArray).enter().append(elementName) works.
// Instead just observe that a DOM element (here a 'rect') is entered for each data element.
// A reference to the selection of these 'rect' elements are stored in a variable ('bars') so they can be referenced later. let bars = this.barContainer.selectAll('.bar').data(viewModel.dataPoints); bars.enter() .append('rect') .classed('bar', true);
// Before this step the visualization contains the visual elements for the horizontal axis and an (underdefined) 'rect' element for each data element.
// In this step the positions, sizes, and styling of the bars are defined.
// To do this, the previously defined xScale and yScale are used.
// First the .attr() function is called on the the bars variable.
// Because the bars variable containes a reference to the selection of all 'rect' elements, the passed parameter is applied to all elements.
// The 'd' variable used below is the data element for the corresponding 'rect' element and is made available by D3
// If you are not familiar with the arrow function syntax, you can refer to https://developer.mozilla.org/en/docs/Web/JavaScript/Reference/Functions/Arrow_functions and http://stackoverflow.com/questions/22939130/when-should-i-use-arrow-functions-in-ecmascript-6 bars.attr({
// .rangeBand() returns the width available for each 'rect' element to distribute them evenly across the width of the visualization. width: xScale.rangeBand(),
// The height attribute of the 'rect' element is set to a height corresponding to the value property of the d variable
// The height - yScale(number) is because SVG elements are described from top to bottom. This means the 'rect' elements are drawn from the top and down to the horizontal axis. height: d => height - yScale(<number>d.value),
// The vertical point of the top-left corner of the 'rect' element is fetched from the yScale y: d => yScale(<number>d.value),
// The horizontal point of the top-left corner of the 'rect' element is fetched from the xScale x: d => xScale(d.category),
// The color of the rect is set to the color property of the d variable (this is defined in the BarChartSettings elsewhere) fill: d => d.color,
// All 'rect' elements are defined to have the same fill-opacity (defined in the static Config object elsewhere) 'fill-opacity': BarChart.Config.solidOpacity, })

 

v-chuncz-msft
Community Support
Community Support

@vkokorin,

 

You will need to learn TypeScript and Less first. For tips about debugging your custom visual, see the debugging guide.

Community Support Team _ Sam Zha
If this post helps, then please consider Accept it as the solution to help the other members find it more quickly.

I do agree that both TypeScript and less play a major role in Power BI Visuals but I would say that the true essence of customization in this case [as code sample shows] mainly deals with mastering d3.js logic. Library was created by Mike Bostock and contains an impressive variety of possibilities. So I would say that getting familiar with d3.js is an option that shouldn't be neglected.
Riaon
Microsoft Employee
Microsoft Employee

What's your custom view? Maybe this forum will not help user make design too as it does help solve the issue in the process.

Helpful resources

Announcements
August Power BI Update Carousel

Power BI Monthly Update - August 2025

Check out the August 2025 Power BI update to learn about new features.

August 2025 community update carousel

Fabric Community Update - August 2025

Find out what's new and trending in the Fabric community.