Smart.Grid bound with Smart.Form

Using Smart.Form for editing a row in a Smart.Grid in Angular

What is Smart.Form?

The purpose of our Form component is to provide you an awesome and modern looking Form component, which is easy customizable and will help you to deliver better and professional looking web Forms. You do not have to write complex functions and attach event listeners to validate your form, you can do it fast with our Form Validation.

You can read more about our Form component here: Smart.Form

Implementing an Angular App with Smart.Grid that can interact with the Smart.Form

The main goal of this article is to show you how to bind the Grid Angular Component in the Form so you can edit and validate a single row with a form.

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

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

Setup Smart UI

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

  1. 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.
  2. import the GridModule in app.module.ts
    import { GridModule } from 'smart-webcomponents-angular/grid';
  3. import the FormModule in app.module.ts
    import { FormModule } from 'smart-webcomponents-angular/form';
  4. import the DropDownListModule
    import { DropDownListModule } from 'smart-webcomponents-angular/dropdownlist';
  5. import the ButtonModule
    import { ButtonModule } from 'smart-webcomponents-angular/button';
  6. app.module.ts looks like this:

    import { NgModule } from '@angular/core';
    import { BrowserModule } from '@angular/platform-browser';
    import { GridModule } from 'smart-webcomponents-angular/grid';
    import { FormModule } from 'smart-webcomponents-angular/form';
    import { DropDownListModule } from 'smart-webcomponents-angular/dropdownlist';
    import { ButtonModule } from 'smart-webcomponents-angular/button';
     
    
    import { AppComponent } from './app.component';
    
    @NgModule({
      declarations: [
        AppComponent
      ],
      imports: [
        BrowserModule,
        GridModule,
        FormModule,
        DropDownListModule,
        ButtonModule
      ],
      providers: [],
      bootstrap: [AppComponent]
    })
    export class AppModule { }

Initialize Form

