Composite components - extjs

I'd like to create a component which combines other existing components. A simplified example is below:
Ext.define('CCC.ThreeNames', {
extend: '???',
alias: 'widget.threenames',
items: [
{
xtype: 'textfield',
label: 'First',
itemId: 'first',
name: 'first'
},
{
xtype: 'textfield',
label: 'Middle',
itemId: 'middle',
name: 'middle'
},
{
xtype: 'textfield',
label: 'Last',
itemId: 'last',
name: 'last'
}
]
});
So let's say I want to use it:
items: [
{
xtype: 'threenames',
itemId: 'applicant',
name: 'applicant'
},
{
xtype: 'threenames',
itemId: 'dependent',
name: 'dependent'
}
]
First of all, what do I extend ??? from? I was thinking xtype: 'fieldset', but this type isn't considered a form field, so instead when you're pulling data via getValues, it skips the fieldset and iterates down to the text fields. The text fields don't "know" their context, because that's in the parent element, so they just report as first, middle and last, and now there are duplicate fields in the data set.
I was thinking during initialization, maybe the parent component munges the name and prefixes them with its own name, e.g. first, middle, last to applicant.first, applicant.middle, applicant.last, etc. but then that's going to muck up any code in the components that uses the name for whatever.
How to handle this? We'd really like to be able to reuse some complex composite components so that our designers don't have to hand code them all the time, and have the component work as an independent unit to save and retrieve data, so for example form.getValues() pulls out the data, without knowing anything about the composite component.
Would really appreciate a solution to this.
Regarding the possible duplicate suggested by Coderino Javarino: This question is not a duplicate of that one. That component is able to work using a single value for both fields, i.e. the date field interprets the value as a date, the time field interprets the value as a time. In effect it's really just a single component with a custom display type.

Naming fields requested way is a simple task:
Ext.define('CCC.ThreeNames', {
extend: 'Ext.container.Container', //you can use Ext.form.Panel for additional functionality
alias: 'widget.threenames',
items: [
{ xtype: 'textfield', itemId: 'first', name: 'first', fieldLabel: 'First' },
{ xtype: 'textfield', itemId: 'middle', name: 'middle', fieldLabel: 'middle' },
{ xtype: 'textfield', itemId: 'last', name: 'last', fieldLabel: 'last' },
],
listeners: {
beforerender: function() {
let self = this;
this.query('textfield').forEach(function(field){
field.name = self.name + '.' + field.name;
field.itemId = field.name;
});
}
}
});
using such naming is completely different thing. But as I understood from your comment that MVC pattern is not in use then this solutions should give expected result.

Here is the recommended way to achieve what you want
Ext.define('CCC.ThreeNames', {
extend: '???',
**xtype: 'threenames',**
alias: 'widget.threenames',
items: [
{
xtype: 'textfield',
label: 'First',
itemId: 'first',
name: 'first'
},
{
xtype: 'textfield',
label: 'Middle',
itemId: 'middle',
name: 'middle'
},
{
xtype: 'textfield',
label: 'Last',
itemId: 'last',
name: 'last'
}
]});
Now you can use it like this
items: [
{
xtype: 'threenames',
itemId: 'applicant',
name: 'applicant'
},
{
xtype: 'threenames',
itemId: 'dependent',
name: 'dependent'
}
]

Related

Prevent page reload on form field press enter in ExtJS

I have a problem with ExtJS modern toolkit's Form component.
I create a form with no buttons (you can test it in sencha fiddle). The only button is in the titlebar and it isn't working at the moment.
The problem is :
Form submission on pressing Enter. My form is empty, default method is POST, but my page is reloading when I press Enter while the focus is on any of my fields. Url address string in browser complemented form field names, but the form's method is POST.
Ext.create('Ext.form.Panel', {
renderTo: Ext.getBody(),
requires: [
'Ext.form.FieldSet',
'Ext.field.Text',
'Ext.field.TextArea',
'Ext.TitleBar'
],
items: [{
xtype: 'titlebar',
docked: 'top',
title: 'Searching',
items: [{
iconCls: 'fa fa-search',
iconAlign: 'right',
text: 'Search',
align: 'right',
handler: function() {
//
}
}]
}, {
xtype: 'fieldset',
border: false,
shadow: 'true',
defaults: {
value: ''
},
items: [{
xtype: 'numberfield',
label: 'Some ID',
allowBlank: true,
name: 'id'
},
{
xtype: 'textfield',
label: 'Some Article',
name: 'article'
}
]
}]
});
How to prevent page reloading?
I've never seen this behaviour in ExtJS form's by default.
Just replace id with something else could be Id in field name, it is conflicting with Ext id property
{
xtype: 'numberfield',
label: 'Some ID',
allowBlank: true,
name: 'Id'
},

How to make nested re-usable fieldsets and use updateRecord to retrieve input and setRecord to load entered data?

My app requires a lot of data-input from the user. And there's repetition, so for example the person fields must be filled in three times (all possibly different).
Person fieldset that I want to reuse
Ext.define('MyApp.form.PersonForm',
{
extend: 'Ext.form.FieldSet',
xtype: 'personform',
config:
{
items:
[
{
xtype: 'textfield',
name: 'surname',
label: 'Naam:',
placeHolder: ''
},
{
xtype: 'textfield',
name: 'adres',
label: 'Adres:',
placeHolder: 'Straat en huisnummer'
},
{
xtype: 'textfield',
name: 'zipCode',
label: 'Postcode:',
placeHolder: '1234AB'
},
{
xtype: 'textfield',
name: 'city',
label: 'Plaats:',
placeHolder: 'Of stad'
},
{
xtype: 'textfield',
name: 'country',
label: 'Land:',
placeHolder: ''
},
{
xtype: 'textfield',
name: 'phone',
label: 'Telefoon:',
placeHolder: ''
},
{
xtype: 'textfield',
name: 'email',
label: 'e-mail:',
placeHolder: ''
}
]
}
});
So I've made small fieldset and am trying to reuse them several times in the form. But when I try to load the saved data it concats all fields with the same name into an array of fields, so they don't get updated.
So instead of having one fieldset with others in it, each fieldset now only contains the fields directly in it. And the must all be placed in their own container so I can access them in the controller and update\load their respective data.
Ext.define('MyApp.form.UserDataForm',
{
extend: 'Ext.Container',
requires:
[
'Ext.form.FieldSet',
'Ext.form.Panel',
'Ext.field.Hidden',
'MyApp.form.PersonForm'
],
xtype: 'userdataform',
config:
{
items:
[
{
xtype: 'formpanel',
itemId: 'userDataForm',
flex: 1,
items:
{
xtype: 'fieldset',
items:
[
{
xtype: 'hiddenfield',
name: 'id'
},
{
xtype: 'textfield',
name: 'reportCode',
label: 'Meld code:',
itemId: 'txtReportCode'
},
{
xtype: 'textfield',
name: 'firstName',
label: 'Voornaam:',
itemId: 'txtFirstName'
}
]
}
},
{
xtype: 'personform',
name: 'person'
}
]
}
});
The problem is that the fieldset directly in the panel is invisible (unlike when all fieldsets where nested in each other). So this only shows the personform. However the first fieldset is shown when I force the height to a set value.
Code for loading saved data and saving input:
onLoadUserData: function ()
{
var userDataStore = Ext.getStore('userDataStore');
var userDataRecord = userDataStore.getFirst();
var userDataView = this.getUserDataView();
var userDataForm = this.getUserDataForm();
userDataForm.setRecord(userDataRecord);
userDataForm.down('personform[name=insured]').setRecord(userDataRecord.data.person);
},
onTapBtnSave: function ()
{
var userDataView = this.getUserDataView();
var userDataStore = Ext.getStore('userDataStore');
var isNew = userDataStore.getCount() == 0;
var userDataRecord = userDataStore.getFirst();
userDataView.updateRecord(userDataRecord);
userDataRecord.data.person = userDataRecord.data.person|| Ext.create('MyApp.model.PersonModel');
userDataView.updateRecord(userDataRecord.data.person);
if (isNew)
{
userDataStore.add(userDataRecord);
}
else
{
userDataRecord.setDirty();
}
userDataStore.sync();
}
I tried using layout: 'vbox' and layout: 'flex' and or not using the flex: 1 on the containing panel.
So how do I get Sencha to show those fields when they are there, without forcing a specific height?
Edit: the problem seems to lie in using a form.Panel instead of a container. While the container will simply take the space it needs the Panel won't. But I need it to be a panel to get the loading and saving of user input working.

How do I add a custom xtype to another view?

I have two views and I want one to be nested inside the other, like a partial view. My two views are as follows:
ChemicalRisksView.js
Ext.define('HandSurvey.view.ChemicalRisksView', {
extend: 'Ext.form.Panel',
xtype: 'chemicalrisksview',
requires: [
'Ext.form.FieldSet',
'Ext.field.Text',
'Ext.Button',
'HandSurvey.view.SpecificChemicalView'
],
config: {
items:[{
xtype: 'fieldset',
title: 'Fiberglass & Resins',
items: [
{
name: 'test',
xtype: 'specificchemicalview'
},
{
xtype: 'button',
text: 'Save',
action: 'saveChemicalRisks',
margin: '10 5',
ui: 'confirm'
}
]
}]
}
});
SpecificChemicalView.js
Ext.define('HandSurvey.view.SpecificChemicalView', {
extend: 'Ext.form.Panel',
xtype: 'specificchemicalview',
requires: [
'Ext.form.FieldSet',
'Ext.field.Toggle',
'Ext.field.Select',
'Ext.field.Text',
'Ext.Button'
],
config: {
items:[{
xtype: 'fieldset',
title: 'Edit Specific Chemical',
items: [
{
name: 'name',
xtype: 'textfield',
label: 'Name'
},
{
name: 'model',
xtype: 'textfield',
label: 'Model #'
},
{
name: 'manufacturer',
xtype: 'textfield',
label: 'Manufacturer'
},
{
name: 'averageExposure',
xtype: 'textfield',
label: 'Average Exposure Time'
},
{
name: 'msdsOnFile',
xtype: 'checkboxfield',
label: 'MSDS On File'
},
{
name: 'additionalInfo',
xtype: 'textfield',
label: 'Additional Info'
},
{
xtype: 'button',
text: 'Save Chemical',
action: 'saveChemical',
margin: '10 5',
ui: 'confirm'
}
]
}]
}
});
So I have defined the xtype as specificchemicalview and added it to the items in the 'parent' view. But, nothing happens. It just shows nothing in the ChemicalRisksView. I am debugging in Chrome and there are no error messages.
I am using this same method to add all my views to my main navigation view and that works fine. What am I missing here?
In HTML, form cannot contain another form. Although it could work in Ext as it does not use <form> tag, I do not think it is a good idea. Form is designed to contain form fields (isFormField:true) that another form is definitely not one.
I would consider a re-design where the "specific view" would extend FieldSet adding necessary form fields as its items.
That should solve the problem.

