How to Set Single Field Dirty in ExtJS - extjs

PROBLEM: We toggle fields on form. When secondField is shown instead of firstField, then form is changed. But secondField is still marked as not dirty, because both fields remain unchanged. Showing secondField should always make it and the form (model) dirty.
RESEARCH: setDirty() method is done on whole record, setValue() acts as expected, but smells like a hack and can't be used for various field types (textfield, combobox).
QUESTION: How to manually set a single form field state changed to invoke saving its data?

You are mixing up data state with form visualisation. By default, "field is shown" has no relation to data, so you need to explicitly create one. This can be done by changing some data on toggling, or other way round — toggling on changing data.
For example, toggling can occur on checking/unchecking a checkbox field which will represent a piece of form data (also see fiddle):
Ext.create('Ext.form.Panel', {
viewModel: {
type: 'default'
},
items: [
{
xtype: 'checkbox',
reference: 'toggle',
itemId: 'toggle',
boxLabel: 'Toggle',
hidden: true
},
{
xtype: 'button',
text: 'Toggle',
enableToggle: true,
toggleHandler: function() {
var form = this.up('form'),
checkbox = form.child('#toggle');
checkbox.setValue(!checkbox.getValue());
console.log(form.isDirty() ? 'Dirty!' : 'Not dirty');
}
},
{
xtype: 'textfield',
name: 'firstField',
fieldLabel: 'First Field',
bind: {
hidden: '{toggle.checked}'
}
},
{
xtype: 'textfield',
name: 'secondField',
fieldLabel: 'Second Field',
bind: {
hidden: '{!toggle.checked}'
}
}
],
renderTo: Ext.getBody()
});

Related

Make ExtJS button's ariaLabel content to be dynamic

Is it possible to make the button's aria-label text dynamic in ExtJS? For example, I want the button's aria-label to be dynamically changing depending on the application's context.
Ext.create('Ext.panel.Panel', {
title: 'test title',
viewModel: {
data: {
firstname: undefined
}
},
items: [
{
xtype: 'textField',
fieldLabel: 'firstname'
},
{
xtype: 'button',
ariaLabel: 'open modal',// make this text dynamic
text: 'SignIn',
bind: {
text: '{firstname}'
}
}
],
});
You cannot bind aria-label or set it after rendering time.
Read ariaAttributes which says
Note that this config is only meaningful at the Component rendering time, and setting it after that will do nothing.
You can only re-set them by recreating the component.

How to know if the data in the viewmodel has been changed?

I have 3 field like this:
{
name: 'firstName',
fieldLabel: 'First name',
bind: '{person.firstName}',
xtype: 'textfield',
allowBlank: false,
listeners: {
blur : {
buffer : 1000,
fn : 'detectGender'
},
}
},
{
name: 'middleName',
fieldLabel: 'Middle name',
bind: '{person.midleName}',
xtype: 'textfield',
allowBlank: true,
listeners: {
blur : {
buffer : 1000,
fn : 'detectGender'
},
}
},
{
name: 'lastName',
fieldLabel: 'Last name',
bind: '{person.lastName}',
xtype: 'textfield',
allowBlank: true,
listeners: {
blur : {
buffer : 1000,
fn : 'detectGender'
},
}
},
And I have a function to determine the gender of the user:
detectGender: function () {
const vm = this.getViewModel()
const firstName = vm.get('person.firstName');
const lastName = vm.get('person.lastName');
const personName = `${firstName ? firstName : ' '}${middleName ? middleName : ' '}${lastName ? lastName : ' '}`
if (firstName) {
//ajax request to server with data: personName
// personName migth be SarahSmith, or Sarah, or SarahEllen, or SarahEllenSmith
}
},
It works, but my problem is that function 'detectGender' runs all three times if all these fields are filled in. And my server returns three responses. But i need run this only one time. Maybe there is some way to create an event for the viewmodel to listen when person name has been changed?
Or any ideas on how to send the user name from the three fields only once?
Hope i was clear enoght... Please any help
I think Arthur's comment is probably the simplest way to solve the issue.
However, here's a couple more options.
Opt1 - You could put each of the name fields in a "parent" and listen to the "blur" event on the parent. In the sencha fiddle example, look at the code "Opt1" which I've set up with a "fieldcontainer". Since a container doesn't have a "blur" event, the "focusleave" event is used. I think of my two options, this would be the best as it gives you a good indication the user is done entering the name.
// Opt 1: Detect "blur" on higher level element
// so you know user is done
xtype: 'fieldcontainer',
layout: 'hbox',
fieldLabel: 'Name',
listeners: {
// "Opt1: blur equivilent" for a container
focusleave: 'detectGender'
},
items: [{
name: 'firstName',
emptyText: 'First',
bind: '{person.firstName}',
xtype: 'textfield',
allowBlank: false
}, {
name: 'middleName',
emptyText: 'Middle',
bind: '{person.middleName}',
xtype: 'textfield',
allowBlank: true
}, {
name: 'lastName',
emptyText: 'Last',
bind: '{person.lastName}',
xtype: 'textfield',
allowBlank: true
}]
}
Opt2 - You brought up listening to the viewModel data. I've provided an example of this, but it's basically a "change" event, so you'd get an event each time someone typed a letter in the field, resulting in a similar problem you have now. I did an example for fun, with a throttled function to help delay, but I think you'd end up calling the server way too much as unlike the above, you don't know when someone is "done" entering the name.
This uses the bindings config on a ViewController, which is a convenience for manually calling viewModel.bind(), which is how you can "listen" to viewModel data.
Ext.app.ViewController - bindings config
Ext.app.ViewModel - bind method
Here's the fiddle, hopefully it's not too confusing having both examples in one.
Sencha Fiddle

