Base solution for your next web application

Activities of "aslam.loni"

Hi @ismcagdas,

We are building an Accounting and Financial Modelling related system. Hence, on the dashboard we have widgets that can be mapped to specific accounts selected by the user. The widgets will then show data with various filters such as Monthly, Quarterly, Yearly, under different budgets, scenarios, etc. Here, it is important to note that every user may want to add different accounts on their dashboard depending upon their role in the organization, type of the organization, organizational goals etc.

Hence, we need to create a Custom Account Widget that can be mapped by the users to any account of their choice. Since there can be multiple accounts being watched, it would be useful to have multiple instances of this widget bound to different Account Numbers.

Hi @ismcagdas,

Unfortunately this would not work for us since we want to add the same widget multiple times to the dashboard and bind each instance to a specific value. We want to then retrieve the data for the widget from the database using this specific value.

Can you please suggest any alternative way to implement same or similar functionality?

Hi @musa.demir,

Essentially we would like to pass Widget.SomeProperty ( here Widget refers to the Widget Class defined in service-proxies.ts ) from customizable-dashboard.component.html to widget-{WidgetName}.component.ts

Can you please explain the steps needed to achieve this? We are not able to understand the relationship between the code shared above and what we need to implement exactly.

Hi @musa.demir

Looking forward to hear from you.

Regards, Aslam

Hi @musa.demir

I have github access to this URL. I have read that code and implimented in our sourc code. It's not working as we want in our application. Do you have any other solution. Our release is stuck due to this issue. Looking forward to hear from you as soon as possible.

Please see below error during execution

ERROR NullInjectorError

core.mjs:6494 
        
       ERROR NullInjectorError: R3InjectorError(MainModule)[InjectionToken WidgetOnResizeEventHandlerToken -> InjectionToken WidgetOnResizeEventHandlerToken -> InjectionToken WidgetOnResizeEventHandlerToken -> InjectionToken WidgetOnResizeEventHandlerToken]: 
  NullInjectorError: No provider for InjectionToken WidgetOnResizeEventHandlerToken!
    at NullInjector.get (core.mjs:11157:1)
    at R3Injector.get (core.mjs:11324:1)
    at R3Injector.get (core.mjs:11324:1)
    at R3Injector.get (core.mjs:11324:1)
    at NgModuleRef.get (core.mjs:21888:1)
    at R3Injector.get (core.mjs:11324:1)
    at NgModuleRef.get (core.mjs:21888:1)
    at Object.get (core.mjs:21565:1)
    at lookupTokenUsingModuleInjector (core.mjs:3367:1)
    at getOrCreateInjectable (core.mjs:3479:1)

Please see below code block

customizable-dashboard.component.html

<gridster-item *ngFor="let widget of page.widgets" [item]="widget.gridInformation">
                                    <ng-container *ngComponentOutlet="widget.component"></ng-container>
                                    <gridster-item *ngFor="let widget of page.widgets" [item]="widget.gridInformation" (itemResize)="onGridSterItemResize(widget)">
                                        <ng-container *ngComponentOutlet="widget.component; injector:this.widgetSubjects[widget.guid].injector"></ng-container>
                                        <button
                                        class="btn btn-sm text-white bg-danger deleteWidgetButton"
                                        *ngIf="editModeEnabled"
                                        (click)="removeItem(widget.gridInformation)"
                                    >
                                        <i class="la la-times"></i>
                                    </button>
                                    </gridster-item>
                                    
                                </gridster-item>

customizable-dashboard.component.ts

import { GuidGeneratorService } from '@shared/utils/guid-generator.service';

export const WIDGETONRESIZEEVENTHANDLERTOKEN = new InjectionToken<WidgetOnResizeEventHandler>('WidgetOnResizeEventHandlerToken');

@Injectable()
export class WidgetOnResizeEventHandler {
  onResize: Subject<any> = new Subject();
}