Our grid will have a first name, last name, age and gender columns, so in the form, we will initialize four form controls for these data fields.

  1. First, we need to import Smart in app.component.ts so we can initialize and store our Form

    import { Smart } from 'smart-webcomponents-angular/form';
  2. The form needs a tag with a selector so we will add a form tag with a wrapper

    <div id="form-wrapper">
        <form id='form'></form>
    </div>
  3. Once we have a form tag with an id we can initialize our form with the Smart.Form constructor and assign the returned object to the related property. We will add it in the ngAfterViewInit method, because we do not want to initialize it on every render, but only on mounting.

    Check here what lifecycle hooks are.

    import { AfterViewInit, Component } from '@angular/core';

    First, we will define a property for the form.

    form: any;

    Now the initialization of the form:

    ngAfterViewInit(): void {
      this.form = new Smart.Form('#form', {
        controls: [
          {
            controlType: 'input',
            dataField: 'firstName',
            label: 'First Name',
            placeholder: 'First Name'
          }, {
            controlType: 'input',
            dataField: 'lastName',
            label: 'Last Name',
            placeholder: 'Last Name'
          }, {
            controlType: 'number',
            dataField: 'age',
            label: 'Age',
            placeholder: 'select age'
          }, {
            controlType: 'dropDownList',
            dataField: 'gender',
            label: 'Gender',
            placeholder: 'select gender',
            controlOptions: {
              dataSource: ['male', 'female', 'other']
            }
          }, {
            controlType: 'group',
            columns: 2,
            controls: [
              {
                controlType: 'button',
                label: 'Edit',
                name: "editBtn",
                cssClass: 'success',
              }, {
                controlType: 'button',
                name: "cancelBtn",
                label: 'Cancel',
              }
            ]
          }
        ]
      });
    }
  4. See that we have buttons, but if we click them, they don't do anything

    Let's catch each click of the button by adding an event listener of the form's wrapper and in it check if a button was clicked

    handleEditButtonClick = () => { }
    
    handleCancelButtonClick = () => { }
    
    handleFormClick = (e: Event) => {
    
      const target = e.target as HTMLElement;
      
      switch (target.getAttribute('name')) {
        case 'editBtn':
          this.handleEditButtonClick();
          break;
        case 'cancelBtn':
          this.handleCancelButtonClick();
          break;
        default:
          break;
      }
    }
    <div id="form-wrapper">
      <form id='form' (click)="handleFormClick($event)"></form>
    </div>
  5. Let's style the form a little bit. Paste this in App.css

    #form-wrapper {
        padding: 20px;
        border-radius: 10px;
        width: fit-content;
        border: 1px solid #333;
        margin: auto;
    }
  6. We need a button, which shows and hides the form and later begins editing a row. For this, we will use Smart UI button.

    First we have to add a click handler for it.

    handleStartEditingButtonClick = () => {}
    <div id="grids-editing-button-wrapper">
        <smart-button id='edit-grids-row-button' (onClick)="handleStartEditingButtonClick()">Start Editing</smart-button>
    </div>

    Paste this css in the App.css to position the button

    #grids-editing-button-wrapper {
        margin: 1rem 0;
        display: flex;
        justify-content: center;
    }

    To show & hide the form we need to save its state in a property.

    isFormHidden: boolean = true;

    When we click the 'Start Editing' button the form should become visible and on 'Edit' or 'Cancel' the form should become hidden.

    We will change the state this way:

    handleEditButtonClick = () => {
    
      this.isFormHidden = true;
    
    }
    
    handleCancelButtonClick = () => {
    
      this.isFormHidden = true;
    
    }
    
    handleStartEditingButtonClick = () => {
    
      this.isFormHidden = false;
      
    }

    Depending on that property a style will be applied for the form's wrapper to show and hide it.

    <div id="form-wrapper" [style.display]="this.isFormHidden ? 'none' : 'block'">
        <form id='form' (click)="handleFormClick($event)"></form>
    </div>
  7. We are ready with our form. Here is what we have written so far:

    app.component.ts:

    import { AfterViewInit, Component } from '@angular/core';
    import { Smart } from 'smart-webcomponents-angular/form';
    
    @Component({
      selector: 'app-root',
      templateUrl: './app.component.html',
      styleUrls: ['./app.component.css']
    })
    export class AppComponent implements AfterViewInit {
    
      title = 'smart-app';
    
      form: any;
    
      isFormHidden: boolean = true;
    
      ngAfterViewInit(): void {
        this.form = new Smart.Form('#form', {
          controls: [
            {
              controlType: 'input',
              dataField: 'firstName',
              label: 'First Name',
              placeholder: 'First Name'
            }, {
              controlType: 'input',
              dataField: 'lastName',
              label: 'Last Name',
              placeholder: 'Last Name'
            }, {
              controlType: 'number',
              dataField: 'age',
              label: 'Age',
              placeholder: 'select age'
            }, {
              controlType: 'dropDownList',
              dataField: 'gender',
              label: 'Gender',
              placeholder: 'select gender',
              controlOptions: {
                dataSource: ['male', 'female', 'other']
              }
            }, {
              controlType: 'group',
              columns: 2,
              controls: [
                {
                  controlType: 'button',
                  label: 'Edit',
                  name: "editBtn",
                  cssClass: 'success',
                }, {
                  controlType: 'button',
                  name: "cancelBtn",
                  label: 'Cancel',
                }
              ]
            }
          ]
        });
      }
    
      handleEditButtonClick = () => {
    
        this.isFormHidden = true;
    
      }
    
      handleCancelButtonClick = () => {
    
        this.isFormHidden = true;
    
      }
    
      handleStartEditingButtonClick = () => {
    
        this.isFormHidden = false;
        
      }
    
      handleFormClick = (e: Event) => {
    
        const target = e.target as HTMLElement;
    
        switch (target.getAttribute('name')) {
          case 'editBtn':
            this.handleEditButtonClick();
            break;
          case 'cancelBtn':
            this.handleCancelButtonClick();
            break;
          default:
            break;
        }
      }
    
    }

    app.component.html:

    <div id="grids-editing-button-wrapper">
        <smart-button id='edit-grids-row-button' (onClick)="handleStartEditingButtonClick()">Start Editing</smart-button>
    </div>
    <div id="form-wrapper" [style.display]="this.isFormHidden ? 'none' : 'block'">
        <form id='form' (click)="handleFormClick($event)"></form>
    </div>

