extjs - Change setting in property grid and save it to the server - extjs

I have a property grid Ext.grid.property.grid. I want to use it to allow the user to change his account settings. I achieved to display custom editors, but I'm stuck to get the new value saved to the server.
Ext.define('Mb.view.Settings', {
extend: 'Ext.grid.property.Grid',
alias: 'widget.settings',
requires: ['Ext.tab.Panel'],
title: Lang._('Options'),
source: {
username: Mb.user.name,
email: Mb.settings.email
},
sourceConfig:{
username: {
displayName: Lang._('Nom d\'utilisateur'),
editor: 'displayfield'
},
email: {
displayName: Lang._('Adresse email'),
editor: {
xtype: 'textfield',
vtype: 'email'
}
}
}
});
I look for an event that allows me to trigger an Ajax call to the server to save the new setting, but I cannot find what I'm looking for in the documentation.

You are looking in the wrong place: it is the record that changes, meaning you have to look in the attached store: look at the store's datachanged event and then use store.getModifiedRecords().

Related

Duplicate reference when maximizing/restoring dialog

When maximizing/restoring a dialog that contains some form fields with names, like:
Ext.create('Ext.Dialog', {
maximizable: true,
items: {
xtype: 'textfield',
name: 'id',
bind: '{record.id}'
},
buttons: [{
text: 'Save',
bind: {
disabled: '{!record.valid}'
}
}]
}).show();
we're getting an error:
Ext.mixin.Container.attachNameRef(): Duplicate name: "id" on ext-viewport between ext-textfield-1 and ext-textfield-5
Two found workarounds :
Disable animation
Ext.define('Override.Dialog', {
override: 'Ext.Dialog',
config: {
maximizeAnimation: false,
restoreAnimation: false
}
});
Make the proxy used for animation have no items (nor buttons since button disable state may not reflect the bounded value
Ext.define('Override.Dialog', {
override: 'Ext.Dialog',
config: {
maximizeProxy: {
items: null,
buttons: null
}
}
});
Background Information
During maximize and minimize ExtJS creates a shadow clone.
This will create a clone of the window, while you still have the original item.
Using an ID means, there can only be one identical one at any given time.
The clone tries to create the your textfield with the same ID, which does not work.
Typically you want to
for forms you usually do not need to grab each item as you can work with validator and getValues on the form
otherwise you might want to work with references in the view and lookupReference in the controller.
not use animation (because that does not create a clone)
write your own maximize animation and do the animation part yourself (write your own maximize function)

How to load data into Combobox?

I try to fill the form with the response data from the backend,but my formpanel contains combobox,how could load the data into combobox.
I tried to use the method setValues() in formpanel,and I set the displayField and valueField, but the combobox still shows [object,object].
Here is my store code
Ext.define('Servicemsg.directory.AppSystemStore', {
extend:'Ext.data.Store',
fields:['id','code','name','comments','lastupdatetime'],
proxy:{
type:'ajax',
url:'',
reader:{
type:'json',
}
},
})
Here is the code of combobox
{
xtype: 'combobox',
label: 'Provider',
name:'provider',
store:Ext.create('Servicemsg.directory.AppSystemStore'),
displayField:'name',
valueField:'id',
autoLoadOnValue:true
}
Here is my request code
form.load({
url:'#url#'+data.id,
success:function (form,result,data) {
form.setValues(result.result);
}
});
Response data like this
{
name:'',
text:'',
id:'',
provider:{
id:'',
name:'',
}
}
You can grab the combobox of the form and add the values to the combobox's store manually. Once the combobox store has the values, you can set a value to the combobox if applicable.
form.load({
url:'#url#'+data.id,
success:function (form,result,data) {
const combobox = form.down('combobox');
combobox.getStore().add(results.providers);
combobox.setValue(/*value that the comobobox should show*/);
form.setValues(result.result);
}
});
It seems like in this case using a store is overkill. Considering you have an
empty proxy and are just using it to try and load data into your combobox.
Instead you should create a View Model and with a property comboBoxData. You should
then bind this value to the data property on your combox box.
This is what your model would like like
Ext.define('Fiddle.view.main.MainModel', {
extend: 'Ext.app.ViewModel',
data: {
comboBoxData: null
}
});
This is what your combo box should look like
{
xtype: 'combobox',
label: 'Provider',
name: 'provider',
displayField: 'name',
valueField: 'id',
autoLoadOnValue: true,
bind: {
data: '{comboBoxData}'
}
}
This is what your success function would look like.
(How you get the view model will depend on where this is being called)
success: function (form, result, data) {
var vm = this.lookupViewModel();
vm.set('comboBoxData', result.result);
}
You may have to tweak this for you specific implementation. But, hopefully this gives you a good idea of the best approach for this.

How to restrict file type using xtype filefield(extjs 4.1.0)?

I am trying to implement file upload feature using extjs 4.1.0. Whereas I want to restrict users to select only image files(jpg,png,gif). Is there any filter which can be applied so that users will only be able to see and then select the types of the files mentioned above?
You could stuff like this as well :
{
xtype: 'filefield',
buttonText: '....',
listeners:{
afterrender:function(cmp){
cmp.fileInputEl.set({
accept:'image/*' // or w/e type
});
}
}
}
See http://docs.sencha.com/ext-js/4-1/#!/api/Ext.form.field.VTypes VAlidation types for an example of a custom type. You could use a regexp to specify alphaMask as well.
{
xtype: 'fileuploadfield',
name: 'file',
fieldLabel: 'Photo',
labelWidth: 50,
allowBlank: false,
buttonText: 'SelectPhoto',
anchor: '100%',
reset: function () {
var me = this,
clear = me.clearOnSubmit;
if (me.rendered) {
me.button.reset(clear);
me.fileInputEl = me.button.fileInputEl;
me.fileInputEl.set({
accept: 'image/*'
});
if (clear) {
me.inputEl.dom.value = '';
}
me.callParent();
}},
listeners:{
change: 'fileInputChange',
afterrender:function(cmp){
cmp.fileInputEl.set({
accept:'image/*'
});
}
},
regex: /(.)+((\.png)|(\.jpg)|(\.jpeg)(\w)?)$/i,
regexText: 'Only PNG and JPEG image formats are accepted'
}
regex adds client side validation, to which a form bind can be put on whatever form or action you are planning on doing.

Dynamically changing the DataStore of a ComboBox

I have a combo box which populates its values based on the selection of another combobox.
I have seen examples where the params in the underlying store are changed based on the selection, but what I want to achieve is to change the store itself of the second combo based on the selection on the first combo. This is my code, but it doesn't work. Can someone please help?
{
xtype: 'combo',
id: 'leads_filter_by',
width: 100,
mode: 'local',
store: ['Status','Source'],
//typeAhead: true,
triggerAction: 'all',
selectOnFocus:true,
typeAhead: false,
editable: false,
value:'Status',
listeners:{
'select': function(combo,value,index){
var filter_to_select = Ext.getCmp('cmbLeadsFilter');
var container = filter_to_select.container;
if (index == 0){
filter_to_select.store=leadStatusStore;
filter_to_select.displayField='leadStatusName';
filter_to_select.valueField='leadStatusId';
} else if(index==1) {
filter_to_select.store=leadSourceStore;
filter_to_select.displayField='leadSourceName';
filter_to_select.valueField='leadSourceId';
}
}
}
},
{
xtype: 'combo',
id: 'cmbLeadsFilter',
width:100,
store: leadStatusStore,
displayField: 'leadStatusName',
valueField: 'leadStatusId',
mode: 'local',
triggerAction: 'all',
selectOnFocus:true,
typeAhead: false,
editable: false
},
That is not how its designed to work!! When you set a store in the config, you are binding a store to the combo. You don't change the store, instead you are supposed to change the data when required.
The right way of doing it would be to load the store with correct data from the server. To fetch data, you can pass params that will help the server side code get the set of options you need to load.
You will not want to change the store being used... Simply put, the store is bound to the control as it is instantiated. You can, however, change the URL, and params/baseParams used in any additional POST requests.
Using these params, you can code your service to return different sets of data in your combo box's store.
For the proposed problem you can try below solution :
Use below "listener" snippet for the first "leads_filter_by" combo. It will handle the dynamic store binding / changing for the second combobox.
listeners:{
'select': function(combo,value,index){
var filter_to_select = Ext.getCmp('cmbLeadsFilter');
var container = filter_to_select.container;
if (index == 0){
//filter_to_select.store=leadStatusStore;
filter_to_select.bindStore(leadStatusStore);
filter_to_select.displayField='leadStatusName';
filter_to_select.valueField='leadStatusId';
} else if(index==1) {
//filter_to_select.store=leadSourceStore;
filter_to_select.bindStore(leadSourceStore);
filter_to_select.displayField='leadSourceName';
filter_to_select.valueField='leadSourceId';
}
}
}
Hope this solution will help you.
Thanks & Regards.
I had a similar problem. The second combobox would load the store and display the values, but when I would select a value, it would not actually select. I would click the list item and the combobox value would remain blank.
My research also suggested that it was not recommended to change the store and field mappings on a combobox after initialization so here was my solution:
Create a container in the view that would hold the combobox to give me a reference point to add it back later
Grab a copy of the initial config off of the combobox ( this lets me set my config declaritively in the view and not hard code it into my replace function ... in case I want to add other config properties later)
Apply new store, valueField and displayField to that config
Destroy old combobox
Create new combobox with modified config
Using my reference from step 1, add the new combobox
view:
items: [{
xtype: 'combobox',
name: 'type',
allowBlank: false,
listeners: [{change: 'onTypeCombo'}],
reference: 'typeCombo'
}, { // see controller onTypeCombo for reason this container is necessary.
xtype: 'container',
reference: 'valueComboContainer',
items: [{
xtype: 'combobox',
name: 'value',
allowBlank: false,
forceSelection: true,
reference: 'valueCombo'
}]
}, {
xtype: 'button',
text: 'X',
tooltip: 'Remove this filter',
handler: 'onDeleteButton'
}]
controller:
replaceValueComboBox: function() {
var me = this;
var typeComboSelection = me.lookupReference('typeCombo').selection;
var valueStore = Ext.getStore(typeComboSelection.get('valueStore'));
var config = me.lookupReference('valueCombo').getInitialConfig();
/* These are things that get added along the way that we may also want to purge, but no problems now:
delete config.$initParent;
delete config.childEls;
delete config.publishes;
delete config.triggers;
delete config.twoWayBindable;
*/
config.store = valueStore;
config.valueField = typeComboSelection.get('valueField');
config.displayField = typeComboSelection.get('displayField');
me.lookupReference('valueCombo').destroy();
var vc = Ext.create('Ext.form.field.ComboBox', config);
me.lookupReference('valueComboContainer').add(vc);
},

ExtJS Panel Inheritance/Base class

I am trying to create my own Date/Time field. I know there are a few that others have made, I'm making my own .
My question is as follows. I want to create a new object, DateTime, which extends Ext.Panel. I specify some properties for width, height, etc but I also specify the values for the items property which will contain a date field and a time field. When I try to actually instantiate the created object, I get an error saying "Object or property not supported". When I go into the error, it seems that the items collection throws an error The code is as follows:
var dateField = new AppealDate({
id: 'dateField',
tabIndex: 0,
fieldLabel: '',
msgTarget: 'under'
});
var timeField = new Ext.form.TimeField({
id: 'timeField',
tabIndex: 0,
fieldLabel: '',
msgTarget: 'under'
});
var DateTime = Ext.extend(Ext.Panel, {
id: '',
xtype: 'panel',
fieldLabel: '',
layout: 'table',
layoutConfig: {
columns: 2
},
items: [dateField, timeField]
});
var dateTimeField = new DateTime(); //this throws an error
Your class is missing initComponent. You also need to render the panel somewhere.
DateTime = Ext.extend(Ext.Panel, {
initComponent: function() {
// define dateField, timeField here.
this.dateField = new AppealDate({
id: 'dateField',
msgTarget: 'under'
});
this.timeField = new Ext.form.TimeField({
id: 'timeField',
msgTarget: 'under'
});
Ext.apply(this, {
items: [this.dateField, this.timeField]
});
DateTime.superclass.initComponent.call(this);
}
});
var dateTimeField = new DateTime();
dateTimeField.render(Ext.get('someDiv'));
As a comment outside of your direct question, "DateTime" is a terrible name for a Panel subclass. You want someone coming along later to know what kind of class they are dealing with -- "DateTimeField" would be much better, based on how you're using it (although that implies a Field subclass as explained below...).
However, note that another potential issue since you are intending to use this Panel as a Field is that a FormPanel is going to expect its form fields to support the Ext.form.Field interface, which your "field" will not (i.e., you won't be able to add your DateTime object into a form's items config). So if your goal is to create a truly reusable component that can be treated as a Field, you're going to want to add methods like getValue, setValue, markInvalid, etc. that internally interact with your constituent fields. It's not a trivial task to get it all working smoothly.
(Not sure if this is your goal, but thought I would mention it since I've gone down this road myself).

Resources