How to create a dynamic (on the fly) form in Sencha Touch - javascript

I am working in a sencha touch ap and I need to generate views with dynamic forms.
Configuration comes from the backend (JSON) and depending of this I paint a component or other.. for example:
"auxiliaryGroups": [
{
"id": "1000",
"name": "Basic Data",
"fields": [
{
"id": "1001",
"name": "Company name",
"editable": true,
"mandatory": true,
"calculation": true,
"internalType": "T",
"type": "textfield",
"options": []
}
]
}
],
Where type is related xtype in Sencha.
In the view I am creating all options of fields:
{
xtype : 'fieldset',
itemId: 'auxiliaryFieldsGroup3',
title: 'Requirements',
//cls : 'headerField',
items:[
{
xtype: 'togglefield',
name: 'f6',
label: 'Auxiliary field R1'
},
{
xtype: 'selectfield',
name: 'f10',
label: 'Auxiliary field R5',
placeHolder: 'Select one..',
options: [
{text: 'First Option', value: 'first'},
{text: 'Second Option', value: 'second'},
{text: 'Third Option', value: 'third'}
]
},
{
xtype: 'textfield'
}
]
}
And I receive the configuration from the server, I understand the view like a store of different fields, and from the controller, it should add only fields specified in the json object.
How to create this dynamically following MVC concept?
Thank you

I assume that you have all the fields in the auxiliaryGroups.fields array.
After loading the form data from AJAX you can do it like this in your success call.
succses:function(response){
var response = Ext.decode(response.responseText);
var fields = response.auxiliaryGroups.fields;
var form = Ext.create("Ext.form.Panel",{
// here your form configuration.
cls:"xyz"
});
Ext.Array.forEach(fields,function(field){
var formField = {
xtype: field.type,
label: field.name,
name: field.id
};
form.add(formField);
});
Ext.Viewport.add(form);
form.show();
}

