Forum Discussion

Silko's avatar
Silko
Helper II
10 years ago
Solved

Custom visual text input (search)

Hi,

 

I have created a custom visual with a text input, which I wanted to use for a search through slicer. For the start I made just a search through table. It worked perfectly fine on development tools.



But when I import it into power bi desktop I can't use the input. It looks like it is disabled or keyboard events are not being processed. 

Why is that?

9 Replies

  • mike_honey's avatar
    mike_honey
    Memorable Member

    This is a great concept - I'd really like to help get it working. Can you share your code so far? Github?

     

    Have you tried publishing that PBI Desktop file to the PBI web service and testing it there?

    • Silko's avatar
      Silko
      Helper II

      Hi, thanks for your replay. I tried web service as well but it is the same.

      I will share my code here:

      module powerbi.visuals {
          export interface CategoryViewModel {
              value: string;
              identity: string;
              color: string;
          }
      
          export interface ValueViewModel {
              values: any[];
          }
      
          export interface ViewModel {
              header: any;
              values: ValueViewModel[];
          }
      
          export class MySlicer implements IVisual {
              public static capabilities: VisualCapabilities = {
                  // This is what will appear in the 'Field Wells' in reports
                  dataRoles: [
                      {
                          name: 'Category',
                          kind: powerbi.VisualDataRoleKind.Grouping,
                      }
                  ],
                  // This tells power bi how to map your roles above into the dataview you will receive
                  dataViewMappings: [{
                      categorical: {
                          categories: {
                              for: { in: 'Category' },
                              dataReductionAlgorithm: { top: {} }
                          }
                      }
                  }],
                  // Objects light up the formatting pane
                  objects: {
                      general: {
                          displayName: data.createDisplayNameGetter('Visual_General'),
                          properties: {
                              formatString: {
                                  type: { formatting: { formatString: true } },
                              },
                          },
                      },
                  }
              };
      
              public static converter(dataView: DataView, colors: IDataColorPalette): ViewModel {
                  var viewModel: ViewModel = {
                      header: 'Default Header',
                      values: []
                  }
                  if (dataView) {
                      var data = dataView.categorical.categories;
                      var metadata = dataView.metadata;
                      
                      viewModel.header = metadata.columns[0].displayName;
                      if (data && data.length > 0) {
                          for (var i = 0, catLength = data[0].values.length; i < catLength; i++) {
                              viewModel.values.push(data[0].values[i]);
                          }
                      }
                  }
      
                  return viewModel;
              }
      
              private hostContainer: JQuery;
              private table: D3.Selection;
              private tHead: D3.Selection;
              private tBody: D3.Selection;
              private colorPalette: IDataColorPalette;
              private searchInput: D3.Selection;
              private searchString: string;
      
              /** This is called once when the visual is initialially created */
              public init(options: VisualInitOptions): void {
                  this.colorPalette = options.style.colorPalette.dataColors;
                  // element is the element in which your visual will be hosted.
                  this.hostContainer = options.element.css('overflow-x', 'hidden');
                  this.searchInput = d3.select(options.element.get(0))
                      .append("input")
                      .classed("my-input", true)
                      .attr("type", "text");
                  this.table = d3.select(options.element.get(0))
                      .append("table")
                      .classed('powerbi-sample-table', true);
      
                  this.tHead = this.table.append('thead').append('tr').append('th');
                  this.tBody = this.table.append('tbody');
                  
                  var slicerVisual = this;
                  this.searchInput.on("keyup", function () {
                      var searchString = slicerVisual.searchString =  $(this).val().toLowerCase();
                      slicerVisual.onSearchInput(slicerVisual, searchString);
                  });
              }
              
              public onSearchInput(slicerVisual, searchString) {
                  var tds = slicerVisual.hostContainer.find("tbody tr");
                  tds.each(function() {
                      var rowString = $(this).text().toLowerCase();
                      if(!searchString || rowString.indexOf(searchString) > -1)
                          $(this).removeClass("hidden");
                      else
                          $(this).addClass("hidden");
                  })
              }
      
              /** Update is called for data updates, resizes & formatting changes */
              public update(options: VisualUpdateOptions) {
                  var dataViews = options.dataViews;
                  if (!dataViews) return;
      
                  this.updateContainerViewports(options.viewport);
      
                  var viewModel = MySlicer.converter(dataViews[0], this.colorPalette);
                  var slicerTitle = this.tHead.text(viewModel.header);
                  var slicerData = this.tBody;
                  this.tBody.html("");
                  for(var row in viewModel.values) {
                      var rowString = viewModel.values[row] + "";
                      rowString = rowString.toLowerCase();
                      if(!this.searchString || rowString.indexOf(this.searchString) > -1)
                          slicerData.append('tr').append('td').text(viewModel.values[row]);
                      else    
                          slicerData.append('tr').classed("hidden", true).append('td').text(viewModel.values[row]);
                  }
              }
      
              private updateContainerViewports(viewport: IViewport) {
                  var width = viewport.width;
                  var height = viewport.height;
      
                  this.tHead.classed('dynamic', width > 400);
                  this.tBody.classed('dynamic', width > 400);
      
                  this.hostContainer.css({
                      'height': height,
                      'width': width
                  });
                  this.table.attr('width', width);
              }
              
              private format(d: number){
                  var prefix = d3.formatPrefix(d);
                  return d3.round(prefix.scale(d),2) + ' ' +prefix.symbol
              }
          }
      }