Form with textfield and grid: send all values to the server - extjs

In create and update forms, it is sometimes necessary to give the user the ability to dynamically add fields to values of the same type (more than one phone, more than one address, etc.).
I'm exploring several possibilities to do this.
One of them is to use a grid as a form field.
However, I have doubts about how best to implement this idea, especially on how to send all the form field values (textfield and grid) to the server (and then how to load them later in the form to edit).
Fiddles with some ideas:
One with cellediting plugin https://fiddle.sencha.com/#view/editor&fiddle/2ftp
Another one with roweditin gplugin a https://fiddle.sencha.com/#view/editor&fiddle/2fto

Not sure about the "best to implement", but I have seen so many requirements for multivalue input, that for reusability I have in my toolbox a gridfield similar to the following one:
Ext.define('Ext.ux.GridField', {
extend: 'Ext.form.FieldContainer',
alias: 'widget.gridfield',
initComponent: function () {
var me = this;
if(!me.columns) me.columns = {
dataIndex: 'field1'
};
if(!me.mapFn) me.mapFn = function(value) {
if(Ext.isObject(value)) return value;
return {
field1: value
};
};
if(!me.unmapFn) me.unmapFn = function(record) {
return record.get('field1');
};
me.grid = Ext.widget(Ext.apply({
xtype: 'grid',
viewConfig: {
markDirty: false
},
store: me.store || Ext.create('Ext.data.Store', {
fields: me.columns.map(function(column) {
return {
name: column.dataIndex,
type: column.dataType || 'auto',
defaultValue: column.defaultValue
};
}),
listeners: {
update: me.updateValue,
datachanged: me.updateValue,
scope: me
}
}),
columns: [{
xtype: 'actioncolumn',
getClass: function () {
return 'x-fa fa-times'
},
handler: function(grid, rowIndex, colIndex, item, e, record) {
grid.getStore().remove(record);
},
width: 35
}].concat(me.columns),
bbar: [{
xtype: 'button',
iconCls: 'x-fa fa-pencil',
text: 'Add',
handler: function(btn) {
var grid = btn.up('grid'),
store = grid.getStore(),
record = store.add(Ext.clone(me.emptyRecord) || {})[0];
grid.getPlugin('editor').startEditByPosition({
row: store.indexOf(record),
column: 1
});
}
}],
plugins: [
Ext.create('Ext.grid.plugin.CellEditing', {
pluginId: 'editor',
clicksToEdit: 1
})
]
}, me.gridConfig)); // "gridConfig" config can override everything on each instance.
me.hiddenField = Ext.widget({
xtype: 'hiddenfield',
name: me.name,
value: '',
allowNull: false,
rawToValue: function (raw) {
return raw;
},
valueToRaw: function (value) {
return value;
},
getRawValue: function () {
return Ext.valueFrom(this.rawValue, '')
},
isEqual: function (a, b) {
return Ext.encode(a) == Ext.encode(b)
},
listeners: {
change: function(field, nV, oV) {
if(!Ext.isArray(nV)) nV = [nV];
var store = me.grid.getStore();
store.removeAll();
store.add(nV.map(me.mapFn));
}
}
});
Ext.apply(me, {
layout: 'fit',
items: [{
xtype:'container',
border: 1,
style: {
borderColor: '#d0d0d0',
borderStyle: 'solid'
},
items: [me.grid]
}, me.hiddenField]
});
me.callParent(arguments);
},
updateValue: function() {
var me = this,
grid = me.grid,
hiddenField = me.hiddenField,
nV = grid.getStore().getRange().map(me.unmapFn, me),
oV = me.hiddenField.getValue();
if(!oV || Ext.isArray(oV) && Ext.encode(nV) != Ext.encode(oV)) {
hiddenField.suspendCheckChange++;
hiddenField.setValue(nV);
hiddenField.suspendCheckChange--;
me.fireEvent('change', me, nV, oV);
}
}
});
which can then be used like this:
},{
xtype: 'gridfield',
fieldLabel: 'Contacts',
name: 'contacts',
columns: [{
text: 'Type',
dataIndex: 'type',
editor:{
xtype: 'combobox',
name: 'type',
valueField: 'name',
displayField: 'name',
store: combostore,
queryMode: 'local'
},
flex: 0.7
},{
text: 'Description',
dataIndex: 'description',
editor:{
xtype: 'textfield',
name: 'description'
},
flex: 1
}],
mapFn: function(value) {
return value;
},
unmapFn: function(record) {
return record.getData();
}
}, {
I have made a fiddle for you based on your fiddle, including working load and save operations on the form, but in ExtJS 6.x. And I have checked that it works with ExtJS 5 as well, although you have to add working icons.

Related

ExtJS: Issue with scope in class

I'm keep facing with a issue to choice exact component with scope. As you'll notice below I've created 2 different functions inside gridpanel. One of those creates a Ext.MessageBox for confirm. And other function creates a Ext.window.Window depends on button click of MessageBox.
The thing here is; It should destroy related component with cancel and no buttons. Both buttons always point to gridpanel because of var me = this state and destroys the gridpanel itself.
How can I point destroy method directly to related component?
Ext.define('MyApp.FooGrid', {
extend: 'Ext.grid.Panel',
reference: 'fooGrid',
getGridMenu: function () {
// Here is the 'Update' function; with right-click user being able to see `contextmenu`
var me = this;
var ret = [
{
text: 'Update',
listeners: {
click: me.onUpdate,
scope: me
}
}
];
return me.callParent().concat(ret);
},
onUpdate: function () {
var me = this,
gridRec = this.getSelectionModel().getSelection(); // Here being able to retrieve row data.
Ext.MessageBox.confirm(translations.confirm, translations.confirmChange, me.change, me);
return gridRec;
},
change: function (button) {
var me = this;
var selectedRec = me.onUpdate();
var selectedRecEmail = selectedRec[0].data.email; //Retrieves selected record's email with right-click action
if (button === "yes") {
return new Ext.window.Window({
alias: 'updateWin',
autoShow: true,
title: translations.update,
modal: true,
width: 350,
height: 200,
items: [
{
xtype: 'container',
height: 10
},
{
xtype: 'textfield',
width: 300,
readOnly: true,
value: selectedRecEmail //Display selected record email
},
{
xtype: 'textfield',
width: 300,
fieldLabel: translations.newPassword
}
],
dockedItems: [
{
xtype: 'toolbar',
dock: 'bottom',
items: [
{
xtype: 'tbfill'
},
{
xtype: 'button',
text: translations.cancel,
listeners: {
click: function () {
me.destroy(); // Here is the bug: When user clicks on this button; should destroy current window but it destroys 'gridpanel' itself
}
}
},
{
xtype: 'button',
text: translations.save,
listeners: {
click: function () {
console.log("I'll save you!");
}
}
}
]
}
]
});
} else {
console.log('this is no!');
me.destroy(); // Another bug raises through here: If user will click on No then 'messagebox' should destroy. This one is destroys the gridpanel as well.
}
}
});
How can I point destroy method directly to related component?
Firstly on confirmation box button's(No) click, you don't need to destroy it will automatically hide the box whenever you click into No.
And for update window instead of using me.destroy() you need to use directly button.up('window').destroy() so it will only destroy your update window not the grid.
And also you don't need to again call me.onUpdate() inside of change function otherwise it will again show the confirmation box. You can directly get selected record on the change function like this me.getSelection().
In this Fiddle, I have created a demo using your code and I have put my efforts to get result.
CODE SNIPPET
Ext.application({
name: 'Fiddle',
launch: function () {
Ext.create('Ext.data.Store', {
storeId: 'demostore',
fields: ['name', 'email', 'phone'],
data: [{
name: 'Lisa',
email: 'lisa#simpsons.com',
phone: '555-111-1224'
}, {
name: 'Bart',
email: 'bart#simpsons.com',
phone: '555-222-1234'
}, {
name: 'Homer',
email: 'homer#simpsons.com',
phone: '555-222-1244'
}, {
name: 'Marge',
email: 'marge#simpsons.com',
phone: '555-222-1254'
}]
});
Ext.create('Ext.grid.Panel', {
title: 'Demo GRID',
store: 'demostore',
columns: [{
text: 'Name',
dataIndex: 'name'
}, {
text: 'Email',
dataIndex: 'email',
flex: 1
}, {
text: 'Phone',
dataIndex: 'phone'
}],
height: 200,
listeners: {
itemcontextmenu: function (grid, record, item, index, e, eOpts) {
e.stopEvent();
grid.up('grid').getGridMenu().showAt(e.getXY());
}
},
renderTo: Ext.getBody(),
getGridMenu: function () {
var me = this;
if (!me.contextMenu) {
me.contextMenu = Ext.create('Ext.menu.Menu', {
width: 200,
items: [{
text: 'Update',
handler: me.onUpdate,
scope: me
}]
});
}
return me.contextMenu;
},
onUpdate: function () {
var me = this;
Ext.MessageBox.confirm('Confirmation ', 'Are your sure ?', me.change, me);
},
change: function (button) {
var me = this,
selectedRecEmail = me.getSelection()[0].data.email; //Retrieves selected record's email with right-click action
if (button === "yes") {
return new Ext.window.Window({
autoShow: true,
title: 'Update',
modal: true,
width: 350,
height: 200,
items: [{
xtype: 'tbspacer',
height: 10
}, {
xtype: 'textfield',
width: 300,
readOnly: true,
value: selectedRecEmail //Display selected record email
}, {
xtype: 'textfield',
inputType:'password',
width: 300,
fieldLabel: 'New Password'
}],
dockedItems: [{
xtype: 'toolbar',
dock: 'bottom',
items: [{
xtype: 'tbfill'
}, {
xtype: 'button',
text: 'cancel',
listeners: {
click: function (btn) {
btn.up('window').destroy(); // Here is the bug: When user clicks on this button; should destroy current window but it destroys 'gridpanel' itself
}
}
}, {
xtype: 'button',
text: 'save',
listeners: {
click: function () {
console.log("I'll save you!");
}
}
}]
}]
});
}
}
});
}
});

How to create a Form for each row in a grid panel:extjs

How do i create a different form for each row in the Grid...
i have a grid like ..
Ext.create('Ext.grid.Panel', {
title: 'Simpsons',
store: Ext.data.StoreManager.lookup('simpsonsStore'),
columns: [
{ text: 'Name', dataIndex: 'name' },
{ text: 'Email', dataIndex: 'email', flex: 1 },
{ text: 'Phone', dataIndex: 'phone' }
],
height: 200,
width: 400,
renderTo: Ext.getBody()
});
Your question doesn't explain your problem very well. Please update your topic and question. As I understood from your question, yes, you can. There are couple of ways. One of them is putting a form into a grid cell using grid column renderer. Another way is using editor in grid column. The second way is easy, but it's not proper way. If you want the second way also, I can add it. So, I'll add an efficient one. Please check the code below and fiddle:
https://fiddle.sencha.com/#fiddle/11i5
Ext.define('UploadForm', {
extend: 'Ext.form.Panel',
width: 200,
frame: true,
items: [{
xtype: 'filefield',
name: 'photo',
msgTarget: 'side',
allowBlank: false,
buttonText: 'Select'
}],
buttons: [{
text: 'Upload',
handler: function() {
var form = this.up('form').getForm();
if(form.isValid()){
form.submit({
url: 'photo-upload.php',
waitMsg: 'Uploading your photo...',
success: function(fp, o) {
Ext.Msg.alert('Success', 'Your photo "' + o.result.file + '" has been uploaded.');
}
});
}
}
}],
initComponent: function () {
if (this.delayedRenderTo) {
this.delayRender();
}
this.callParent();
},
delayRender: function () {
Ext.TaskManager.start({
scope: this,
interval: 200,
run: function () {
var container = Ext.fly(this.delayedRenderTo);
if (container) {
this.render(container);
return false;
} else {
return true;
}
}
});
}
});
var store = Ext.create('Ext.data.Store', {
fields: ['Name', 'Phone', 'Email', 'filePath'],
data: [{
Name: 'Rick',
Phone: '23122123',
Email: 'something#mail.com',
filePath: '/home'
}, {
Name: 'Jane',
Phone: '32132183',
Email: 'some#thing.com',
filePath: '/home'
}]
});
var renderTree = function(value, meta, record) {
var me = this;
var container_id = Ext.id(),
container = '<div id="' + container_id + '"></div>';
Ext.create('UploadForm', {
delayedRenderTo: container_id
});
return container;
}
Ext.create('Ext.grid.Panel', {
title: 'Simpsons',
store: store,
columns: [
{ text: 'Name', dataIndex: 'Name' },
{ text: 'Email', dataIndex: 'Email' },
{ text: 'Phone', dataIndex: 'Phone' },
{ text: 'Upload',
dataIndex: 'filePath',
width: 300,
renderer: renderTree
}
],
renderTo: Ext.getBody()
});
P.s. Its based from Render dynamic components in ExtJS 4 GridPanel Column with Ext.create

How to display image in column of grid based on value in column's renderer?

I have a grid panel like:
Ext.define('Demo.view.main.content.source.Ex', {
extend: 'Ext.grid.Panel',
requires: [
'Demo.store.main.content.source.Ex',
'Demo.view.main.content.source.ExController',
],
xtype: 'app-main-content-ex',
title: 'Example',
store: 'Demo.store.main.content.source.Ex',
controller:'main-content-source-ex',
//multiSelect: false,
columnLines: true,
initComponent: function() {
var store = Ext.create('Demo.store.main.content.source.Ex', {
storeId: 'app-main-content-source-exstore'
});
Ext.apply(this, {
store: store
});
this.columns= [
{
text : 'Driver Name',
flex : 3,
sortable : false,
dataIndex: 'name'
},
{
xtype: 'gridcolumn',
getEditor: function(record) {
console.log(record.get('state'));
var value;
if (record.get('state') == 'free') {
value = 'xf09c#FontAwesome'
} else {
value = 'xf023#FontAwesome'
}
return Ext.create('Ext.grid.CellEditor', {
field:{
xtype: 'image',
glyph: value
}
});
},
text : 'State',
flex : 1,
dataIndex: 'state'
}]
this.callParent();
},
listeners:{
afterRender: 'setUpInfo'
}
});
I am loading the store of that in grid afterrender event. I want to set the image in the State column based on the value of state(free/busy). Its not working.
How should I do it?
You can use a renderer to augment the displayed value of the cell.
columns: [{
xtype: 'gridcolumn',
dataIndex: 'name',
text: 'Driver Name',
flex: 1,
editor: {
xtype: 'textfield'
}
}, {
xtype: 'gridcolumn',
renderer: function(value) {
if (value == 'free') {
return 'xf09c#FontAwesome'
} else {
return 'xf023#FontAwesome'
}
},
getEditor: function(record) {
var value;
if (record.get('state') == 'free') {
value = 'xf09c#FontAwesome'
} else {
value = 'xf023#FontAwesome'
}
return Ext.create('Ext.grid.CellEditor', {
field: {
xtype: 'image',
glyph: value
}
});
},
text: 'State',
flex: 1,
dataIndex: 'state'
}]
Docs: - http://docs.sencha.com/extjs/5.1/5.1.0-apidocs/#!/api/Ext.grid.column.Column-cfg-renderer

ExtJs getValues() from Form

i have a question.
probably it will be a easy solution.
how can i get the Values of the textfields, when i click the Save Button???
Ext.define('MyApp.view.main.MyForm', {
extend: 'Ext.Window',
layout: 'column',
.
.
.
defaults: {
layout: 'form',
xtype: 'container',
defaultType: 'textfield',
labelWidth: 150,
width: 300
},
items: [{
items: [
{ fieldLabel: 'FirstName', allowBlank: false },
{ fieldLabel: 'LastName', allowBlank: false },
]
}, {
items: [
{ fieldLabel: 'Street' },
{ fieldLabel: 'Town' },
]
}],
buttons: [
{ text: 'Save', handler: function(){ alert('Saved!'); } },
]
});
You must use form field container, for example - Ext.form.Panel.
Then you can use getForm() and then getValues(), also check your fields - isValid() for checking allowBlank.
var formPanel = Ext.create('Ext.form.Panel', {
name: 'myfieldform',
defaults: {
layout: 'form',
xtype: 'container',
defaultType: 'textfield',
labelWidth: 150,
width: 300
},
items: [{
items: [
{
fieldLabel: 'FirstName',
allowBlank: false
},
{
fieldLabel: 'LastName',
allowBlank: false
},
]
}, {
items: [
{ fieldLabel: 'Street' },
{ fieldLabel: 'Town' },
]
}]
});
Ext.define('MyApp.view.main.MyForm', {
...
items: [
formPanel
],
buttons: [
{
text: 'Save',
handler: function(btn) {
var form = btn.up().up().down('[name="myfieldform"]').getForm(),
values;
if (!form || !form.isValid())
{
alert('Check your form please!');
return;
}
values = form.getValues();
for(var name in values) {
alert(values[name]);
}
}
}
]
});
Sencha Fiddle Example
Your handler function will have the button and the event options in it's signature. Use the button and the "Up" function to get the form element and retrieve the record model attached to the form (assuming you are using models).
handler: function(btn, eOpts){
var form = btn.up('form');
form.getForm().updateRecord();
var record = form.getForm().getRecord();
alert('Saved!');
}
If you are not using a model and just want the values add an itemId to each field in your form and again use the up function with a "#" to retrieve a specific component. Then simply use the getValue method.
items: [
{ fieldLabel: 'FirstName', itemId: 'firstnamefield', allowBlank: false },
{ fieldLabel: 'LastName', itemId: 'lastnamefield', allowBlank: false },
]
handler: function(btn, eOpts){
var firstNameField = btn.up('#firstnamefield');
var firstNameValue = firstNameField.getValue();
alert('Saved!');
}
Seriously, why go with the up.up.down approach, if you can get to the thing straight away?
var form = Ext.ComponentQuery.query('[name="myfieldform"]').getForm()[0];
Or
values = Ext.ComponentQuery.query('[name="myfieldform"]').getForm()[0].getValues();
In other words, take above answer and make it like this:
var formPanel = Ext.create('Ext.form.Panel', {
name: 'myfieldform',
defaults: {
layout: 'form',
xtype: 'container',
defaultType: 'textfield',
labelWidth: 150,
width: 300
},
items: [{
items: [
{
fieldLabel: 'FirstName',
allowBlank: false
},
{
fieldLabel: 'LastName',
allowBlank: false
},
]
}, {
items: [
{ fieldLabel: 'Street' },
{ fieldLabel: 'Town' },
]
}]
});
Ext.define('MyApp.view.main.MyForm', {
...
items: [
formPanel
],
buttons: [
{
text: 'Save',
handler: function(btn) {
var form = Ext.ComponentQuery.query('[name="myfieldform"]').getForm()[0];
if (!form || !form.isValid())
{
alert('Check your form please!');
return;
}
values = form.getValues();
for(var name in values) {
alert(values[name]);
}
}
}
]
});

Live Search on Grid --ExtJs

I am new to ExtJs, and i am playing around to build logic to perform "live search" on Grid columns.
From the code below i am able to populate data into the grid but cannot make live search functionality. i am not sure where i am missing the logic.
Ext.define('abc.view.EmployeePanel', {
extend: 'Ext.window.Window',
alias: 'widget.EmployeePanel',
requires: [
'Ext.tab.Panel',
'Ext.form.*'],
constructor: function () {
this.callParent(arguments);
},
this. employeePopUPGridStore = new Ext.data.ArrayStore({
fields: [
{
name: 'empid',
type: 'number'
},
{
name: 'fname',
type: 'string'
},
{
name: 'lname',
type: 'string'
},
],
});
this.employeePopUPGridStore.loadData(localAr, false);
this.down('#addempgrid').bindStore(this.employeePopUPGridStore);
this.down('#addempgrid').getView().refresh();
},
items: [{
xtype:'textfield',
name:'search',
itemId:'search',
emptyText:'Search by First Name / Last Name',
listeners: {
onTextFieldChange: function(field, newValue, oldValue, eOpts){
var grid = field.down('addempgrid');
grid.store.clearFilter();
if (newValue) {
var matcher = new RegExp(Ext.String.escapeRegex(newValue), "i");
grid.store.filter({
filterFn: function(item) {
return matcher.test(item.get('empid')) ||
matcher.test(item.get('fname')) ||
matcher.test(item.get('job'));
}
});
}
}
}
},
{
xtype: 'gridpanel',
itemId: 'addempgrid',
autoHeight: true,
columns: [
{
header: "Employee ID",
flex: 1,
dataIndex: 'empid',
},
{
header: "Full Name",
flex: 3,
dataIndex: 'fname'
},
{
header: "LastName",
flex: 1,
dataIndex: 'lname'
},
]
}
] }
});
Any help around is much appreciated.
Hi Please try this way once..
{
xtype: 'textfield',
itemId: 'searchBar',
cls: 'search-bar',
width: 230,
margin: '0 0 0 10',
listeners: {
buffer: 250,
scope: this,
change: function (field, newVal) {
var grid = field.down('addempgrid');
grid.store.clearFilter();
if (newValue) {
var matcher = new RegExp(Ext.String.escapeRegex(newValue), "i");
grid.store.filter({
filterFn: function(item) {
return matcher.test(item.get('empid')) ||
matcher.test(item.get('fname')) ||
matcher.test(item.get('job'));
}
});
}
}
}
}
Hope it helps you

Resources