Angular Standalone Smart.Grid

Smart.Grid as a standalone Angular component

This topic will show you how to use Smart.Grid and any other of our components in a combination of the new Angular's feature: standalone components. This feature was a preview in Angular 14 and now in Angular 15, it was introduced as a stable API.

What are standalone components?

The long-awaited feature: standalone components, allow the developer to create a component without declaring it in a NgModule. You can directly import the newly created component into another one. Standalone components can manage their template dependencies alone without NgModule. That reduces the need for NgModule and can make it optional. The goal is to shift the focus from NgModules to Components. A standalone component is marked as standalone with the property standalone: true The dependencies are directly specified in an imports array

Setup Angular Environment

To create new Angular project we need Angular cli:

npm install -g @angular/cli

the -g flag is for global. Once you install it you can use it everywhere, not only in this project.

Create a new project this way:

ng new smart-app

We do not need routing and we will use CSS, so press enter two times

Next navigate to smart-app

cd smart-app
  1. Delete src/app.component.spec.ts

    Delete src/app.module.ts

  2. Remove Everything from src/app.component.html

  3. Convert src/app/app.component.ts to standalone:

    @Component({
      selector: 'app-root',
      standalone: true,
      templateUrl: './app.component.html',
      styleUrls: ['./app.component.css'],
      imports: []
    })
  4. Bootstrap the AppComoponent in the main.ts file:

    // in the main.ts file
    import { bootstrapApplication } from '@angular/platform-browser';
    import { AppComponent } from './app/app.component';
    
    bootstrapApplication(AppComponent);

Setup Smart UI

Smart UI for Angular is distributed as smart-webcomponents-angular NPM package

  • Open the terminal and install the package:
    ng add smart-webcomponents-angular

    The command executes the following actions:

    1. Adds the smart-webcomponents-angular package as a dependency.
    2. Imports the Modules in the current application module.
    3. Registers the default Smart UI theme in the angular.json file.
    4. Adds all required peer dependencies to package.json
    5. Triggers npm install to install the theme and all peer packages that are added.

Import the GridModule

Standalone components can use existing NgModules by directly importing them in the imports array.

To use Smart.Grid we should import our GridModule in our application and since we are using a standalone component we will import it into the imports array:

import the GridModule directly in app.component.ts

import { GridModule } from 'smart-webcomponents-angular/grid';

@Component({
  selector: 'app-root',
  standalone: true,
  templateUrl: './app.component.html',
  styleUrls: ['./app.component.css'],
  imports: [GridModule]
})

Initialize Smart.Grid

We did everything required to use Smart.Grid and it is time to create it:

Open app.component.html and use smart-grid component as shown:

<smart-grid #grid id="grid" [dataSource]="dataSource" [columns]="columns"></smart-grid>

Now you must initialize the properties for the grid

Open app.component.ts and paste the following inside the component's class:

export class AppComponent {
  title = 'smart-app';

  @ViewChild('grid', { read: GridComponent, static: false }) grid!: GridComponent;

  dataSource = new Smart.DataAdapter({
    dataSource: [],
    dataFields: [
      'id: number',
      'firstName: string',
      'lastName: string',
      'productName: string',
      'available: bool',
      'quantity: number',
      'price: number',
      'total: number'
    ]
  })

  columns = [
    {
      label: 'First Name', dataField: 'firstName'
    },
    { label: 'Last Name', dataField: 'lastName' },
    { label: 'Product', dataField: 'productName' },
    { label: 'Available', dataField: 'available', template: 'checkBox' },
    { label: 'Quantity', dataField: 'quantity' },
    { label: 'Unit Price', dataField: 'price', cellsFormat: 'c2' }
  ]
}

Injecting data service

