Javascript-форум (https://javascript.ru/forum/)
-   ExtJS (https://javascript.ru/forum/extjs/)
-   -   Submit формы используя REST API (https://javascript.ru/forum/extjs/65467-submit-formy-ispolzuya-rest-api.html)

Sogl 21.10.2016 03:14

Submit формы используя REST API
 
Всем привет!

Сделал REST ресурс и нарисовал к нему грид и форму редактирования.
Создаю окно редактирования и добавления так:
this.dialog = main.add({
            xtype: 'employees-window',
            viewModel: {
                data: {
                    title: record ? 'Редактирование: ' + record.get('id') : 'Добавить сотрудника'
                },
                links: {
                    theEmployee: record || {
                        type: 'MyApp.model.employees.Employees',
                        create: true
                    }
                }
            },
        });


Сама модель помимо объявления полей содержит вот такое объявление прокси (в store код такой же):
proxy: {
        type: 'api',
        url: MyApp.Global.getApiUrl() + 'api/v1/employees'
    }


В общем, окошко функционирует нормально, выдает пустые поля для добавления и заполняет значения при редактировании. Но возникла проблема уже на этапе добавления записи. Такой код:
onSaveClick: function () {

        //get form
        var form = this.lookupReference('form');

        if (form.isValid()) {

            form.submit();
        }
    }


Выдает ошибку `No URL specified`.
Пока поиск мало информации дал... одни пишут форму сабмитить надо, другие то что с моделью работать через `model.save()`, третьи про ViewModel предлагают... пока никаких результатов у меня.

Так как правильно то добавлять и обновлять записи в store?

Sogl 21.10.2016 10:01

В общем, выкрутился вот так:
var store = Ext.getStore('employees1');

store.insert(0, this.getViewModel().data.theEmployee);

store.reload();


Что интересно, если выводишь в таблице id, то вместо него выдает мне название класса с цифрой на конце, например, `employees.Employees-7`.
`reload()` перезагружает store уже нормально, хотелось бы понять как id предварительно получить... в примерах это есть, но для раздельных сессий.

XAPuTOH 22.10.2016 06:26

Я сделал базовый клас для формы с кнопками "Сохранить" и "Отменить"
На сохранить навесил примерно такой код:
var record = form.getRecord();
 record.save()


А на отмену закрытие окна(сделал у формы modal:true) формы.

Далее при необходимости редактирования какой либо модели. Создаю новую форму - наследника от базовой. Она содержит только поля формы.
Далее form.setRecord(экземпляр модели)

Очень удобно получилось. Работает уже для кучи моделей. И как правило базового функционала хватает.

Еще у меня форма при клике сохранить вызывает событие save. на него подписывается контроллер который создал форму. И по сохранению записи и закрытии форму он например обновляет дерево или каконибудь грид.

XAPuTOH 22.10.2016 06:49

Про Id.
Если экземпляр модели новый то id по умолчанию именно такой.
Если это экземпляр из грида то у него по логике уже есть id. и он должен сохранится при setRecord/getRecord

Sogl 22.10.2016 15:18

Цитата:

Сообщение от XAPuTOH
Я сделал базовый клас для формы с кнопками "Сохранить" и "Отменить"

Можете поделиться своим решением? Если код очень объемный, то можно на gist GitHub'а.

Кстати, у вас проверяется правильность ответа сервера? А то вот у меня была ошибка в server-side коде, а при этом в store запись вставилась и в Grid'е отобразилась :blink:
Все это до перезагрузки страницы.

Цитата:

Сообщение от XAPuTOH
Еще у меня форма при клике сохранить вызывает событие save. на него подписывается контроллер который создал форму.

А модальное окно отображается поверх всего viewport'а? Тут соседнюю тему я создавал.... если делать форму как дочернюю от Grid'а, то и отображается она поверх Grid'а с оверлеем, а все остальное при этом без оверлея. Выглядит ужасно и нелепо.

Цитата:

Сообщение от XAPuTOH
Если экземпляр модели новый то id по умолчанию именно такой.

Новый экземпляр. Если выводить поле id в таблице и store не обновлять после вставки, то это ужасное значение так там и отобразится :)

Sogl 24.10.2016 08:18

А каким образом обновлять запись в store? Не удалять, не вставлять, а именно обновлять...

Не нашел метода update в документации, а мне необходимо именно обновление записи.

Если я пишу вот так:
dialog.getViewModel().data.theEmployee.save();

то событие write у Store попросту не срабатывает, хотя вызов PUT метода в REST происходит и запись обновляется!

p.s. Если попытаться обновить запись вставкой, вот так:
store.insert(index, dialog.getViewModel().data.theEmployee);


То сначала сработает необходимый нам PUT с изменениями... и тут же сразу DELETE этой записи. В итоге запись удаляется :blink:

XAPuTOH 25.10.2016 09:58

Такая базовая форма:
Ext.define('common.helpers.BaseForm', {
    extend: 'Ext.form.Panel',
    alias: 'helpers.baseform',

    controller:{
        type:'helpers.baseformcontroller'
    },


    modal:true,
    frame: true,
    closable:true,
    floating:true,
    reference: 'form',
    buttons: [
        {
            formBind: true,
            disabled: true,
            text:'Сохранить',
            handler:'save'
        },
        {
            text: 'Отменить',
            handler: function(){
                this.up('form').close();
            }
        }
    ]
});


Её контроллер:
Ext.define('KSoft.common.helpers.BaseFormController', {
    extend: 'Ext.app.ViewController',
    alias: 'controller.ksoft.helpers.baseformcontroller',

    save:function(){
        var me = this,
            form = this.view,
            record = form.getRecord();
        if (form.isValid()) {
            form.updateRecord(record);
            if(form.extraParams != undefined)
                record.proxy.setExtraParams(form.extraParams);
            record.save({
                success: function(record, operation) {
                    form.fireEvent('save', form); //--- Тут вызываем событие о том что запись успешно сохранена
                    form.close();
                },
                failure: function(record, operation) {
                    //Тут обрабатываем ошибку сохранения.
                }
            });
        } else {
            Ext.Msg.alert('Некорректные данные', 'Введите корректные данные');
        }
    }



});


Вот пример формы:
Ext.define('view.objects.ObjectForm', {
    extend: 'view.helpers.BaseForm',
    alias: 'objectform',

    title:'Объект',
    items: [
        {
            xtype: 'textfield',
            fieldLabel: 'Name',
            name: 'Name'
        }
        {
            xtype: 'textfield',
            fieldLabel: 'Code',
            name: 'Code'
        },
        {
            xtype: 'textfield',
            fieldLabel: 'Icon Cls',
            name: 'iconCls'
        },
        {
            xtype: 'textfield',
            fieldLabel: 'Long Name',
            name: 'LongName'
        },
        {
            xtype: 'textfield',
            fieldLabel: 'Short Name',
            name: 'ShortName'
        },
        {
            xtype: 'numberfield',
            fieldLabel: 'Number Name',
            name: 'IntName'
        },
        {
            xtype: 'checkboxfield',
            fieldLabel: 'isActive',
            name: 'Active',
            checked: true,
            uncheckedValue: false
        },
        {
            xtype: 'hiddenfield',
            name: 'ObjId'
        }
    ]
});


Использую так: Например есть дерево объектов в котором можно добавлять или изменять объекты. Делается это при вызове соответствующих методов контроллера. В которых я уже вызываю непосредственно форму:
var form = new view.objects.ObjectForm;

        form.loadRecord(object);

        form.on('save',this.formSave, this);
        form.show();


После сохранения формы я обновляю соответствующую ветку дерева.
Кстати форма получилась универсальной. Например у меня есть грид в качестве модели которого модель из формы выше. Так я из него вызываю данную форму и меняю свойства модели и т.п.

Окно поверх вьюпорта. так как создается с помощью new.

По поводу обновления.
Я обычно обновляю весь стор после обновления записи. Ну или конкретную ветку если это дерево.

nohuhu 26.10.2016 03:46

Цитата:

Сообщение от XAPuTOH (Сообщение 432894)
...
            if(form.extraParams != undefined)
                record.proxy.setExtraParams(form.extraParams);
            ...

В качестве лирического отступления: очень категорически не рекомендую использовать синтаксическую форму if/for/while/etc без скобок {}. И вовсе не из абстрактных соображений навроде красоты кода или кошерности стиля, а просто потому, что в JavaScript это чревато очень мерзкими и крайне трудноуловимыми жуками.

Личный опыт, один раз потратил несколько часов на отлов такого животного. Поймал только благодаря тому, что какой-то несчастный клиент вместе с ребятами из техподдержки бодались две недели и сузили набор вариантов до "в dev версии работает, в боевой сборке не работает".

XAPuTOH 26.10.2016 07:52

Да. Есть, такое.
Поправим. Этот костыль из прошлого вообще убрать нужно.
Пытались к модели привязать дополнительные параметры которые не являются свойствами модели. Решили. а строчки остались.

Sogl 28.10.2016 02:38

XAPuTOH, у вас и вставка новой записи отрабатывает через этот же код?

Sogl 03.11.2016 09:42

XAPuTOH, Сделал по примеру того как Вы написали. Вот код на редактирование:
onEdit: function(view, cell, rowIndex, colIndex, e, rec, row) {
    console.log(rec);

    var form = new MyApp.view.dictionaries.positions.PositionsForm({
        viewModel: {
            data: {
                title: 'Редактирование'
            }
        }
    });

    console.log(form);

    form.loadRecord(rec);

    form.show();
},


Форма показывается, заполняется и даже отправляется куда надо, но при этом если мы жмем "Сохранить" без редактирования, то в request'е уходит лишь id! Т е получается у нас значения уходят на сервер лишь при изменении.
Как Вы этот момент отлавливаете?

Еще почему-то у меня два PUT'а на сервер уходят вместо 1го...я так понимаю, что тут проблема в том, что после updateRecord идет сразу record.save и получается 2 одинаковых действия. Если мы убираем record.save, то уже нет проблемы отправки пустой записи и форма просто закрывается на "Сохранить".

Можете также выложить ф-цию `this.formSave`и объяснить момент со вставкой? :thanks:

p.s. Этот код не сработает никогда:
} else {
            Ext.Msg.alert('Некорректные данные', 'Введите корректные данные');
        }


Потому что в форме прописано на кнопке "Сохранить" вот это:
formBind: true,
disabled: true,


У пользователя нет никакой возможности нажать на кнопку, которая отключена =)

Sogl 08.11.2016 10:06

В общем, унифицированной универсальной формы не получилось. Получилось некое её подобие. :-?

Дело в том, что мне вставка новой записи нужна именно через Store, иначе событие write, которое показывает всплывающие toast-сообщения, не отрабатывает. А к Store, который может быть любым, неизвестно каким образом вообще обращаться из этой формы.

То, что `record.save()` не связано с обновлением хранилища писали еще тут:
http://javascript.ru/forum/extjs/564...tml#post375259

Вот такая форма:
Ext.define('MyApp.view.helpers.Baseform', {
    extend: 'Ext.form.Panel',
    xtype: 'baseform',
    controller: 'baseform',

    bind: {
        title: '{editformTitle}'
    },

    layout: {
        type: 'vbox',
        align: 'stretch'
    },

    bodyPadding: 10,
    border: false,

    width: 400,
    modal: true,
    closable: true,
    closeToolText: 'Закрыть',
    floating: true,
    reference: 'form',
    buttons: [
        {
            formBind: true,
            disabled: true,
            text:'Сохранить',
            handler:'save'
        },
        {
            text: 'Отменить',
            handler: function(){
                this.up('form').close();
            }
        }
    ]
});


Ее контроллер. Тут я проверяю id на целое число. Если целое, то у нас редактирование, если нет, то это новая запись. Также я сам record передаю при вызове события:
Ext.define('MyApp.view.helpers.BaseformController', {
    extend: 'Ext.app.ViewController',
    alias: 'controller.baseform',

    save: function() {
        var me = this,
            form = this.view,
            record = form.getRecord();

        // console.log(record);

        if (form.isValid()) {

            var id = record.getId();

            var values = form.getValues();
            // console.log(values);


            //check if edit
            if (Ext.isNumber(id)) {

                record.beginEdit();
                record.set(values);
                record.endEdit();

            } else {

                record.set(values);
                form.fireEvent('insertNew', record);
            }

            form.close();
        }
    }

});