initializeUserDashboardDefinition(userDashboardResultFromServer: Dashboard, dashboardDefinitionResult: DashboardOutput) {
        this.userDashboard = {
            dashboardName: this.dashboardName,
            filters: [],
            pages: userDashboardResultFromServer.pages.map((page) => {
                //gridster should has its own options
                this.options.push(this.getGridsterConfig());

                if (!page.widgets) {
                    return {
                        id: page.id,
                        name: page.name,
                        widgets: [],
                    };
                }

                //only use widgets which dashboard definition contains and have view definition
                //(dashboard definition can be changed after users save their dashboard, because it depends on permissions and other stuff)
                page.widgets = page.widgets.filter(
                    (w) =>
                        dashboardDefinitionResult.widgets.find((d) => d.id === w.widgetId) &&
                        this.getWidgetViewDefinition(w.widgetId)
                );

                return {
                    id: page.id,
                    name: page.name,
                    widgets: page.widgets.map((widget) => {
                        return {
                            id: widget.widgetId,
                            guid: this._guidGenerator.guid(),
                            //View definitions are stored in the angular side(a component of widget/filter etc.) get view definition and use defined component
                            component: this.getWidgetViewDefinition(widget.widgetId).component,
                            gridInformation: {
                                id: widget.widgetId,
                                cols: widget.width,
                                rows: widget.height,
                                x: widget.positionX,
                                y: widget.positionY,
                                uniqueWidgetId:widget.uniqueWidgetId,
                                customAccountNumber:widget.customAccountNumber,
                            },
                        };
                    }),
                };
            }),
        };
    }
    
    ngOnInit() {
    
    //abp.event.trigger('app.dashboardFilters.dateRangePicker.onDateChange', this.selectedDateRange);
    forkJoin([
            this._dashboardCustomizationServiceProxy.getUserDashboard(this.dashboardName, DashboardCustomizationConst.Applications.Angular),
            this._dashboardCustomizationServiceProxy.getDashboardDefinition(this.dashboardName, DashboardCustomizationConst.Applications.Angular)
        ]
          )
      .subscribe(([userDashboardResultFromServer, dashboardDefinitionResult]) => {
        this.dashboardDefinition = dashboardDefinitionResult;
        if (!this.dashboardDefinition.widgets || this.dashboardDefinition.widgets.length === 0) {
          this.loading = false;
          this.busy = false;
          return;
        }

        if (!userDashboardResultFromServer.pages ||
          userDashboardResultFromServer.pages.length === 0 ||
          userDashboardResultFromServer.pages.flatMap(page=>page.widgets).length === 0
          ) {
             this.loading = false;
             this.busy = false;
             return;
        }

        this.initializeUserDashboardDefinition(userDashboardResultFromServer, dashboardDefinitionResult);
        this.initializeUserDashboardFilters();
        this.createWidgetSubjects();
     
        //select first page (if user delete all pages server will add default page to userDashboard.)
        this.selectedPage = {
          id: this.userDashboard.pages[0].id,
          name: this.userDashboard.pages[0].name
        };
    
    }

 onGridSterItemResize(item: any): void {
      if (this.editModeEnabled) {
          if (this.widgetSubjects[item.guid]) {
              this.widgetSubjects[item.guid].handler.onResize.next(null);
          }
      }
  }

  createWidgetSubjects() {
    for (let i = 0; i < this.userDashboard.pages.length; i++) {
        var page = this.userDashboard.pages[i];
        for (let i = 0; i < page.widgets.length; i++) {
            const widget = page.widgets[i];
            this.createWidgetSubject(widget.guid);
        }
    }
}


createWidgetSubject(guid: string) {
  var handler = new WidgetOnResizeEventHandler();
  this.widgetSubjects[guid] = {
      handler,
      injector: Injector.create({
          providers: [{ provide: WIDGETONRESIZEEVENTHANDLERTOKEN, useValue: handler }],
          parent: this._injector
      })
  }
}

widget-profit-share.component.html

export class WidgetProfitShareComponent extends WidgetComponentBaseComponent implements OnInit {
    profitSharePieChart: ProfitSharePieChart;

    constructor(injector: Injector, 
        private _dashboardService: TenantDashboardServiceProxy,
        @Inject(WIDGETONRESIZEEVENTHANDLERTOKEN) private _widgetOnResizeEventHandler: WidgetOnResizeEventHandler
        ) {
        super(injector);
        this.profitSharePieChart = new ProfitSharePieChart(this._dashboardService);
        _widgetOnResizeEventHandler.onResize.subscribe(() => { this.profitSharePieChart.reload(); });
    }

    ngOnInit() {
        this.profitSharePieChart.reload();
    }
}

Hi @musa.demir

I tried to implement code from above github link but it is not working as expected. Do you have any other solution or code to refer?

Regards, Aslam

Hi,

looking forward to hear from you.

Regards, Aslam

Hi Team,

We have done migration from 10.2 to 11.2.1 angular for multiple widgets. In new version of angular application multiple customized widgets.

Please see below design page for customizable dashboard. we have added two customized widgets. it's showing same result.

we have added two properties uniqueWidgetId and customAccountNumber in gridInformation.

on the basis of these properties we need to initialise the customized widgets. we want to send uniqueWidgetId and customAccountNumber to the customized widgets component from customizable dashboard component. how to send these id's to customized widgets ?.

Hi,

I was raised issue regarding Modal opened within widget not able to access (#11084). You were provided some temporary solution. It's working but closing modal dailog automatically if i click/scroll down through the mouse.

Also getting error at the time of modal opening. Please see below screen shot.

code :

show(): void {
    this.modal.show();
  }

Temporary Solution was :

Please do the needfull.

Hi @ismcagdas,

Can you arrange the call on high priority? We need to fix this issue as soon as possible.

Thanks.

Showing 1 to 10 of 15 entries