Forum Discussion

TheMoM's avatar
TheMoM
Regular Visitor
2 years ago

Issue with filtering not working after pbiviz package in Power BI Custom Visual

Hello Power BI Community,

I am experiencing an issue with a custom Power BI visual that I am developing
. The visual works perfectly during development when using pbiviz start, but after packaging it using pbiviz package, the filtering functionality does not work at all, no values are modified in the report where I m using it because of the filtering.

There are no errors or missing steps shown in the console related to the filtering
. In picture you have my applydatefilter function that I use for the filtering in my custom visual if you want to check.


Has anyone encountered a similar issue with Power BI custom visuals
? What could be the possible reasons for this discrepancy between development and packaged visual behavior?

Thank you in advance for your assistance!

2 Replies

    • TheMoM's avatar
      TheMoM
      Regular Visitor

      The visual of the filter work well, what  does not work is the filter when I m using pbiviz package which means when modifying the dates, the other data in the report does not change, but somehow this problem is not present when using pbiviz start to show the visual.  I m also thinking right now that the problem is not in the filter function, so I will give you my entire visual.ts code if you want to check. Also, the console.log will not help me to find the error because when using it every function seem to work fine.

      Here’s my visual.ts code :


      "use strict";

      // Import necessary libraries and styles
      import "core-js/stable";
      import "regenerator-runtime/runtime";
      import './../style/visual.less';
      import 'bootstrap-slider/dist/css/bootstrap-slider.min.css';
      import Slider from 'bootstrap-slider';
      import * as $ from 'jquery';
      import powerbi from "powerbi-visuals-api";

      // Import Power BI types
      import DataView = powerbi.DataView;
      import IVisualHost = powerbi.extensibility.IVisualHost;
      import VisualConstructorOptions = powerbi.extensibility.visual.VisualConstructorOptions;
      import VisualUpdateOptions = powerbi.extensibility.visual.VisualUpdateOptions;
      import { AdvancedFilter, IFilterColumnTarget } from "powerbi-models";

      // Import settings model
      import { VisualFormattingSettingsModel } from "./settings";

      // Interface for date range data points
      interface DateRangeDataPoint {
      category: string;
      minDate: string;
      maxDate: string;
      }

      // Main Visual class
      export class Visual implements powerbi.extensibility.visual.IVisual {
      private target: HTMLElement;
      private host: IVisualHost;

      private dateRangeContainer: HTMLDivElement;
      private dateInputStart: HTMLInputElement;
      private dateInputEnd: HTMLInputElement;
      private sliderElement: HTMLInputElement;
      private sliderInstance: Slider;

      private settings: VisualFormattingSettingsModel;
      private minDateValue: number;
      private maxDateValue: number;
      private isApplyingFilter: boolean = false;
      private lastAppliedStartDate: string;
      private lastAppliedEndDate: string;

      // Constructor
      constructor(options: VisualConstructorOptions) {
      this.target = options.element;
      this.host = options.host;

      // Create date range container and inputs
      this.dateRangeContainer = document.createElement('div');
      this.dateRangeContainer.className = 'date-range-container';

      this.dateInputStart = document.createElement('input');
      this.dateInputStart.type = 'date';
      this.dateInputStart.className = 'date-input';

      this.dateInputEnd = document.createElement('input');
      this.dateInputEnd.type = 'date';
      this.dateInputEnd.className = 'date-input';

      this.sliderElement = document.createElement('input');
      this.sliderElement.type = 'text';
      this.sliderElement.className = 'slider';

      this.dateRangeContainer.appendChild(this.dateInputStart);
      this.dateRangeContainer.appendChild(this.dateInputEnd);

      this.target.appendChild(this.dateRangeContainer);
      this.target.appendChild(this.sliderElement);

      this.initializeSlider();

      // Add event listeners to date inputs
      this.dateInputStart.addEventListener('change', () => this.onDateInputChange());
      this.dateInputEnd.addEventListener('change', () => this.onDateInputChange());
      }

      // Initialize the slider
      private initializeSlider() {
      this.sliderInstance = new Slider(this.sliderElement, {
      min: 0,
      max: 100,
      value: [0, 100],
      tooltip: 'hide',
      tooltip_split: false
      });

      // Add event listener for slider stop event
      this.sliderInstance.on('slideStop', (event) => {
      const values = this.sliderInstance.getValue();
      const minDate = values[0];
      const maxDate = values[1];
      this.dateInputStart.value = new Date(minDate).toISOString().split('T')[0];
      this.dateInputEnd.value = new Date(maxDate).toISOString().split('T')[0];
      this.applyDateFilter(this.dateInputStart.value, this.dateInputEnd.value);
      });
      }

      // Update slider range
      private updateSliderRange(minDateValue: number, maxDateValue: number) {
      if (this.minDateValue !== minDateValue || this.maxDateValue !== maxDateValue) {
      this.sliderInstance.setAttribute('min', minDateValue);
      this.sliderInstance.setAttribute('max', maxDateValue);
      this.sliderInstance.setValue([minDateValue, maxDateValue]);

      this.dateInputStart.value = new Date(minDateValue).toISOString().split('T')[0];
      this.dateInputEnd.value = new Date(maxDateValue).toISOString().split('T')[0];

      // Save the new values to prevent unnecessary updates
      this.minDateValue = minDateValue;
      this.maxDateValue = maxDateValue;
      }
      }

      // Update function called by Power BI
      public async update(options: VisualUpdateOptions) {
      if (this.isApplyingFilter) {
      return;
      }

      if (options.dataViews && options.dataViews[0]) {
      const dataView: DataView = options.dataViews[0];
      const dateRangeDataPoints = this.createSelectorDataPoints(options, this.host);

      if (dateRangeDataPoints.length > 0) {
      const newMinDateValue = new Date(dateRangeDataPoints[0].minDate).getTime();
      const newMaxDateValue = new Date(dateRangeDataPoints[0].maxDate).getTime();

      if (newMinDateValue !== this.minDateValue || newMaxDateValue !== this.maxDateValue) {
      this.updateSliderRange(newMinDateValue, newMaxDateValue);
      this.applyDateFilter(this.dateInputStart.value, this.dateInputEnd.value);

      // Sleep for 10 seconds to prevent re-entrant calls
      await new Promise((resolve) => setTimeout(resolve, 10000));
      }
      } else {
      this.clear();
      }
      } else {
      this.clear();
      }
      }

      // Create selector data points from data view
      private createSelectorDataPoints(options: VisualUpdateOptions, host: IVisualHost): DateRangeDataPoint[] {
      const dateRangeDataPoints: DateRangeDataPoint[] = [];
      const dataViews = options.dataViews;

      if (!dataViews || !dataViews[0]) {
      return dateRangeDataPoints;
      }

      const dataView = dataViews[0];
      if (!dataView.categorical) {
      return dateRangeDataPoints;
      }

      const categorical = dataView.categorical;
      if (!categorical.categories || !categorical.values) {
      return dateRangeDataPoints;
      }

      const category = categorical.categories[0];
      const minDateValue = categorical.values[0];
      const maxDateValue = categorical.values[1];

      if (!category || !minDateValue || !maxDateValue) {
      return dateRangeDataPoints;
      }

      // Assuming you only need the first value
      dateRangeDataPoints.push({
      category: `${category.values[0]}`,
      minDate: this.ensureDateFormat(`${minDateValue.values[0]}`),
      maxDate: this.ensureDateFormat(`${maxDateValue.values[0]}`)
      });

      return dateRangeDataPoints;
      }

      // Ensure date format is correct
      private ensureDateFormat(dateString: string): string {
      const date = new Date(dateString);
      return date.toISOString().split('.')[0] + "Z";
      }

      // Clear the visual
      private clear() {
      this.sliderInstance.setValue([0, 100]);
      this.dateInputStart.value = "";
      this.dateInputEnd.value = "";
      }

      // Event handler for date input change
      private onDateInputChange() {
      const startDate = new Date(this.dateInputStart.value).getTime();
      const endDate = new Date(this.dateInputEnd.value).getTime();
      this.sliderInstance.setValue([startDate, endDate]);
      this.applyDateFilter(this.dateInputStart.value, this.dateInputEnd.value);
      }

      // Apply date filter
      private async applyDateFilter(startDate: string, endDate: string): Promise<void> {
      if (this.lastAppliedStartDate === startDate && this.lastAppliedEndDate === endDate) {
      return;
      }

      this.lastAppliedStartDate = startDate;
      this.lastAppliedEndDate = endDate;

      const target: IFilterColumnTarget = {
      table: "document",
      column: "document_creation_date"
      };

      const filter = new AdvancedFilter(target, "And", [
      {
      operator: "GreaterThanOrEqual",
      value: this.ensureDateFormat(startDate)
      },
      {
      operator: "LessThanOrEqual",
      value: this.ensureDateFormat(endDate)
      }
      ]);

      this.isApplyingFilter = true;
      (this.host as any).applyJsonFilter(
      filter,
      "dataPoint",
      "filter",
      powerbi.FilterAction.merge
      );
      this.isApplyingFilter = false;}
      }