You may have seen that the grid is empty that is happening because in the dataSource we passed an empty array. Now we will create a data service for our grid. This will show you how to use dependency injection with the standalone approach

  1. Open the terminal and run
    ng g service data
  2. Delete data.service.spec.ts
  3. Open data.service.ts and replace the
    @Injectable({
      providedIn: 'root'
    })
    with
    @Injectable()
  4. Add the following interface above the
    @Injectable()
    interface IRowGenerateData {
      id: number;
      reportsTo: number | null;
      available: boolean | null;
      firstName: string;
      lastName: string;
      name: string;
      productName: string;
      quantity: string | number;
      total: string | number;
      price: string | number;
      date: Date;
      leaf: boolean;
    }
  5. Add this method inside the class:
    export class DataService {
    
      constructor() {}
    
      GetData(rowscount?: number, last?: number, hasNullValues?: boolean): IRowGenerateData[] {
        const data: IRowGenerateData[] = new Array();
    
        if (rowscount === undefined) {
          rowscount = 100;
        }
    
        let startIndex = 0;
    
        if (last) {
          startIndex = rowscount;
          rowscount = last - rowscount;
        }
    
        const firstNames =
          [
            'Andrew', 'Nancy', 'Shelley', 'Regina', 'Yoshi', 'Antoni', 'Mayumi', 'Ian', 'Peter', 'Lars', 'Petra', 'Martin', 'Sven', 'Elio', 'Beate', 'Cheryl', 'Michael', 'Guylene'
          ];
    
        const lastNames =
          [
            'Fuller', 'Davolio', 'Burke', 'Murphy', 'Nagase', 'Saavedra', 'Ohno', 'Devling', 'Wilson', 'Peterson', 'Winkler', 'Bein', 'Petersen', 'Rossi', 'Vileid', 'Saylor', 'Bjorn', 'Nodier'
          ];
    
        const productNames =
          [
            'Black Tea', 'Green Tea', 'Caffe Espresso', 'Doubleshot Espresso', 'Caffe Latte', 'White Chocolate Mocha', 'Caramel Latte', 'Caffe Americano', 'Cappuccino', 'Espresso Truffle', 'Espresso con Panna', 'Peppermint Mocha Twist'
          ];
    
        const priceValues =
          [
            '2.25', '1.5', '3.0', '3.3', '4.5', '3.6', '3.8', '2.5', '5.0', '1.75', '3.25', '4.0'
          ];
    
        for (let i = 0; i < rowscount; i++) {
          const row = {} as IRowGenerateData;
    
          const productindex = Math.floor(Math.random() * productNames.length);
          const price = parseFloat(priceValues[productindex]);
          const quantity = 1 + Math.round(Math.random() * 10);
    
          row.id = startIndex + i;
          row.reportsTo = Math.floor(Math.random() * firstNames.length);
    
          if (i % Math.floor(Math.random() * firstNames.length) === 0) {
            row.reportsTo = null;
          }
    
          row.available = productindex % 2 === 0;
    
          if (hasNullValues === true) {
            if (productindex % 2 !== 0) {
              const random = Math.floor(Math.random() * rowscount);
              row.available = i % random === 0 ? null : false;
            }
          }
    
          row.firstName = firstNames[Math.floor(Math.random() * firstNames.length)];
          row.lastName = lastNames[Math.floor(Math.random() * lastNames.length)];
          row.name = row.firstName + ' ' + row.lastName;
          row.productName = productNames[productindex];
          row.price = price;
          row.quantity = quantity;
          row.total = price * quantity;
    
          const date = new Date();
          date.setFullYear(2016, Math.floor(Math.random() * 11), Math.floor(Math.random() * 27));
          date.setHours(0, 0, 0, 0);
          row.date = date;
    
          data[i] = row;
        }
    
        return data;
      }
    }
  6. The next step is to provide this service in our application, so open the main.ts file and add pass options to the bootstrapApplication method:
    bootstrapApplication(AppComponent, {
      providers: [
          DataService
      ]
    });

This is the way of using dependency injection with standalone components.

Filling Smart.Grid with data

Now since we have an injectable service, it is time to inject it in our AppComponent and use the GetData method to fill the Smart.Grid

Open app.component.ts and add the following constructor that will inject our service:

constructor(private dataService: DataService) {}

Now use the GetData function to pass records to the grid

          dataSource = new Smart.DataAdapter({
            dataSource: this.dataService.GetData(100),
            dataFields: [
              'id: number',
              'firstName: string',
              'lastName: string',
              'productName: string',
              'available: bool',
              'quantity: number',
              'price: number',
              'total: number'
            ]
          })

Result

Our Smart.Grid is ready and filled with data, this should be the result of the help topic: