Here, you will learn how to implement CRUD in Ext JS 6 MVVM architecture.
We created our first Ext JS 6 application in First Ext JS Application section. Here, we will continue from there and will create a data entry form for students with Create, Read, Update, Delete, Reset and clear buttons which will look like below.
Let's start with model first. Create Student.js file into app -> model folder and create School.model.Student
class as shown below.
Ext.define('School.model.Student', { extend: 'Ext.data.Model', idProperty:'Id', schema: { namespace: 'School.model' }, fields: [ { name: 'Id', type: 'int', defaultValue: 0}, { name: 'firstName', type: 'string' }, { name: 'middleName', type: 'string' }, { name: 'lastName', type: 'string' }, { name: 'birthDate', type: 'date' }, { name: 'address1', type: 'string' }, { name: 'address2', type: 'string' }, { name: 'city', type: 'string' }, { name: 'state', type: 'string' } ], validations: [{ type: 'presence', field: 'firstName' }] });
Now, let's create a form view using classic toolkit of Ext JS 6. So, create student folder into classic -> src -> view folder and then add Student.js file as shown below.
Now, create a custom form view class that derives from Ext.form.Panel
with necessary textfields and buttons as shown below.
Ext.define('School.view.student.Student', { extend: 'Ext.form.Panel', xtype: 'studentForm', title: 'Student Entry Form', controller: 'student', initComponent: function () { Ext.apply(this, { //set jsonsubmit to true for CUD operation using form.Submit() jsonSubmit: true, url: '/api/student', resizable: false, collapsible: false, bodyPadding: '5', buttonAlign: 'center', border: false, trackResetOnLoad: true, layout: { type: 'vbox' }, fieldDefaults: { xtype: 'textfield', msgTarget: 'side', labelAlign: 'top', labelStyle: 'font-weight:bold' }, defaultType: 'textfield', items: [{ xtype: 'fieldcontainer', layout: 'hbox', defaultType: 'textfield', width: '100%', fieldDefaults: { labelAlign: 'top', labelStyle: 'font-weight:bold' }, items: [{ fieldLabel: 'Id', name: 'Id', readOnly: true, width: 55 }, { fieldLabel: 'First Name', flex: 1, name: 'firstName', margin: '0 0 0 5', allowBlank: false }, { name: 'middleName', width: 150, margin: '0 0 0 5', fieldLabel: 'Middle Name:' }, { fieldLabel: 'Last Name', flex: 1, margin: '0 0 0 5', name: 'lastName' }] }, { xtype: 'datefield', fieldLabel: 'Date of Birth', name: 'birthDate' }, { xtype: 'textfield', fieldLabel: 'Address', width: '100%', name: 'address1' }, { xtype: 'textfield', hideLabel: true, name: 'address2', width: '100%', fieldLabel: 'address2' }, { xtype: 'textfield', fieldLabel: 'City', width: '100%', name: 'city' }, { xtype: 'textfield', fieldLabel: 'state', width: '100%', name: 'state' } ], buttons: [{ text: 'Create', itemId: 'btnCreate', formBind: true, handler: 'onCreateClick' }, { text: 'Read', itemId: 'btnLoad', handler: 'onReadClick' }, { text: 'Update', itemId: 'btnUpdate', formBind: true, handler: 'onUpdateClick' }, { text: 'Delete', itemId: 'btnDelete', formBind: true, handler: 'onDeleteClick' }, { text: 'Reset', itemId: 'btnReset', handler: 'onResetClick' }, { text: 'Clear', itemId: 'btnClear', handler: 'onClearClick' }] }); this.callParent(arguments); }, clearForm: function () { this.getForm().getFields().each(function (field) { field.validateOnChange = false; field.setValue(''); field.resetOriginalValue(); }); } });
Now, add ViewController class that handles all events of the above Student view. Create StudentController.js into app -> view -> student folder as shown below.
The ViewController class from student must be dericed from Ext.app.ViewController
. The following is a StudentViewController class that handles all the events of Student view.
Ext.define('School.view.student.StudentController', { extend: 'Ext.app.ViewController', alias: 'controller.student', onCreateClick: function (sender, record) { var studentForm = this.getView().getForm(); if (!studentForm.isDirty()) { Ext.Msg.alert('Status', 'No new data to create.'); return; } else if (!studentForm.isValid()) { Ext.Msg.alert('Status', 'Invalid data.'); return; } // Submit the Ajax request and handle the response studentForm.submit({ url: '/api/student', waitMsg: 'Saving..', headers: { 'Content-Type': 'application/json' }, clientValidation: true, submitEmptyText: true, success: function (form, action) { var student = Ext.create('School.model.Student'); var resp = Ext.decode(action.response.responseText); if (resp.data[0]) { // addstudent returns student model with Id so we can re-load model into form so form will have isDirty false student.set(resp.data[0]); studentForm.loadRecord(student); } Ext.Msg.alert('Status', 'Saved successfully.'); }, failure: function (form, action) { if (action.failureType === Ext.form.action.Action.CLIENT_INVALID) { Ext.Msg.alert('CLIENT_INVALID', 'Something has been missed. Please check and try again.'); } if (action.failureType === Ext.form.action.Action.CONNECT_FAILURE) { Ext.Msg.alert('CONNECT_FAILURE', 'Status: ' + action.response.status + ': ' + action.response.statusText); } if (action.failureType === Ext.form.action.Action.SERVER_INVALID) { Ext.Msg.alert('SERVER_INVALID', action.result.message); } } }); }, onReadClick: function (sender, record) { var studentForm = this.getView().getForm(); //result should contain success=true and data property otherwise it will go to failure even if there is no failure studentForm.load({ waitMsg: 'Loading...', method: 'GET', params: { id: 1 }, success: function (form, action) { try { var resp = Ext.decode(action.response.responseText); if (resp.data.length > 0) { // addstudent returns student model with Id so we can re-load model into form so form will have isDirty false var student = Ext.create('School.model.Student'); student.set(resp.data[0]); studentForm.loadRecord(student); } } catch (ex) { Ext.Msg.alert('Status', 'Exception: ' + ex.Message); } }, failure: function (form, action) { Ext.Msg.alert("Load failed", action.result.errorMessage); } }); }, onUpdateClick: function (sender, record) { var studentForm = this.getView().getForm(); if (!studentForm.isDirty()) { Ext.Msg.alert('Status', 'No pending changes to save.'); return; } else if (!studentForm.isValid()) { Ext.Msg.alert('Status', 'Invalid data.'); return; } studentForm.submit({ url: '/api/student', waitMsg: 'Updating..', method: 'PUT', headers: { 'Content-Type': 'application/json' }, clientValidation: true, success: function (form, action) { try { var student = Ext.create('School.model.Student'); var resp = Ext.decode(action.response.responseText); if (resp.data.length > 0) { // addstudent returns student model with Id so we can re-load model into form so form will have isDirty false student.set(resp.data[0]); studentForm.loadRecord(student); } Ext.Msg.alert('Status', 'Saved successfully.'); } catch (ex) { Ext.Msg.alert('Status', 'Exception: ' + ex.Message); } }, failure: function (form, action) { if (action.failureType === Ext.form.action.Action.CLIENT_INVALID) { Ext.Msg.alert('CLIENT_INVALID', 'Something has been missed. Please check and try again.'); } if (action.failureType === Ext.form.action.Action.CONNECT_FAILURE) { Ext.Msg.alert('CONNECT_FAILURE', 'Status: ' + action.response.status + ': ' + action.response.statusText); } if (action.failureType === Ext.form.action.Action.SERVER_INVALID) { Ext.Msg.alert('SERVER_INVALID', action.result.message); } } }); }, onDeleteClick: function (sender, record) { var me = this, studentForm = me.getView(); if (!studentForm.getValues(false, false, false, true).Id) { Ext.Msg.alert('Status', 'Invalid or No data to delete.'); return; } var student = Ext.create('School.model.Student'), data; student.set(studentForm.getValues()); data = student.getData(); Ext.Msg.show({ title: 'Delete', msg: 'Do you want to delete this record? ', width: 300, closable: false, buttons: Ext.Msg.YESNO, icon: Ext.Msg.QUESTION, fn: function (buttonValue, inputText, showConfig) { if (buttonValue === 'yes') { studentForm.submit({ url: '/api/student', method: 'DELETE', clientValidation: true, waitMsg: 'Deleting..', headers: { 'Content-Type': 'application/json' }, success: function (form, action) { try { var resp = Ext.decode(action.response.responseText); studentForm.clearForm(); Ext.Msg.alert('Success', resp.message); } catch (ex) { Ext.Msg.alert('Status', 'Exception: ' + ex.Message); } }, failure: function (form, action) { if (action.failureType === Ext.form.action.Action.CLIENT_INVALID) { Ext.Msg.alert('CLIENT_INVALID', 'Something has been missed. Please check and try again.'); } if (action.failureType === Ext.form.action.Action.CONNECT_FAILURE) { Ext.Msg.alert('CONNECT_FAILURE', 'Status: ' + action.response.status + ': ' + action.response.statusText); } if (action.failureType === Ext.form.action.Action.SERVER_INVALID) { Ext.Msg.alert('SERVER_INVALID', action.result.message); } } }); } } }); }, onResetClick: function (sender, record) { this.getView().getForm().reset(); }, onClearClick: function (sender, record) { this.getView().clearForm(); }, });
As you can see in the above controller class, it reads data from the remote server using form.load() method and perform CUD operation using form.submit() method.
It calls rest api url /api/student
for CRUD operation. (The full url of rest api is http://localhost/api/student
) To load the data, form.load() method sends GET request to /api/student
url.
The same way, form.submit() sends POST request to create a new student record, PUT request to update the record and DELETE request to delete the record. Please check click handler of Create, Update, Delete and Read buttons to know more.
Thus, you can implement CRUD operation in Ext JS Form. Click Live Example below to see the whole example code in MVVM folder structure.
Live Example