Transaction Service class hierarchy

    Transaction, State, Transaction Log

    The Transaction is the main building block of the Transaction service. The Transaction is actually every operation that you execute on the data. The Transaction interface defines three properties: id, newValue and type.

    The id of the Transaction should be unique per data record and defines the record that this transaction is affecting. The type may be any of the three transaction types: ADD, DELETE and UPDATE, depending what operation you execute. The newValue contains the value of the new record in case you are adding an ADD transaction. If you are updating an existing record, the newValue would contain the changes only. You may have several transactions of UPDATE type with same id. If you are deleting a record, the newValue will contain the value of the deleted record.

    You can see an example of how adding each type of transaction looks like in the How to use the Transaction service topic.

    Every time you add a Transaction, it is added to the transaction log and undo stack. All the changes in the transaction log are then accumulated per record. From that point, the service maintains an aggregated State. The State consists of unique records and every record may be of one of the supported transaction types, mentioned above.

    While adding transactions you may turn on pending transactions by calling startPending. All subsequent transactions will be accumulated in single transaction until you call endPending. If you pass true to endPending all accumulated transactions will be added as a single transaction in the transaction log and in the undo stack.

    Using igxBaseTransaction

    Our grid module provides a very basic implementation of the Transaction service (igxBaseTransactionService) with just pending session functionality allowing for Row Editing feature. By using startPending and endPending Row editing can combine multiple per-cell operations into a single change. This means editing multiple cells of a single record creates a single transaction and you can handle just the row edit event.

    With the accumulated state being a partial object, we can also use the service to check which cell has been edited and build UI around that.

    The igxBaseTransactionService has no undo stack so it does not provide undo/redo functionality.

    A detailed example of how you may use igxBaseTransactionService to enable Row Editing is provided in the following topics:

    General information on igxTransactionService and igxHierarchicalTransactionService

    igxTransactionService and igxHierarchicalTransactionService are injectable middlewares, that implement the Transaction Service interface. A component may use those to accumulate changes without affecting the underlying data. The provider exposes API to access, manipulate (undo and redo) and discard or commit one or all changes to the data.

    In a more concrete example, igxTransactionService and igxHierarchicalTransactionService can work with both cell editing and row editing of the IgxGrid. The transaction for the cell edit is added when the cell exits edit mode. When row editing starts the grid sets its transaction service in pending state by calling startPending. Each edited cell is added to the pending transaction log and is not added to the main transaction log. When the row exits edit mode all the changes are added to the main transaction log and to the undo log as a single transaction.

    In both cases (cell editing and row editing) the state of the grid edits consists of all updated, added and deleted rows and their last states. Those can later be inspected, manipulated and submitted at once or per id. Changes are collected for individual cells or rows, depending on editing mode, and accumulated per data row/record.

    Using igxTransactionService

    igxTransactionService extends igxBaseTransactionService.

    If you want your component to use transactions when making data operation, you need to define the igxTransactionService as a provider in your component's providers array.

    The igxTransactionService provides an undo stack so you may get advantage of the undo/redo functionality. The Undo stack is actually an array that contains arrays of transactions. When using the igxTransactionService, you may check the canUndo accessor in order to understand if there are any transactions in the Undo stack. If there are - you may use the undo method to remove the last transaction and redo to apply the last undone transaction.

    You may find a detailed example of how igxGrid with Batch Editing is implemented in the following topic:

    Using igxHierarchicalTransactionService

    igxHierarchicalTransactionService extends igxTransactionService.

    The igxHierarchicalTransactionService is designed to handle the relations between parents and children (use this when you have a hierarchical data structure, as in igxTreeGrid, for example). The service ensures that a new record will be added to the place you expect when adding an ADD transaction. When you delete a parent record, its' children will be promoted to the higher level of hierarchy, or will be deleted with their parent, depending on implementation. You can see the cascadeOnDelete property of the tree grid for a concrete example - depending on the value, deleting a parent record will have different effects on its children.

    In your application, you may want to handle the scenario where the user tries to add a child record to a parent record that is already deleted and is waiting for the transaction to be committed. The Transaction Service will not allow adding a record to a parent that is to be deleted and an error message will be shown in the Console. However, you may check if a parent is to be deleted and implement your own alert to the user using the following code:

        const state = this.transactions.getState(parentRecordID);
        if (state && state.type === TransactionType.DELETE) {
            // Implement your logic here
        }
    

    You may find a detailed examples of how igxTreeGrid and igxHierarchicalGrid with Batch Editing are implemented in the following topics:

    Transaction Factory

    In the concrete implementation of transactions inside of Ignite UI for Angular grids, a factory is used in order to instantiate the proper transaction service, depending on the value of the grid's batchEditing. There are two separate transaction factories - the IgxFlatTransactionFactory (used for Grid and Hierarchical Grid) and IgxHierarchicalTransactionFactory (used for Tree Grid). Both classes expose only one method - create - which returns a new instance of the proper type. The parameter passed (TRANSACTION_TYPE) is internally used - None is used when batchEditing is false and Base - when batch editing is enabled. An enum is used (instead of a true - false flag), as it allows to be expanded upon.

    Using Transaction Factory

    Both IgxFlatTransactionFactory and IgxHierarchicalTransactionFactory are provided in root and are exposed in the public API. If you want to instantiate a new instance of a transaction service, depending on some arbitrary check, you can use a transaction factory.

    In the below example, you can see how you can instantiate different transaction services depending on an arbitrary (hasUndo) flag:

    import { IgxFlatTransactionFactory, TRANSACTION_TYPE } from 'igniteui-angular';
    // import { IgxFlatTransactionFactory, TRANSACTION_TYPE } from '@infragistics/igniteui-angular'; for licensed package
    
    export class MyCustomComponent {
        ...
        constructor(private transactionFactory: IgxFlatTransactionFactory) {}
        ...
        public transaction!: IgxTransactionService<Transaction, State>;
    
        public set hasUndo(val: boolean) {
            if (val) {
                this.transactions = this.transactionFactory.create(TRANSACTION_TYPE.Base);
            } else {
                this.transactions = this.transactionFactory.create(TRANSACTION_TYPE.None);
            }
        }
    }
    

    Both factory classes can be extended and overriden in the DI hierarchy (using the providers array) in order to provide your own, custom implementation. This, combined with the fact that all of the classes the get instantiated by the factories are also public, gives you a lot of control over what's provided to the components that use transaction implementations internally.

    For example, to override the transaction service used internally by the IgxGridComponent, you can do the following:

    First, define a custom factory class

    import { IgxFlatTransactionFactory, TRANSACTION_TYPE, IgxBaseTransactionService,
    TransactionService, Transaction, State } from 'igniteui-angular';
    // import { IgxFlatTransactionFactory, TRANSACTION_TYPE, IgxBaseTransactionService,
    // TransactionService, Transaction, State } from '@infragistics/igniteui-angular'; for licensed package
    
    class CustomTransactionService extends IgxBaseTransactionService {
        ...
    }
    
    export class CustomTransactionFactory extends IgxFlatTransactionFactory {
        ...
        create(type: TRANSACTION_TYPE): TransactionService<Transaction, State> {
            if (type === TRANSACTION_TYPE.Base) {
                return new CustomTransactionService();
            }
            super.create(type);
        }
    }
    
    

    Then, in your component's providers array, override the IgxFlatTransactionFactory (used by igx-grid) with your custom implementation.

    import { IgxFlatTransactionFactory } from 'igniteui-angular';
    // import { IgxFlatTransactionFactory } from '@infragistics/igniteui-angular'; for licensed package
    import { CustomTransactionFactory } from '../custom-factory.ts';
    
    @Component({
        selector: 'app-grid-view',
        template: `<igx-grid [batchEditing]="true" [data]="data" [autoGenerate]="true"></igx-grid>`,
        providers: [{ provide: IgxFlatTransactionFactory, useClass: CustomTransactionFactory }]
    })
    
    export class GridViewComponent {
        ...
    }
    

    Now, when batchEditing is set to true, the grid will receive an instance of CustomTransactionService.

    Additional Resources