4. Add jQWidgets Grid Component

If we want to present more data records with more details in our project, we will use a collection of objects. We will implement jqxGrid in this chapter.

Create dummy records of heroes

We will create a few records for demonstration purposes. They will be collected in a new array. Create a new file with the name mock-heroes.ts in the src/app/ folder. We should import the Hero class because our collection of heroes should be of this type.

import { Hero } from './hero';

export const HEROES: Hero[] = [
  { id: 11, name: 'Mr. Nice' },
  { id: 12, name: 'Narco' },
  { id: 13, name: 'Bombasto' },
  { id: 14, name: 'Celeritas' },
  { id: 15, name: 'Magneta' },
  { id: 16, name: 'RubberMan' },
  { id: 17, name: 'Dynama' },
  { id: 18, name: 'Dr IQ' },
  { id: 19, name: 'Magma' },
  { id: 20, name: 'Tornado' }
];

The jqxGrid implementation

The jqxGrid provides a suitable way to represent the records in a table with a lot of options to customize it. We will use the same approach as for the jqxInput to include jqxGrid. Open the tsconfig.json file and include angular_jqxgrid.ts file to the files member:

"files": [
    "node_modules/jqwidgets-scripts/jqwidgets-ts/angular_jqxinput.ts",
    "node_modules/jqwidgets-scripts/jqwidgets-ts/angular_jqxgrid.ts"
  ]

The next thing that we should do is to include this component in the app.module.ts file and add it in the declarations:

import { BrowserModule } from '@angular/platform-browser';
import { NgModule } from '@angular/core';
import { FormsModule } from '@angular/forms';

import { AppComponent } from './app.component';
import { HeroesComponent } from './heroes/heroes.component';

import { jqxInputComponent } from 'jqwidgets-scripts/jqwidgets-ts/angular_jqxinput';
import { jqxGridComponent } from 'jqwidgets-scripts/jqwidgets-ts/angular_jqxgrid';

@NgModule({
  declarations: [
    AppComponent,
    HeroesComponent,
    jqxInputComponent,
    jqxGridComponent
  ],
  imports: [
    BrowserModule,
    FormsModule
  ],
  providers: [],
  bootstrap: [AppComponent]
})
export class AppModule { }

Now let's return to the heroes.component.ts and import the mock HEROES:

import { HEROES } from '../mock-heroes';

After that add a new property - heroes that exposes these heroes:

heroes = HEROES;

Using *ngFor directive

The ngFor is one of the Angular repeater directives. It is like a "for loop" that executes a block of code number of times. Here it iterates all records of an array.

The jqxGrid has this feature to be built with this ngFor directive. More details and example can be found on our site about Angular 6 Grid Features.
With the <tr> tag determine the rows (and also the header of the jqxGrid). The <td> tag is used for different columns. To iterate each hero in this list we use the following syntax: <tr *ngFor="let hero of heroes">.
The list of HEROES is collected in the heroes property of the HeroesComponent, already. The heroes.component.html file should look like this:

<h2>{{ hero.name | uppercase }} Details</h2>
<h2>My Heroes</h2>
<jqxGrid #grid>
    <tr>  
    <th>Id</th>  
    <th>Name</th>  
    </tr>  
    <tr *ngFor="let hero of heroes">  
    <td>  
            {{hero.id}}</td>  
    <td>  
            {{hero.name}}</td>  
    </tr>  
</jqxGrid>

<div>
    <span>id: </span>{{hero.id}}</div>
<div>
    <label>name:
    <jqxInput [(ngModel)]="hero.name" [placeHolder]="'hero name'"></jqxInput>
    </label>
</div>

More details about the Angular directives you could find on the official site.

Don't forget the asterisk (*) in front of ngFor. It's a critical part of the syntax.

Set property on the jqxGrid

Under the last hero in the jqxGrid there is an annoying boundary. If we set the autoheight property with value - true this will be avoided. Let make this change: <jqxGrid #grid [autoheight]="true">.

