Kęstutis Ramulionis
Kęstutis Ramulionis

Reputation: 290

How do I dynamically insert Ignite UI grid for Angular?

I have taken this as an example: Hierachical grid

So I am trying to insert whole html snippet dynamically but struggling. Inserting right into DOM doesn't seem to work. innerHTML property also does not help. I have found out this Dynamic hooks for angular which I've been trying to use as so:

<div class="grid__wrapper">
  <ngx-dynamic-hooks [content]="grid_html"></ngx-dynamic-hooks>
</div>

I have defined my grid html in typescript:

export class UsersGridComponent implements OnInit {
  public years = 10;
  public localdata: any[];
  public col: IgxColumnComponent;
  public pWidth: string;
  public nWidth: string;
  public singers: any[];
  data: SafeHtml;
  constructor(private sanitizer: DomSanitizer) {
    this.singers = SINGERS;
    this.localdata = this.singers;
    this.data = this.sanitizer.sanitize(SecurityContext.HTML, this.grid_html);
  }
  ngOnInit(): void {}

  grid_html: any = `<igx-hierarchical-grid
  IgxPreventDocumentScroll
  class="hierarchicalGrid"
  [data]="localdata"
  (columnResized)="onResize($event)"
  [autoGenerate]="false"
  [height]="'480px'"
  [width]="'100%'"
  [rowHeight]="'65px'"
  #hierarchicalGrid
>
  <igx-column field="Artist" [resizable]="true"></igx-column>
  <igx-column field="Photo" [resizable]="true" [minWidth]="'115px'">
    <ng-template igxCell let-cell="cell">
      <div class="cell__inner_2">
        <img [src]="cell.value" class="photo" />
      </div>
    </ng-template>
  </igx-column>
  <igx-column
    field="Debut"
    [resizable]="true"
    [minWidth]="'88px'"
    [maxWidth]="'230px'"
    dataType="number"
    [formatter]="formatter"
  ></igx-column>
  <igx-column
    field="GrammyNominations"
    header="Grammy Nominations"
    [resizable]="true"
  ></igx-column>
  <igx-column
    field="GrammyAwards"
    header="Grammy Awards"
    [resizable]="true"
  ></igx-column>
  <igx-column
    field="Sales"
    header="Album Sales in last {{ this.years }} years"
    [width]="'230px'"
    [filterable]="false"
  >
    <ng-template igxCell let-val>
      <igx-sparkline
        height="40px"
        width="220px"
        [dataSource]="val"
        valueMemberPath="Copies"
        displayType="Line"
        lineThickness="2"
        brush="rgb(255,102,0)"
      >
      </igx-sparkline>
    </ng-template>
  </igx-column>

  <igx-row-island [height]="null" [key]="'Albums'" [autoGenerate]="false">
    <igx-column field="Album" [resizable]="true"></igx-column>
    <igx-column
      field="LaunchDate"
      header="Launch Date"
      [resizable]="true"
      [dataType]="'date'"
    ></igx-column>
    <igx-column
      field="BillboardReview"
      header="Billboard Review"
      [resizable]="true"
    ></igx-column>
    <igx-column
      field="USBillboard200"
      header="US Billboard 200"
      [resizable]="true"
    ></igx-column>
    <igx-row-island [height]="null" [key]="'Songs'" [autoGenerate]="false">
      <igx-column field="Number" header="No." [resizable]="true"></igx-column>
      <igx-column field="Title" [resizable]="true"></igx-column>
      <igx-column
        field="Released"
        dataType="date"
        [resizable]="true"
      ></igx-column>
      <igx-column field="Genre" [resizable]="true"></igx-column>
    </igx-row-island>
  </igx-row-island>

  <igx-row-island [height]="null" [key]="'Tours'" [autoGenerate]="false">
    <igx-column field="Tour" [resizable]="true"></igx-column>
    <igx-column
      field="StartedOn"
      header="Started on"
      [resizable]="true"
    ></igx-column>
    <igx-column field="Location" [resizable]="true"></igx-column>
    <igx-column field="Headliner" [resizable]="true"></igx-column>
  </igx-row-island>
</igx-hierarchical-grid>`;