Initialize Grid

Currently we have a form that does nothing. Now we will create a Smart.Grid with our Angular's Grid Component.

We will have 4 columns First Name, Last Name, Age and Gender and the selection will be enabled so we can select which column we want to edit.

Our Grid supports sorting, filtering and paging so we will enable them also.

  1. First, we will create an interface for our grid's row

    interface Person {
      firstName: string,
      lastName: string,
      age: number,
      gender: 'male' | 'female' | 'other'
    }
  2. Next, we should define our grid's settings.
    gridSettings = {
      dataSource: new window.Smart.DataAdapter({
        dataSource: generateGridData(30),
        dataFields: [
          'firstName: string',
          'lastName: string',
          'age: number',
          'gender: string'
        ]
      }),
      selection: {
        enabled: true,
        action: 'click',
        mode: 'one'
      },
      sorting: {
        enabled: true,
        sortMode: 'one'
      },
      filtering: {
        enabled: true
      },
      paging: {
        enabled: true,
        pageSize: 15
      },
      pager: {
        visible: true,
        pageSizeSelector: {
          visible: true,
          dataSource: [15, 30, 50]
        }
      },
      columns: [
        {
          label: 'First Name',
          dataField: 'firstName'
        },
        {
          label: 'Last Name',
          dataField: 'lastName'
        }, {
          label: 'Age',
          dataField: 'age'
        },
        {
          label: 'Gender',
          dataField: 'gender'
        }
      ]
    }
  3. See that we do not have generateGridData() method. Let's add it.

    Add this method outside the Component:

    const generateGridData = (rows: number): Person[] => {
    
      const data: Person[] = [];
    
      const firstNames = ['Peter', 'Alexander', 'George', 'Steve', 'Zlat', 'Philip', 'Michael', 'Oliver', 'Kassandra', 'Maria', 'Iglika'];
      const lastNames = ['Madison', 'Marley', 'Bolden', 'Raven', 'Steve', 'Plain', 'Michael', 'Hansley', 'Ashley', 'Monroe', 'West'];
    
      for (let i = 0; i < rows; i++) {
    
        const row: Person = {
          firstName: firstNames[Math.floor(Math.random() * (firstNames.length - 0) + 0)],
          lastName: lastNames[Math.floor(Math.random() * (lastNames.length - 0) + 0)],
          age: Math.floor(Math.random() * (110 - 10) + 10),
          gender: Math.random() > 0.5 ? 'male' : 'female'
        }
    
        data.push(row);
    
      }
    
      return data;
    }
  4. We will be ready to create a query for the Grid's reference, after creating our Smart.Grid

    <div id="grid-wrapper">
      <smart-grid #grid id="grid" [dataSource]="gridSettings.dataSource" [selection]="gridSettings.selection"
            [sorting]="gridSettings.sorting" [filtering]="gridSettings.filtering" [paging]="gridSettings.paging"
            [pager]="gridSettings.pager" [columns]="gridSettings.columns"></smart-grid>
    </div>

    Again we wrapped the grid to make the positioning easier with the help of the following CSS pased in the App.css

    #grid-wrapper {
    margin: 20px;
    display: flex;
    justify-content: center;
    }

    Now our view query for the reference. More info about View queries here

    import { AfterViewInit, Component, ViewChild } from '@angular/core';
    import { GridComponent } from 'smart-webcomponents-angular/grid';
    @ViewChild("grid", { read: GridComponent, static: false}) grid!: GridComponent;

    The parameter in the @ViewChild is the reference of the element in the app.component.html

  5. Now we have Smart.Form and Smart.Grid. It is time to bind them. Here is the code so far.

    app.component.ts:

    import { AfterViewInit, Component, ViewChild } from '@angular/core';
    import { Smart } from 'smart-webcomponents-angular/form';
    import { GridComponent } from 'smart-webcomponents-angular/grid';
    
    interface Person {
      firstName: string,
      lastName: string,
      age: number,
      gender: 'male' | 'female' | 'other'
    }
    
    const generateGridData = (rows: number): Person[] => {
    
      const data: Person[] = [];
    
      const firstNames = ['Peter', 'Alexander', 'George', 'Steve', 'Zlat', 'Philip', 'Michael', 'Oliver', 'Kassandra', 'Maria', 'Iglika'];
      const lastNames = ['Madison', 'Marley', 'Bolden', 'Raven', 'Steve', 'Plain', 'Michael', 'Hansley', 'Ashley', 'Monroe', 'West'];
    
      for (let i = 0; i < rows; i++) {
    
        const row: Person = {
          firstName: firstNames[Math.floor(Math.random() * (firstNames.length - 0) + 0)],
          lastName: lastNames[Math.floor(Math.random() * (lastNames.length - 0) + 0)],
          age: Math.floor(Math.random() * (110 - 10) + 10),
          gender: Math.random() > 0.5 ? 'male' : 'female'
        }
    
        data.push(row);
    
      }
    
      return data;
    }
    
    @Component({
      selector: 'app-root',
      templateUrl: './app.component.html',
      styleUrls: ['./app.component.css']
    })
    export class AppComponent implements AfterViewInit {
      @ViewChild("grid", { read: GridComponent, static: false}) grid!: GridComponent;
    
      title = 'smart-app';
    
      form: any;
    
      isFormHidden: boolean = true;
    
      gridSettings = {
        dataSource: new window.Smart.DataAdapter({
          dataSource: generateGridData(30),
          dataFields: [
            'firstName: string',
            'lastName: string',
            'age: number',
            'gender: string'
          ]
        }),
        selection: {
          enabled: true,
          action: 'click',
          mode: 'one'
        },
        sorting: {
          enabled: true,
          sortMode: 'one'
        },
        filtering: {
          enabled: true
        },
        paging: {
          enabled: true,
          pageSize: 15
        },
        pager: {
          visible: true,
          pageSizeSelector: {
            visible: true,
            dataSource: [15, 30, 50]
          }
        },
        columns: [
          {
            label: 'First Name',
            dataField: 'firstName'
          },
          {
            label: 'Last Name',
            dataField: 'lastName'
          }, {
            label: 'Age',
            dataField: 'age'
          },
          {
            label: 'Gender',
            dataField: 'gender'
          }
        ]
      }
    
      ngAfterViewInit(): void {
        this.form = new Smart.Form('#form', {
          controls: [
            {
              controlType: 'input',
              dataField: 'firstName',
              label: 'First Name',
              placeholder: 'First Name'
            }, {
              controlType: 'input',
              dataField: 'lastName',
              label: 'Last Name',
              placeholder: 'Last Name'
            }, {
              controlType: 'number',
              dataField: 'age',
              label: 'Age',
              placeholder: 'select age'
            }, {
              controlType: 'dropDownList',
              dataField: 'gender',
              label: 'Gender',
              placeholder: 'select gender',
              controlOptions: {
                dataSource: ['male', 'female', 'other']
              }
            }, {
              controlType: 'group',
              columns: 2,
              controls: [
                {
                  controlType: 'button',
                  label: 'Edit',
                  name: "editBtn",
                  cssClass: 'success',
                }, {
                  controlType: 'button',
                  name: "cancelBtn",
                  label: 'Cancel',
                }
              ]
            }
          ]
        });
      }
    
      handleEditButtonClick = () => {
    
        this.isFormHidden = true;
    
      }
    
      handleCancelButtonClick = () => {
    
        this.isFormHidden = true;
    
      }
    
      handleStartEditingButtonClick = () => {
    
        this.isFormHidden = false;
    
      }
    
      handleFormClick = (e: Event) => {
    
        const target = e.target as HTMLElement;
    
        switch (target.getAttribute('name')) {
          case 'editBtn':
            this.handleEditButtonClick();
            break;
          case 'cancelBtn':
            this.handleCancelButtonClick();
            break;
          default:
            break;
        }
      }
    }

    app.component.html:

    <div id="grids-editing-button-wrapper">
        <smart-button id='edit-grids-row-button' (onClick)="handleStartEditingButtonClick()">Start Editing</smart-button>
    </div>
    <div id="form-wrapper" [style.display]="this.isFormHidden ? 'none' : 'block'">
        <form id='form' (click)="handleFormClick($event)"></form>
    </div>
    <div id="grid-wrapper">
        <smart-grid #grid id="grid" [dataSource]="gridSettings.dataSource" [selection]="gridSettings.selection"
            [sorting]="gridSettings.sorting" [filtering]="gridSettings.filtering" [paging]="gridSettings.paging"
            [pager]="gridSettings.pager" [columns]="gridSettings.columns">
        </smart-grid>
    </div>