Based on my comment on your question, my idea about dynamic form generation is using ExtJS syntax to load form definition would be something like the code below; of course it would be extended and the data can be loaded from the server.
Ext.require([
'Ext.form.*'
]);
Ext.onReady(function() {
var data = [{
fieldLabel: 'First Name',
name: 'first',
allowBlank: false
}, {
fieldLabel: 'Last Name',
name: 'last'
}, {
fieldLabel: 'Company',
name: 'company'
}, {
fieldLabel: 'Email',
name: 'email',
vtype: 'email'
}, {
xtype: 'timefield',
fieldLabel: 'Time',
name: 'time',
minValue: '8:00am',
maxValue: '6:00pm'
}];
var form = Ext.create('Ext.form.Panel', {
frame: true,
title: 'Simple Form',
bodyStyle: 'padding:5px 5px 0',
width: 350,
fieldDefaults: {
msgTarget: 'side',
labelWidth: 75
},
defaultType: 'textfield',
defaults: {
anchor: '100%'
}
});
form.add(data);
form.render(document.body);
});
update:
When you create a view in run-time, communication between view and controller might be problematic because the controller doesn't know anything about the view. considering that the controller is responsible for initializing the view with appropriate data and handling view's events, data binding mechanism in ExtJS can solve the first part (initializing the view) which is described at View Models and Data Binding, the second part (handling view's events) can be solved with general methods in the controller, every element in the view should send its event to some specific methods and the methods based on the parameters should decide what to do.
This approach makes your methods in the controller a little fat and complicated that is the price of having dynamic form.
It is worth to mention that EXTJS raises different events in the life cycle of controllers and views and you should take benefit of them to manage your dynamic form.

I don't know about Touch, but in ExtJS, I usually do it by using the add function of a fieldset. You just loop through all your records, and create the fields on the fly.
var field = Ext.create('Ext.form.field.Text'); //or Ext.form.field.Date or a number field, whatever your back end returns.
Then set correct config options on field then
formFieldset.add(field);

Related

Sencha: How to use data from one file to another

Im new at this sencha thingy and im trying to experiment a bit with it. I've been making some very simple tests and now i've reached the point where i want to pass the data from one file to another. To make it easier to understand im trying to get the data from the following text box to make a simple filter tool.
the textfield has been created in the following piece of code in the file Filters.js
Ext.define('Prj01Filtro.view.main.Filters', {
extend:'Ext.form.Panel',
xtype: 'Filters',
alias: 'form.formFilter',
requires: [
'Prj01Filtros.view.main.FilterController'
],
controller: 'controllerFilter',
items:[
{
margin: '0 0 10 0',
buttons: [
{
xtype: 'textfield',
fieldLabel: 'Filter',
name: 'textFieldSearch'
}, {
name: 'buttonSearch',
text: 'Buscar',
listeners: {
click: 'onClickFilter'
}
}, {
name: 'buttonRemoveFilter',
text: 'X',
listeners: {
click: 'onClickRemove'
}
}
]
}
]
The code of the buttons have been located in a file named FilterController.js
Ext.define('Prj01Filtros.view.main.FilterController',{
extend: 'Ext.app.ViewController',
alias: 'controller.controllerFilter',
onClickFilter: function() {
//Code to apply the filter
},
onClickRemove: function() {
//code to remove the filter
}
})
Finally the code of the table is located in a file named List.js
Ext.define('Prj01Filtros.view.main.List', {
extend: 'Ext.grid.Panel',
xtype: 'mainlist',
plugins: 'gridfilters',
requires: [
'Prj01Filtros.store.Personnel'
],
title: 'Personnel',
store: {
type: 'personnel'
},
columns: [
{ text: 'Name', dataIndex: 'name', align: 'left', filter: {
type: 'string'
}},
{ text: 'Email', dataIndex: 'email', flex: 1, align: 'left', filter: {
type: 'string'
}},
{ text: 'Phone', dataIndex: 'phone', flex: 1, align: 'left', filter: {
type: 'string'
}},
],
listeners: {
select: 'onItemSelected'
},
});
So my goal is to make a function in FilterController.js that can be called from Filters.js which sets the value for the filters applied in the columns of List.js but im kind of stuck on how to set the value property for the filter from the function that i have created. If someone knows how to do it i would appreciate the help. Thanks!
I've tried many things but since im new to sencha im pretty much sure that they were incorrect anyways.
I suggest to study View Models & Data Binding and ViewModel Internals sections of Ext JS documentation. These are among the most powerful features of Ext JS so it good to develop a deep understanding.
For you current question, you need to access the store behind your List view to manage the filters, not the list itself. Once you get the store, you can set / clear the filters with setFilters and clearFilter methods on the store:
store.setFilter([{
property: 'email',
value: '<search term here>',
operator: 'like'
}]);
store.clearFilter();
To easily access the store, define the store in the view model of a view that is above both List and Filters view. This is the important part because this way you can take advantage of inheriting the view model from the container parent. Let's say you have a panel which contains both List and Filters. Define the store in the view model:
stores: {
personnel: {
type: 'personnel'
}
}
Use bind config when assigning the store in your List:
Ext.define('Prj01Filtros.view.main.List', {
...
bind: {
store: '{personnel}'
}
...
});
After all this, you can access the store from the Filters view controller's methods:
onClickFilter: function() {
this.getViewModel().getStore('personnel').setFilters(...);
}
You need the get the value the user entered into the field. You can access it from the controller with querying the textfield component, but you can also do it with the view model.

Change Extjs Grid Validation Text

I'm looking at an older project that is using ExtJS. Admittedly, I'm not too familiar with this framework so I learn as I go and as I am asked to fix things. My issue is that there is a grid and each column uses allowBlank: false. This causes a tooltip box to display with some info about what field(s) need to be populated. This all works great, but I am trying to change that text in that tooltip i.e. "Error", "Benefit Category Name: This field is required", and I can't seem to make this work. How can I target and change that text? I'll include a screenshot of the grid and tooptip that I am talking about for reference. I am also using ExtJS version 6.
Here is a sample of one of the grid columns:
columns: [
{
dataIndex: 'benefit_category_name',
text: 'Benefit Category Name',
flex: 1,
editor: {
xtype: 'combo',
store: new Ext.data.ArrayStore({
fields: [
'benefit_category_id',
'benefit_category_name',
],
data: [],
}),
queryMode: 'local',
valueField: 'benefit_category_name',
displayField: 'benefit_category_name',
emptyText: 'Select one',
listeners: {
select: 'handleComboChange',
},
itemId: 'benefit_category_id',
allowBlank: false,
listeners: {
renderer: function(value, metaData) {
metaData.tdAttr = Ext.String.format('data-qtip="{0}"', value);
return value;
}
}
}
},
{
...
}
]
I tried to use the metaData property I read about in some other posts, but I can't seem to change the tooptip text (Error, Benefit Category Name: This field is required).
Here is a screenshot of the tooltip mentioned. I know this is an older framework, but any tips I could get would be useful. Thanks for your time.
Thats a simple validation example with custom message.
Ext.create({
xtype: 'textfield',
allowBlank:false, // will show required message - remove key to display vtypeText only
validateBlank: true,
vtype: 'alpha', // check for available types or create / extend one
vtypeText: 'Please change to what you want ...',
renderTo: Ext.getBody()
})
Details:
https://docs.sencha.com/extjs/6.5.3/classic/Ext.form.field.VTypes.html
https://fiddle.sencha.com/fiddle/3770

Problems with references and stores in Sencha ExtJS

I’m new here in at Stackoverflow and to Sencha ExtJS development. I’m a student from Germany and I’m current trying to get my degree in media computer sciences. As a part of my final assignment I’m currently developing the UI of a webapp for a local company.
While I was trying out the capabilities of the Sencha ExtJS framework I came across some problems, which is why I’m now reaching out to the community for help ;)
My first problem I had, was when I was playing around with the syntax for instancing classes using xtypes and the association of Stores inside the ViewModel:
For the purpose of easier to read and less cluttered code I wanted to give my stores their own xtype so I could instead of declaring all the stores and their configs inside the ViewModels’ stores config wanted to have every store inside their own file and then just create an instance of them later inside the ViewModel. The code I wrote for this looks like this:
ViewModel:
Ext.define('Example.viewmodel.MyViewModel', {
extend: 'Ext.app.ViewModel',
alias: 'viewmodel.myviewmodel',
requires: [
'Example.store.MyStore',
],
stores: {
StoreA: { xtype: 'store_a' },
StoreB: { xtype: 'store_b' },
StoreC: { xtype: 'store_c' }
},
data: {
/* This object holds the arbitrary data that populates the ViewModel and
is then available for binding. */
}
});
StoreA:
Ext.define('Example.store.StoreA', {
extend: 'Ext.data.Store',
xtype: 'store_a',
requires: [
'Example.model.ModelA'
],
storeId: 'StoreA',
autoLoad: false,
model: 'Example.model.ModelA',
listeners: {
load: 'onLoadofStoreA'
}
});
But apparently this isn’t working… My load listener of the store does not seem to fire at the method inside my controller and does not even seem to know about the view that is associated with the ViewModel. What am I doing wrong or is this just not meant to be done like that?
My Second Problem was when I was playing around with some of the UI components. My scenario was like this:
I wanted to have a menu that would slide in, where u could do some inputs that would then load the content for the view.
I found this example for a sliding menu (https://examples.sencha.com/extjs/6.7.0/examples/kitchensink/?modern#menus) and built this:
Inside my ViewController:
getMenuCfg: function (side) {
var cfg = {
side: side,
controller: example_controller',
id: 'topMenu',
items: [
{
xtype: 'container',
layout: 'hbox',
width: '100%',
items: [
{
xtype: 'fieldset',
reference: 'fldSet',
id: 'fldSet',
layout: 'vbox',
width: '50%',
defaults: {
labelTextAlign: 'left'
},
items: [
{
autoSelect: false,
xtype: 'selectfield',
label: 'Selectfield',
reference: 'sfExample',
id: 'sfExample',
listeners: {
change: 'onSFChange'
}
},
{
xtype: 'container',
layout: {
type: 'hbox',
align: 'end',
pack: 'center'
},
items: [{
xtype: 'textfield',
reference: 'ressource',
id: 'ressource',
flex: 1,
textAlign: 'left',
margin: '0 10 0 0',
label: 'Ressource',
labelAlign: 'top',
labelTextAlign: 'left',
editable: false,
readOnly: true
},
{
xtype: 'button',
shadow: 'true',
ui: 'action round',
height: '50%',
iconCls: 'x-fa fa-arrow-right',
handler: 'openDialog'
}
]
},
{
xtype: 'textfield',
reference: 'tfExample',
id: 'tfExample',
label: 'Textfield',
editable: false,
readOnly: true
}
]
},
}]
}];
The problem I come across now is, that I would no longer be able to easily get the references of components inside the menu (input fields) with this.lookupReference() as if they were just part of the view. In fact to find a workaround I had to trace a way back to the components using a debugger.
For example if another method inside my controller wanted to use a field inside this menu, instead of simply just doing this.lookupReference(‘sfExample’) I now had to do something like this:
var me = this,
vm = me.getViewModel(),
menuItems = me.topMenu.getActiveItem().getItems(),
fieldset = menuItems.getByKey('fldSet'),
selectfieldRessArt = fieldsetLeft.getItems().getByKey('sfExample');
I’m pretty sure that I am missing out on something important here and there has to be a way to do this easier. I’m really looking forward to your answers, thank you in advance ;)
use xtype only for components. if you need to define an type/alias for store, use alias config property instead and especify the alias category "store.".
Defining a store with an alias
Ext.define('Example.store.StoreA', {
extend: 'Ext.data.Store',
//use store. to category as a store
alias: 'store.store_a',
requires: [
'Example.model.ModelA'
],
storeId: 'StoreA',
autoLoad: false,
model: 'Example.model.ModelA',
listeners: {
load: 'onLoadofStoreA'
}
});
Instantianting your store by type
Ext.define('Example.viewmodel.MyViewModel', {
extend: 'Ext.app.ViewModel',
alias: 'viewmodel.myviewmodel',
requires: [
'Example.store.MyStore',
],
stores: {
StoreA: { type: 'store_a' },
StoreB: { type: 'store_b' },
StoreC: { type: 'store_c' }
},
data: {
/* This object holds the arbitrary data that populates the ViewModel and
is then available for binding. */
}
});
I Hope it can help you

JS console error shows Extjs trying to make a GET request for a class as if it was a file

Sorry if the title isn't the best.
I'm roughing out a test project in Extjs 6. I have a viewmodel class that uses a store called customers:
Ext.define('ChildSessionBinding.view.main.ChildSessionModel',{
extend: 'Ext.app.ViewModel',
alias: 'viewmodel.binding.childsession',
requires:[
'Ext.data.Store',
'ChildSessionBinding.model.Customer'
],
stores:{
customers:{
model: 'ChildSessionBinding.model.Customer',
autoLoad: true,
session: true
}
}
});
The model it requires has hard coded test data in it:
Ext.define('ChildSessionBinding.model.Customer', {
extend: 'Ext.data.Model',
fields: [
{ name: 'name', type: 'auto' },
{ name: 'phone', type: 'auto' }
],
data:[
{name: 'test', phone: '12345'}
]
});
And the view that uses the ViewModel is just a panel that shows a simple grid:
Ext.define('ChildSessionBinding.view.main.ChildSession', {
extend: 'Ext.panel.Panel',
xtype: 'binding-child-session',
title: 'Binding Child Session Demo',
layout: {
type: 'vbox',
align: 'stretch'
},
viewModel: {
type: 'binding.childsession'
},
controller: 'binding.childsession',
session: true,
items:[
{
flex: 1,
xtype: 'grid',
bind: '{customers}',
columns:[
{
dataIndex: 'name',
flex: 1,
text: 'Name'
},
{
dataIndex: 'phone',
flex: 1,
text: 'Phone'
},
{
xtype: 'widgetcolumn',
width: 90,
widget: {
xtype: 'button',
text: 'Edit',
handler: 'onEditCustomerClick'
}
}
]
}
]
});
When I load this in the browser, the grid does not load. I popped open the javascript console and saw that it was trying to make a get request to the server using the model's fully qualified name:
I've compared it to the kitchen sink example I'm trying to duplicate as well as other viewmodel stores that I've created in other projects and I don't see anything that would cause this.
Also, to rule out any project file structure questions, here's the folder/file structure:
EDIT
Here's the stack trace from the javascript console:
Anyone see the problem?
Put the data in your store, not in your model. Model doesn't have a data property.
I haven't used much of Ext 5 or 6 but I think I have seen this error on Ext 4. If a required class is undefined during the time of a class definition Ext will try to load a file with that name at the root of your application. I think that Ext should be throwing an error here by default instead of trying to load a file since I assume that most applications are not set up to handle this.
The fix would be to have ChildSessionBinding.model.Customer defined and executed before you load ChildSessionBinding.view.main.ChildSessionModel or have your application be able to load that file where Ext expects it to be.

sending textfield data to servlet in ExtJS

I want send a text field data to a servlet page. I dont know the process. I give the code for textbox and a button below.
Ext.onReady(function(){
var movie_form = new Ext.FormPanel({
renderTo: document.body,
frame: true,
title: 'Personal Information Form',
width: 250,
items: [{
xtype: 'textfield',
fieldLabel: 'Firstname',
name: 'firstname',
allowBlank: false
},{
xtype:'button',
text:'save'
}]
});
});
This is the code ofr a textbox and a button. when i click the button the data of the field will go to a servlet page. But I cant do that. Please any one help me. the name of the servlet page is url_servlet
in your service method (get or post )
you can get your data field values by the attribute "name".
String firstname = reg.getParameter("firstname");
For handling the data in the backend check #The Suresh Atta's answer.
I found some errata in your code:
1) Construct an Ext component with the Ext.create function.
2) Use Ext.getBody() to get the document body.
3) Put your fields in the items config and buttons in the buttons config (Not always the best practice).
Ext.create('Ext.form.Panel', {
frame: true,
title: 'Personal Information Form',
width: 250,
url: 'url_servlet',// The form will submit an AJAX request to this URL when submitted
items: [{
xtype: 'textfield',
fieldLabel: 'Firstname',
name: 'firstname',
allowBlank: false
}],
buttons: [{
text: 'Save',
handler: function() {
var form = this.up('form').getForm();
if (form.isValid()) {
form.submit({
success: function(form, action) {
Console.log('Success', action.result.msg);
},
failure: function(form, action) {
Console.log('Failed', action.result.msg);
}
});
}
}
}],
renderTo: Ext.getBody()
});
I hope this helps you a bit ;)

Categories

Resources