  public onResize(event) {
    this.col = event.column;
    this.pWidth = event.prevWidth;
    this.nWidth = event.newWidth;
  }

  public formatter = (a) => a;
}

I have imported necessary modules:

const componentParsers: Array<HookParserEntry> = [
  { component: IgxHierarchicalGridComponent },
  { component: IgxColumnComponent },
  { component: IgxRowIslandComponent },
];

@NgModule({
  declarations: [UsersGridComponent],
  imports: [
    BrowserModule,
    BrowserAnimationsModule,
    FormsModule,
    IgxPreventDocumentScrollModule,
    IgxHierarchicalGridModule,
    IgxSparklineCoreModule,
    IgxSparklineModule,
    DynamicHooksModule.forRoot({ globalParsers: componentParsers }),
  ],
  providers: [],
  bootstrap: [UsersGridComponent],
})
export class UsersGridModule {}

Currently I am receiving errors Errors

Is there something I can do to fix this or is it more likely impossible to achieve? Thanks

Upvotes: 0

Views: 476

Answers (2)

Damyan Petev
Damyan Petev

Reputation: 1635

Not super familiar with ngx-dynamic-hooks, but I reproduced the errors trying your code alright. They (at least the first one I debugged) are from initializing the column components which are looking to inject their parent Grid(token), but are instead initialized with the same injector from the containing view (UsersGridComponent) so it can't be found. In reality, Angular creates injectors in hierarchy so the Grid's will be the parent injector for the columns and so on. It's technically possible to get the injector of the dynamically created grid and pass it down, but not sure if that's something that can be configured through ngx-dynamic-hooks or if it's not supported at all as a scenario.

So I'll answer the original question with an alternative suggestion: Angular has some API to dynamically create components, see Dynamic component loader, however it's very geared towards generating a single component rather than a full nested markup (the latter can be achieved through multiple creates for each child, but it will be prohibitively complicated IMHO). So, a much simpler path, with minimal changes to the original sample:

  • Replace the grid sample component from the parent component (in this case app.component) with an anchor:
    <ng-template #anchor></ng-template>
    
  • in the component code get the anchor's ViewContainerRef and use it's API to create a dynamic component containing the grid as need. You can expose public properties of said component that can internally re-configure the grid as needed, for this example changing the years for the Sparkline column:
      export class AppComponent {
          @ViewChild('anchor', { static: true, read: ViewContainerRef })
          private anchor!: ViewContainerRef;
    
          public loadGrid() {
              const componentRef = this.anchor.createComponent(HGridColumnResizingSampleComponent);
              componentRef.instance.years = 15;
          }
      }
    

Running example: https://stackblitz.com/edit/angular-yxfn7c?file=src%2Fapp%2Fapp.component.html,src%2Fapp%2Fapp.component.ts

Upvotes: 1

Zdravko Kolev
Zdravko Kolev

Reputation: 1587

Have you treid injecting the IGX_GRID_BASE injection token, from where you can access the interface describing the common grid type

constructor(@Host() @Optional() @Inject(IGX_GRID_BASE) private grid: IgxGridBaseDirective) {
}

/**
* Prevents scrolling the page, when mouse wheel over the grid body.
*/
private preventDocumentScroll(event) {
   event.preventDefault();
}

private getGridTBody(): HTMLElement {
   return this.grid.tbody.nativeElement;
}

There is an example that we are using to define our own scrolling handler and prevent some functionalities on certain criterias.

Available here: https://github.com/IgniteUI/igniteui-angular-samples/blob/master/src/app/directives/prevent-scroll.directive.ts

Upvotes: 0

Related Questions