Обработчики добавления, редактирования и создания формы:
onAdd: function() {
    console.log('onAdd');

    var record = Ext.create('MyApp.model.dictionaries.positions.Positions');
    console.log(record);

    this.createForm(record, true);
},

onEdit: function(view, cell, rowIndex, colIndex, e, rec, row) {
    this.createForm(rec, false);
},


createForm: function(record, stateNew) {
    // console.log(record);

    var form = new MyApp.view.dictionaries.positions.PositionsForm({
        viewModel: {
            data: {
                editformTitle: stateNew ? 'Добавление' : 'Редактирование'
            }
        }
    });

    // console.log(form);

    form.loadRecord(record);
    form.on('insertNew', this.formInsertNew, this);
    form.show();
},


А вот обработчик события вставки:
formInsertNew: function(record) {
    console.log('NEW RECORD!!!!');
    console.log(record);

    var store = this.getView().getStore();
    store.insert(0, record);
}


Обновляю я store в событии `write`, там же вывожу toast-сообщение.

Да, есть неудобный костыль с обращением к store, но пока иного решения не нашел.

Буду признателен, если кто-нибудь из форумчан даст ответы на эти вопросы:
  1. Каким образом можно передать Store или ссылку на него при создании формы и обращаться к нему уже в её контроллере?
  2. Можно ли записать вот эту конструкцию иначе?
    record.beginEdit();
    record.set(values);
    record.endEdit();
    
  3. Каким образом при extend'е формы добавить поля во вложенный(!) component с xtype: form? Я хочу основу сделать на Window, а форма будет уже внутри него.


Часовой пояс GMT +3, время: 16:30.