We are ready to bind our Form to the Grid

  1. The first thing is to check whether a row is selected when the 'Start editing' is clicked, if not, we will take the first visible row in the Grid. After that the form's value should be changed to the row data.

    The data from the grid is being collected via Smart.Grid's methods

    See the Smart.Grid docs here

    We will store a property for the formRowId

    formRowId: number = -1;
    
    async handleStartEditingButtonClick() {
      
      const selection = await gridRef.current.getSelectedRows();
    
      const selectedRowId = (await gridRef.current.getSelectedRowIds(selection))[0]
      const selectedRow = await gridRef.current.getRowByIndex(selectedRowId);
    
      if(selectedRowId && selectedRow.visibleIndex >= 0) {
    
        formRef.current.value = await gridRef.current.getRowData(selectedRowId);
        this.formRowId = selectedRowId;
    
      } else {
    
        const firstVisibleRow = (await gridRef.current.getVisibleRows())[0];
    
        formRef.current.value = await gridRef.current.getRowData(firstVisibleRow.data.$.id);
        this.formRowId = firstVisibleRow.data.$.id;
        gridRef.current.ensureVisible(firstVisibleRow.data.$.id);
    
      }
    
      setIsFormHidden(false);
    
    }
  2. The next is to disable the Grid when we are editing and enable it when we finish the editing. For this, we will change our gridSettings. The disableGrid is a method in our component

    disableGrid(isDisabled: boolean) {
    
      this.gridSettings = {
        ...this.gridSettings,
        selection: { ...this.gridSettings.selection, enabled: !isDisabled },
        sorting: { ...this.gridSettings.sorting, enabled: !isDisabled },
        filtering: { ...this.gridSettings.filtering, enabled: !isDisabled },
      }
    
    }
    
    const handleEditButtonClick = () => {
    
      setIsFormHidden(true);
      this.disableGrid(false);
    
    }
    
    const handleCancelButtonClick = () => {
    
      setIsFormHidden(true);
      this.disableGrid(false);
    
    }
    
    const handleStartEditingButtonClick = async () => {
    
      this.disableGrid(true);
    
      //Rest of the previously written code
    
    }
  3. We are almost ready! Our next step will be to commit the change when the editing is finished.

    handleEditButtonClick() {
    
      this.isFormHidden = true;
    
      this.grid.updateRow(this.formRowId, this.form!.value);
      this.grid.ensureVisible(this.formRowId);
    
      this.disableGrid(false);
    
    }
  4. We can step further and add validation. Each of our Smart.Form controls can accept validation rules.

    How to use validation rules here

    We will add validation for the first name, last name and age.

    First and Last names will be with min length of 3 and the age should be greater than 0 and less than 120

    The 'Edit' button will be disabled if the form is invalid

    The First thing is to define our validation rules like this:

    controls: [
    {
      controlType: 'input',
      dataField: 'firstName',
      label: 'First Name',
      placeholder: 'First Name',
      validationRules: [
        {
          type: 'stringLength',
          min: 3,
        }
      ]
    }, {
      controlType: 'input',
      dataField: 'lastName',
      label: 'Last Name',
      placeholder: 'Last Name',
      validationRules: [
        {
          type: 'stringLength',
          min: 3,
        }
      ]
    }, {
      controlType: 'number',
      dataField: 'age',
      label: 'Age',
      placeholder: 'select age',
      validationRules: [
        {
          type: 'range',
          min: 1,
          max: 120
        }
      ]
    }

    Our next task is to disable and enable our form upon form status changes

    We will define a function so it will be easier for us to manage the state of the button.

    validateFormEditButton() {
    
      const editBtn: HTMLButtonElement = this.form!.element.querySelector('smart-button[name="editBtn"]')!;
    
        if (this.form!.state.form === 'invalid') {
    
          return editBtn.disabled = true;
    
        } else if (this.form!.state.form === 'valid') {
    
          return editBtn.disabled = false;
    
        }
    
        return;
    }

    Next, we will catch the status change of the form and manage the 'Edit' button's state

    This code is placed in the ngAfterViewInit under the Smart.Form's initialization

    this.form!.onStatusChanges = () => {
    
      this.validateFormEditButton();
    
    }

    Now, we will set the state of the 'Edit' button when our 'Start Editing' button is clicked

    asyn chandleStartEditingButtonClick() {
    
      //REST OF OUR PREVIOUSLY WRITTEN CODE
    
      this.validateFormEditButton();
    
      setIsFormHidden(false);
    
    }
  5. Everything is ready and this is the code it:

    app.component.ts

    import { AfterViewInit, Component, ViewChild } from '@angular/core';
    import { Smart } from 'smart-webcomponents-angular/form';
    import { GridComponent } from 'smart-webcomponents-angular/grid';
    
    interface Person {
      firstName: string,
      lastName: string,
      age: number,
      gender: 'male' | 'female' | 'other'
    }
    
    const generateGridData = (rows: number): Person[] => {
    
      const data: Person[] = [];
    
      const firstNames = ['Peter', 'Alexander', 'George', 'Steve', 'Zlat', 'Philip', 'Michael', 'Oliver', 'Kassandra', 'Maria', 'Iglika'];
      const lastNames = ['Madison', 'Marley', 'Bolden', 'Raven', 'Steve', 'Plain', 'Michael', 'Hansley', 'Ashley', 'Monroe', 'West'];
    
      for (let i = 0; i < rows; i++) {
    
        const row: Person = {
          firstName: firstNames[Math.floor(Math.random() * (firstNames.length - 0) + 0)],
          lastName: lastNames[Math.floor(Math.random() * (lastNames.length - 0) + 0)],
          age: Math.floor(Math.random() * (110 - 10) + 10),
          gender: Math.random() > 0.5 ? 'male' : 'female'
        }
    
        data.push(row);
    
      }
    
      return data;
    }
    
    @Component({
      selector: 'app-root',
      templateUrl: './app.component.html',
      styleUrls: ['./app.component.css']
    })
    export class AppComponent implements AfterViewInit {
      @ViewChild("grid", { read: GridComponent, static: false }) grid!: GridComponent;
    
      title = 'smart-app';
    
      form: any;
    
      isFormHidden: boolean = true;
    
      formRowId: number = -1;
    
      gridSettings = {
        dataSource: new window.Smart.DataAdapter({
          dataSource: generateGridData(30),
          dataFields: [
            'firstName: string',
            'lastName: string',
            'age: number',
            'gender: string'
          ]
        }),
        selection: {
          enabled: true,
          action: 'click',
          mode: 'one'
        },
        sorting: {
          enabled: true,
          sortMode: 'one'
        },
        filtering: {
          enabled: true
        },
        paging: {
          enabled: true,
          pageSize: 15
        },
        pager: {
          visible: true,
          pageSizeSelector: {
            visible: true,
            dataSource: [15, 30, 50]
          }
        },
        columns: [
          {
            label: 'First Name',
            dataField: 'firstName'
          },
          {
            label: 'Last Name',
            dataField: 'lastName'
          }, {
            label: 'Age',
            dataField: 'age'
          },
          {
            label: 'Gender',
            dataField: 'gender'
          }
        ]
      }
    
      ngAfterViewInit(): void {
        this.form = new Smart.Form('#form', {
          controls: [
            {
              controlType: 'input',
              dataField: 'firstName',
              label: 'First Name',
              placeholder: 'First Name',
              validationRules: [
                {
                  type: 'stringLength',
                  min: 3,
                }
              ]
            }, {
              controlType: 'input',
              dataField: 'lastName',
              label: 'Last Name',
              placeholder: 'Last Name',
              validationRules: [
                {
                  type: 'stringLength',
                  min: 3,
                }
              ]
            }, {
              controlType: 'number',
              dataField: 'age',
              label: 'Age',
              placeholder: 'select age',
              validationRules: [
                {
                  type: 'range',
                  min: 1,
                  max: 120
                }
              ]
            }, {
              controlType: 'dropDownList',
              dataField: 'gender',
              label: 'Gender',
              placeholder: 'select gender',
              controlOptions: {
                dataSource: ['male', 'female', 'other']
              }
            }, {
              controlType: 'group',
              columns: 2,
              controls: [
                {
                  controlType: 'button',
                  label: 'Edit',
                  name: "editBtn",
                  cssClass: 'success',
                }, {
                  controlType: 'button',
                  name: "cancelBtn",
                  label: 'Cancel',
                }
              ]
            }
          ]
        });
    
        this.form!.onStatusChanges = () => {
    
          this.validateFormEditButton();
        
        }
      }
    
      handleEditButtonClick = () => {
    
        this.isFormHidden = true;
    
        this.grid.updateRow(this.formRowId, this.form!.value);
        this.grid.ensureVisible(this.formRowId);
    
        this.disableGrid(false);
    
      }
    
      handleCancelButtonClick = () => {
    
        this.isFormHidden = true;
        this.disableGrid(false);
    
      }
    
      async handleStartEditingButtonClick() {
    
        this.disableGrid(true);
    
        const selectedRowId = (await this.grid.getSelectedRowIds())[0]
        const selectedRow = await this.grid.getRowByIndex(selectedRowId);
    
        if (selectedRowId && selectedRow.visibleIndex >= 0) {
    
          this.form!.value = await this.grid.getRowData(selectedRowId);
          this.formRowId = selectedRowId;
    
        } else {
    
          const firstVisibleRow = (await this.grid.getVisibleRows())[0];
    
          this.form!.value = await this.grid.getRowData(firstVisibleRow.data.$.id);
          this.formRowId = firstVisibleRow.data.$.id;
          this.grid.ensureVisible(firstVisibleRow.data.$.id);
    
        }
    
        this.validateFormEditButton();
        
        this.isFormHidden = false;
    
      }
    
      handleFormClick = (e: Event) => {
    
        const target = e.target as HTMLElement;
    
        switch (target.getAttribute('name')) {
          case 'editBtn':
            this.handleEditButtonClick();
            break;
          case 'cancelBtn':
            this.handleCancelButtonClick();
            break;
          default:
            break;
        }
      }
    
      disableGrid(isDisabled: boolean) {
    
        this.gridSettings = {
          ...this.gridSettings,
          selection: { ...this.gridSettings.selection, enabled: !isDisabled },
          sorting: { ...this.gridSettings.sorting, enabled: !isDisabled },
          filtering: { ...this.gridSettings.filtering, enabled: !isDisabled },
        }
    
      }
    
      validateFormEditButton() {
        
        const editBtn: HTMLButtonElement = this.form!.element.querySelector('smart-button[name="editBtn"]')!;
    
        if (this.form!.state.form === 'invalid') {
    
          return editBtn.disabled = true;
    
        } else if (this.form!.state.form === 'valid') {
    
          return editBtn.disabled = false;
    
        }
    
        return;
      }
    }

    app.component.html

    <div id="grids-editing-button-wrapper">
        <smart-button id='edit-grids-row-button' (onClick)="handleStartEditingButtonClick()">Start Editing</smart-button>
    </div>
    <div id="form-wrapper" [style.display]="this.isFormHidden ? 'none' : 'block'">
        <form id='form' (click)="handleFormClick($event)"></form>
    </div>
    <div id="grid-wrapper">
        <smart-grid #grid id="grid" [dataSource]="gridSettings.dataSource" [selection]="gridSettings.selection"
          [sorting]="gridSettings.sorting" [filtering]="gridSettings.filtering" [paging]="gridSettings.paging"
          [pager]="gridSettings.pager" [columns]="gridSettings.columns">
        </smart-grid>
    </div>

    app.component.css

    #form-wrapper {
      padding: 20px;
      border-radius: 10px;
      width: fit-content;
      border: 1px solid #333;
      margin: auto;
    }
    
    #grids-editing-button-wrapper {
      margin: 1rem 0;
      display: flex;
      justify-content: center;
    }
    
    #grid-wrapper {
      margin: 20px;
      display: flex;
      justify-content: center;
    }
  6. We achieved this so far

    Start Editing