CheckBox Group disappears when I set an id on it

I'm trying to make a simple toolbar in ExtJS containing a checkbox group and a button and it works just fine but when I try to give an id to my check box group the whole toolbar doesn't display anymore.
Here is my code:
Ext.define
(
'CMS.view.TreeGrid.Filters',
{
extend: 'Ext.form.Panel',
title: 'Filters',
layout: 'fit',
alias: 'widget.filters',
bodyPadding: 10,
renderTo: Ext.getBody(),
vtype: 'hbox',
items :
[
{
xtype: 'toolbar',
vtype: 'vbox',
items:
[
{
xtype: 'checkboxgroup',
//id: 'propertiesCBG',
fieldLabel: 'Properties',
vertical: true,
layout: 'fit',
items:
[
{ boxLabel: 'id', name: 'rb', inputValue: '1' },
{ boxLabel: 'State', name: 'rb', inputValue: '2', checked: true },
{ boxLabel: 'headline', name: 'rb', inputValue: '3' },
{ boxLabel: 'severity', name: 'rb', inputValue: '4' },
{ boxLabel: 'country', name: 'rb', inputValue: '5' },
{ boxLabel: 'hasRelated', name: 'rb', inputValue: '6' }
]
},
{
xtype: 'button',
text: 'Request',
id: 'requestButton',
},
]
}
]
}
);
When the line "id: propertiesCGB" is commented i don't have any problem and the toolbar displays like I want it to display but I need an id for my checkbox group to get its values on my controller.
Please help me, I just can't understand what the problem is.
In general it is a bad practice to use the id property. If you must have some sort of id, use itemId, which will not have any type of global uniqueness constraint and also allow you to use # in your selector.
People generally use ids to make it easy to get a reference to a component, something like:
Ext.ComponentQuery.query('#myid');
What you should really be doing is:
Ext.ComponentQuery.query('filters checkboxgroup')
in order to get a reference to your checkboxgroup.
In your case, change the selector in your controller to be 'filters checkboxgroup' and you should be just fine. Note, if you have more than one of these in your application, add more specificity to your selector, for instance, if your filters component is a child of a particular panel:
Ext.ComponentQuery.query('panel1 filters checkboxgroup')
have you tried changing the id to something else temporarily to confirm you don't have a duplicate elsewhere in your project?
xtype: 'checkboxgroup',
id: 'propertiesCBG_temp',
fieldLabel: 'Properties',
I've seen similar behavior when having two components with the same id.
Finally, it appeared that I called my toolbar in two different views so I guess this created the conflict with the ID. I'll find another way to reuse my toolbar.

What is the correct way to use this.up?

I have a simple Form Panel and cannot seem to access the form by using the this.up command. What is the correct way to use this? All the console.log statements return undefined. I am using the Sencha Architect program and there cannot define an xtype. Instead, I had to use the alias field.
Ext.define('MyApp.view.MyFormPanel', {
extend: 'Ext.form.Panel',
alias: 'widget.contactform',
config: {
id: 'MyFormPanel',
items: [
{
xtype: 'fieldset',
title: 'MyFieldSet1',
items: [
{
xtype: 'textfield',
label: 'Name',
name: 'Name'
},
{
xtype: 'textfield',
label: 'Email',
name: 'Email'
}
]
},
{
xtype: 'button',
itemId: 'mybutton',
text: 'MyButton'
}
],
listeners: [
{
fn: 'onMybuttonTap',
event: 'tap',
delegate: '#mybutton'
}
]
},
onMybuttonTap: function(button, e, eOpts) {
console.log(this.up('widget.contactform'));
console.log(this.up('contactform'));
console.log(this.up('form'));
console.log(this.up('formpanel'));
console.log(this.up('form.panel'));
console.log(this.up('MyFormPanel'));
}
});
Hmm. Considering from where you are calling it, I think that this is the form panel itself.

Resources