Реализация бизнес-справочника на Ext JS 6

https://github.com/ananddayalan/extjs-by-example-company-directory

Проект состоит из следующих компонентов:
  • Grid panel
  • ViewModel
  • Model
  • Data store and rest proxy
  • Layouts and containers
  • RowEditing plugins
  • Pagination
  • REST API in Go
  • References

Операции редактирования/добавления выполняются с помощью плагина RowEditing.

Структура проекта:

Самый легкий путь добавить гриду возможности редактирования это использовать плагин RowEditing:
                        plugins: [{
                            ptype: 'rowediting',
                            clicksToMoveEditor: 1,
                            autoCancel: false
                        }],

Для того чтобы редактирование заработало надо установить значение свойству editor у столбцов грида:
                               editor: {
                                    xtype: 'textfield',
                                    allowBlank: false
                                }

Путем установки правил валидации в эдиторе, плагин RowEditing позволяет ввод только валидных данных в ином случае кнопка Update становится не активной.

В гриде используются два тулбара. Один для пейджинг-тулбара. И один для кнопок ДОБАВИТЬ, УДАЛИТЬ и СОХРАНИТЬ. Эти тулбары состыкованы с гридом через свойство dockedItems. 'dockedItems' - это свойство принадлежащее панели. Оно позволяет подстыковать компоненты слева, справа, сверху или снизу. Можно подстыковать любой компонент, но обычно это тулбары.

Ext.define('CD.view.contactList.ContactList', {
    extend: 'Ext.panel.Panel',

    requires: ['CD.view.contactList.ContactListController'],
    xtype: 'app-contactList',
    controller: 'contactList',

    items: [{
        cls: 'contact-list',
        xtype: 'grid',
        reference: 'contactListGrid',
        scrollable: true,
        autoScroll: true,
        plugins: [{
            ptype: 'rowediting',
            clicksToMoveEditor: 1,
            autoCancel: false
        }],
        listeners: {
            selectionchange: 'onSelectionChange'
        },
        flex: 1,
        store: 'contactList',
        pageSize: 10,
        title: 'Company Directory',
        columns: {
            defaults: {
                editor: {
                    xtype: 'textfield',
                    allowBlank: false
                }
            },
            items: [{
                text: 'First Name',
                width: 100,
                dataIndex: 'fname'
            }, {
                text: 'Email',
                width: 250,
                dataIndex: 'email',
                editor: {
                    vtype: 'email'
                }
            },
            /* Code truncated */
            ]
        },
        dockedItems: [{
            xtype: 'pagingtoolbar',
            store: 'contactList',
            dock: 'bottom',
            displayInfo: true
        }, {
            xtype: 'toolbar',
            dock: 'top',
            ui: 'footer',
            defaults: {
                cls: 'btn-orange'
            },
            items: ['->', {
                text: 'Remove',
                disabled: true,
                reference: 'btnRemoveContact',

                listeners: {
                    click: 'onRemove'
                },
            },
            /* Code truncated */
            ]
        }]
    }]
});

Пейджинг-тулбар нуждается в сторе чтобы правильно отображать количество страниц.
dockedItems: [{
    xtype: 'pagingtoolbar',
    store: 'contactList',
    dock: 'bottom',
    displayInfo: true
}, {
    xtype: 'toolbar',
    dock: 'top',

    ui: 'footer', //This sets style to the component. The 'ui' is a property of the component. The default value of this property for all the component is 'default'. 

    defaults: {
        cls: 'btn-orange'
    },
    items: ['->', {
        text: 'Remove',

        disabled: true, //We set disabled by default, and this will
        be enabled when a row in the grid is selected.Check the
        onSelectionChange method in the controller.
        reference: 'btnRemoveContact',
        listeners: {
            click: 'onRemove'
        },
    },
    // …Code truncated

    ]
}]

Код ViewController очень простой. Он только обрабатывает события добавления, удаления и изменения выделения для вьюхи ContactList. Доступ к гриду из контроллера:
var grid = this.lookupReference('contactListGrid');
contactListGrid помечен как референс в гриде.

Стор получается методом grid.getStore(); или также можно использовать метод Ext.getStore(contactList):
Ext.define('CD.view.contactList.ContactListController', {
    extend: 'Ext.app.ViewController',

    alias: 'controller.contactList',
    views: ['CD.view.contactList.ContactList'],
    requires: ['CD.store.ContactList'],

    onSave: function() {
        //Note, this will trigger one or more calls to the server based on the number of operations performed in the store.
        Ext.getStore('contactList').save();
    },

    onSelectionChange: function() {
        this.lookupReference('btnRemoveContact').enable();
    },

    onRemove: function() {
        var grid = this.lookupReference('contactListGrid');
        var sm = grid.getSelectionModel();

        //This line cancels the row/cell edit if it is active before
        we remove the item.
        grid.plugins[0].cancelEdit();

        grid.getStore().remove(sm.getSelection());
        if (grid.getStore().getCount() > 0) {
            sm.select(0);
        }
    },

    onCreate: function() {
        var grid = this.lookupReference('contactListGrid');
        grid.plugins[0].cancelEdit();

        // Create a model instance
        var r = Ext.create('Contact');
        grid.getStore().insert(0, r);
        grid.plugins[0].startEdit(0, 0);
    }
});

Код для модели:
Ext.define('Contact', {
    extend: 'Ext.data.Model',
    fields: ['fname', 'lname', 'email', 'address', 'city', 'state', 'phone', 'type']
});
Ext.define('CD.store.ContactList', {
    extend: 'Ext.data.Store',
    storeId: 'contactList',
    model: 'Contact',
    pageSize: 10,
    proxy: {
        type: 'rest',
        url: 'contactlist',
        reader: {
            type: 'json',
            rootProperty: 'data',
            totalProperty: 'total'
        }
    }
});
Ext.create('CD.store.ContactList').load();

Свойство totalProperty позволяет стору узнать общее количество записей и сделать пагинацию.

REST API