How it looks now?
'autoheight'

Add styles

We will try to stylisize the project as the same named "tutorial". The heroes list should be attractive and should respond visually when users hover over and select a hero from the list.

It is possible to use styles.css to determine all desired styles or also, the app.component.css file. The best practices suggest to use the closest stylesheet, in this case that is heroes.component.css. It was automatically generated with the whole HeroesComponent and pointed to it in @Component.styleUrls. You could use it to make any transformations on the items of the HeroesComponent.

The jqxGrid theme property

Our jqxGrid has a suitable theme property that can be used to switch between a number of built-in themes. We will use Material theme here:

<jqxGrid #grid [autoheight]="true" [theme]="'material'">
Do not forget to put the theme name in additional quotes - "'theme'".

Also, we should include the file relevant to this particular theme into the .angular-cli.json file into the styles:

"styles": [
    "styles.css",
    "../node_modules/jqwidgets-scripts/jqwidgets/styles/jqx.base.css",
    "../node_modules/jqwidgets-scripts/jqwidgets/styles/jqx.material.css"
],

Additional information - Master/Details

When the user clicks on one of the heroes he will expect to see more about it. In this paragraph, we will demonstrate how to bind to an event of the jqxGrid and how to update data within it.

'Grid'

Binding to the event in the template

We will bind to the rowclick event of the jqxGrid for this purpose. In front of the classic events we should use "on" and after that continue with the name of the event that we want to use but start with a capital letter: onRowclick. After that, you could determine new name of the event. In the case when you use multiple widgets you could use separated events there: (onRowclick)="rowdetails($event)"
Let add these changes to the source code of the heroes.component.html:

<jqxGrid #grid
    (onRowclick)="rowdetails($event)"
    [autoheight]="true" 
    [theme]="'material'">

Implement the event handler

Now it is time to add the logic of this event into the heroes.component.ts. In this way, we will catch each one click over the Grid's rows. Create a new property named selectedHero to save the currently selected hero.

  selectedHero: Hero;

  rowdetails(event: any): void {
    let args = event.args;
    let rowData = args.row.bounddata;
    this.selectedHero = { id: rowData.Id, name: rowData.Name };
  }

Lets make updates on the template of the component's old hero. Rename the hero property to selectedHero.
At that moment the browser should show error as this below.

ERROR TypeError: Cannot read property 'name' of undefined

The current result
This happens because selectedHero is undefined by design. You could choose different approaches to handle this. The template expects some value from class Hero to be set there but there is no such "hero". One of the possible options is to select one row by default and to use the same hero records for the selectedHero property.

How to handle this
The most appropriate way is to check if there is an object set into the selectedHero property or not. For this purpose, we will use *ngIf. Again this is a built-in directive. We will wrap the details about the selected hero in one <div> tag. With ngIf we check if there is a hero selected.

Don't forget the asterisk (*) in front of `ngIf`. It's a critical part of the syntax.

We should also replace the hero property name with the selectedHero in the template:

<h2>My Heroes</h2>
<jqxGrid #grid
    (onRowclick)="rowdetails($event)"
    [autoheight]="true" 
    [theme]="'material'">
        <tr>  
            <th>Id</th>  
            <th>Name</th>  
        </tr>  
            <tr *ngFor="let hero of heroes">  
            <td>  
                {{hero.id}}</td> 
            <td>  
                {{hero.name}}</td>  
        </tr>  
</jqxGrid>

<div *ngIf="selectedHero">
    <h2>{{ selectedHero.name | uppercase }} Details</h2>
    <div>
        <span>id: </span>{{selectedHero.id}}</div>
    <div>
        <label>name:
            <jqxInput [(ngModel)]="selectedHero.name" [placeHolder]="'hero name'"></jqxInput>
        </label>
    </div>
</div>

How *ngIf works
Now the ngIf will check if there is a selected 'hero' or not. When one of the Grid's rows is clicked it will generate details about the 'hero'.

'Grid'

In conclusion