extjs radiogroup before change event

Could you please help me before change/check the event on ExtJS radiogroup. I want some event should check before changing the radio button based on the condition. I need to allow user to change the select another radio button if condition says false, then we need to select the old radio button only.
Please refer the below code:
Ext.application({
name: 'Fiddle',
launch: function () {
Ext.create('Ext.form.Panel', {
title: 'RadioGroup Example',
width: 300,
height: 125,
bodyPadding: 10,
renderTo: Ext.getBody(),
items: [{
xtype: 'radiogroup',
fieldLabel: 'Gender',
columns: 1,
items: [{
boxLabel: 'Male',
name: 'rb',
inputValue: '1'
}, {
boxLabel: 'Female',
name: 'rb',
inputValue: '2',
}],
listeners: {
change: function(field, newValue, oldValue, eOpts) {
console.log(newValue);
alert(newValue.rb);
},
}
}]
});
}
});
https://fiddle.sencha.com/#view/editor&fiddle/32fb
As mentioned in the comments, there is no beforechange event that can be used to veto a change.
Some other options are:
you can the change listener to validate the radiogroup, making it invalid if an option is selected that's not right. This way the user is passively informed that there's a problem, and they have to fix it.
you can get the behaviour you described by using the oldvalue argument, and simply setting the radio control back to the old value:
change: function(field, newValue, oldValue, eOpts) {
if (<new value isn't acceptable>) {
// Probably should let the user know why
field.setValue(oldValue);
return;
}
... any other change logic here.
}
You can also use the change listener enable and disable options pre-emptively as the form state changes, thus preventing the user from making a bad choice in the first place.

Unable to layout composite field items

I am using Ext JS 3.4 and in the composite field, there are three fields, code is as below:
xtype: 'compositefield',
name: 'comboField',
fieldLabel: 'Partner with',
width: 400,
cItems:[{
xtype: 'combo',
name: 'partnerTypeCombo',
value: 'ProviderName',
mode: 'local',
store: new Ext.data.ArrayStore({
fields: ['id', 'displayValue'],
data: [
['ProviderName', 'Provider Partner Name'],
['OtherProvider', 'Other Provider Partner']
]
}),
valueField: 'id',
displayField: 'displayValue',
listeners: {
scope: this,
select: function(combo, record, index) {
var providerField = this.formPanel.getForm().findField('comboField_providerPartnerNameField');
var otherProviderField = this.formPanel.getForm().findField('comboField_otherProviderPartnerNameField');
if (combo.value == "OtherProvider") {
providerField.setVisible(false);
otherProviderField.setVisible(true);
}
else {
providerField.setVisible(true);
otherProviderField.setVisible(false);
}
}
}
}, {
xtype: 'spacer',
width: 10,
flex: 0
}, {
xtype: 'modellinkfield',
name: 'providerPartnerNameField',
modelLevelType: 'Organization',
modelType: 'Organization',
pickerReport: {
reportName: 'TMS.SupplierVendorOrgPicker',
targetLevelType: 'Organization'
}
}, {
xtype: 'textfield',
name: 'otherProviderPartnerNameField',
hidden: true
}]
By using the above code and without hiding any field, I got the below result
But My expectation is
By default third field (which is text field) should be hidden
On selecting Combobox values, the next two fields should be visible/hidden.
Like if dropdown field value is "Provider Partner Name" then only second
field (modeling field) should be visible (shown as below)
And if dropdown field value is "Other Provider Name" then only third
field (i.e text field) should be visible.
But I am unable to achieve this third objective. I am getting the following output for this (the field is getting overridden)
And I am expecting the following output.
Looks like this may be some layout issue or maybe I need to apply some CSS style to handle this. Can someone please help me to solve this issue.
Able to solve this issue by using below code :
otherProviderField.ownerCt.doLayout();

How to select all the TextFields on a FormPanel?

How would I select all the child components of a FormPanel that are of the component type TextField?
I want to loop through only the TextField components and set their values to "".
I have this inside a method on a FormPanel
this.query('textfield').forEach(function(item) { console.log(item.id); } );
It selects too much stuff, it selects all the nested TextFields inside of ComboBox and DateField and what not.
How can I get only the Ext.form.field.TextField instances?
You need call the function getXType().
Extjs documentation http://docs.sencha.com/extjs/4.2.1/#!/api/Ext.Component-method-getXType
Example
var t = new Ext.form.field.Text();
alert(t.getXType()); // alerts 'textfield'
Using this form of query causes the Ext.ComponentQuery to lookup if the current component either is a textfield or extend from textfield. Simply use a property query for such a case like [xtype=textfield]. Is doesn'T matter if you create the textfield without setting the xtype or creating it by it's xtype it be set on the instance.
Here's example will return two results.
var form = Ext.create('Ext.form.Panel', {
title: 'Contact Info',
width: 300,
bodyPadding: 10,
renderTo: Ext.getBody(),
items: [Ext.create('Ext.form.field.Text',{
name: 'name',
fieldLabel: 'Name',
allowBlank: false // requires a non-empty value
}), {
xtype: 'textfield',
name: 'email',
fieldLabel: 'Email Address',
vtype: 'email' // requires value to be a valid email address format
}]
});
console.log(Ext.ComponentQuery.query('[xtype=textfield]', form));

Resources