ExtJs : Why numberfield automatically converts null to 0 while displaying data - extjs

I have an Extjs form wherein we can assign age for a person. So when I assign blank in Age field and save it to DB that works good (in DB age goes as blank). But when I try to refresh and display it again then it displays 0.
I tried with allowBlank:true but it doesn't work.
This also doesn't work :
https://www.sencha.com/forum/showthread.php?181270-Hi-how-can-i-set-null-for-a-numberfield
Form :
Ext.define('com.view.xxxx.entities.forms.yyyyyyy', {
statics: {
buildItems: function ()
{
return [
{
xtype: 'fieldset',
title: 'Person',
defaultType: 'textfield',
defaults: { anchor: '100%'},
items: [
{fieldLabel: 'Age', name: 'age', xtype: 'numberfield', labelWidth: 130}
]
}
]
}
}
});
How can we force number field to display data from DB as is i.e. blank as blank?

My suggestion is to use a Model that converts any non-numeric value to a empty string and use this model in your form:
Ext.define('Fiddle.model.ageModel', {
extend: 'Ext.data.Model',
requires: [
'Ext.data.field.Field'
],
fields: [{
name: 'name'
}, {
name: 'age',
convert: function (v, rec) {
return typeof (v) === 'number' ? v : '';
},
}]
});
See this exemple

I prefer to use Number field in model with allowNull variable.
And allowBlank must be true in numberfield.
Ex.
Ext.define('Person', {
extend: 'Ext.data.Model',
fields: [
{ name: 'age', type: 'number', allowNull: true }
]
});

I was running into this, and changing my code to use the allowNull option seemed to fix my issue (for Ext-js 6). What isn't documented well is that when you define a field as type 'null', without the convert function or the allowNull option set to true, the store treats the value as '0' if it is actually null since 'null' is not a valid 'number'. It took me a while to figure that out on my own.
If running an older version though and allowNull doesn't work, you may be able to resolve it by additionally specifying a convert function that checks for null and returns null. Yes, it's a kludge, but it seemed to work for me before I tried using allowNull. Also works for v6 if you don't specify allowNull at all, like this:
{ name: 'age', type: 'number', convert:
function (v, rec) {
return (v === null ? null : v);
}
}
I had originally changed my app to use the convert function and had it working, but switched to using allowNull after finding this post since that's cleaner/clearer, but the convert function option was one I found on an old Sencha forum post.

Related

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();

Binding Grid and Form ExtJs 6

I have a grid populating records from view model (Ajax proxy data from Java restful web services). When I select a record in the grid form open and the fields are displayed. The records are editable using the form.
I have one tag field which is binded to a store and I should be able to populate the data associated to the record whenever the user selects a record.
"data" : [ {
"createdOn" : 1475678859000,
"updatedOn" : 1475679885000,
"updatedBy" : null,
"id" : 174,
"userName" : "ffff,
"firstName" : "gg",
"lastName" : "ggg",
"salesDepartment" : [ {
"id" : 3,
"code" : "FC",
"name" : "Fiat-Chrysler",
"departmentHead" : "xxx",
"applicationCode" : null} ],}
I need to bind the value of id from sales department object . how can I achieve this. Please help me.
{xtype: 'tagfield',
anchor: '100%',
reference: 'salesDept',
fieldLabel: 'Sales Dept\'s',
name: 'salesDepartment',
allowBlank: false,
displayField: 'name',
valueField: 'id',
bind: {
value: '{record.salesDepartmentIds}',
store: '{SalesDepartmentStore}'},
}
Tag fields are really only designed to work with arrays or comma separated lists;
It looks like you are trying to use with associations, which it would be nice if there was more support for this out of the box.
I've had a go, and in terms of displaying got it working however saving values will really depend on how you plan to sync values to the server, if you are going to use in built proxies etc. then this will present a whole different challenge. I would like a similar component for my own apps, I will think on this further and may even create a UX for this.
I think you have several issues here, one is getting your associations working properly, then you need to make your tagfield work as expected.
Here is the fiddle
And the key parts are:
An extended tagfield:
Ext.define('RelatedTagField', {
extend: 'Ext.form.field.Tag',
xtype: 'rtagfield',
config: {
relatedStore: null
},
setValue: function (val) {
this.setRelatedStore(val);
var value;
if (val && val.isStore) {
value = val.collect('id');
} else {
value = '';
}
this.callParent([value]);
},
getValue: function () {
return this.relatedStore;
},
onBindStore: function () {
this.callParent(arguments);
this.on('select', function (tagfield, selection) {
var selectedIds = [];
//add any new selections
Ext.each(selection, function (rec) {
var rrec = this.relatedStore.getById(rec.id);
if (!rrec) {
this.relatedStore.add(rec.data)
}
selectedIds.push(rec.id);
}, this);
//remove any not selected anymore
this.relatedStore.each(function (rrec) {
if (selectedIds.indexOf(rrec.id) == -1) {
this.relatedStore.remove(rrec);
}
}, this);
}, this);
},
})
Binding the value:
{
xtype: 'rtagfield',
fieldLabel: 'Sales Department',
name: 'id',
displayField: 'name',
valueField: 'id',
bind: {
store: '{salesDepartment}',
value: '{record.salesDepartment}'
}
},
And adding a definition for the association:
Ext.define('MyApp.model.User', {
extend: 'Ext.data.Model',
alias: 'model.user',
hasMany: [{
model: 'MyApp.model.SalesDepartmentModel',
associationKey: 'salesDepartment',
role: 'salesDepartment'
}],
requires: [
'Ext.data.field.String'
],
fields: [{
type: 'string',
name: 'userName'
}, {
type: 'string',
name: 'firstName'
}, {
type: 'string',
name: 'lastName'
}, {
name: 'salesDepartment'
}, {
name: 'roles'
}, {
type: 'string',
name: 'email'
}, {
name: 'notificationFrequencyId'
}]
});
Further reading
I suggest you read more on associations and the tagfield source code
In terms of saving records back to the server, I would recommend looking at sessions

Passing information to Ext.data.Model in Extjs

I'm trying to edit an open source program (and learn Extjs meanwhile) and I encountered a problem about models. I don't want to put code here since it is too long but I can provide if necessary.
So I got a a class which extends Ext.form.Panel and model "PartModel" assigned to it. This model has a string field called "partNumber" along with many other fields.
In this panel I want to choose a part number from a combobox from predefined values at database and assign it to "partNumber".
The problem is I want to assign value that is "displayed" in the combobox.(Not one of store fields, I'm using a custom XTemplate for it)
How can I do it?
Edit: Adding combobox code. I thought adding "dataIndex: 'partNumber'" would be sufficient to do job but this code isn't working at all. I can see Part Numbers strings from combobox but when I choose one and hit save button it doesn't save. (There are many other fields working well with that save button maybe I just need to add another button to save part number?)
{
xtype: 'combobox',
dataIndex: 'partNumber',
fieldLabel: "Part Number",
labelWidth: 150,
flex: 1,
store:{
xtype: 'store',
autoLoad: true,
model: 'PartGroupsClasses',
proxy: getPartGC()},
queryMode: 'local',
renderTo:Ext.getBody(),
tpl:Ext.create('Ext.XTemplate','<tpl for="."><div class="x-boundlist-item">{code}-{descr}-{ccode}-{cdescr}</div></tpl>'),
displayTpl:Ext.create('Ext.XTemplate','<tpl for=".">{code}{descr}{ccode}{cdescr}</tpl>')
}
Edit2: Figured out save button is basically calling following function.
Ext.override(Ext.data.Model, {
setDataWithAssociations: function(data) {
for (var i in data) {
if (this.fields.containsKey(i)) {
this.set(i, data[i]);
}
if (this.associations.containsKey(i)) {
var store = this[i]();
store.add(data[i]);
}
}
}
});
I would do something like this... in your combobox's model, add an extra field that makes use of the convert function to create your displayValue, and then in your combobox, just use that value for your displayValue property.
Model
Ext.define('MyComboModel', {
extend: 'Ext.data.Model',
fields: [
{
name: 'code',
type: 'string'
},
{
name: 'desc',
type: 'string'
},
{
name: 'ccode',
type: 'string'
},
{
name: 'cdesc',
type: 'string'
},
{
name: 'displayValue',
type: 'string',
convert: function(value, record) {
return record.get('code') +
record.get('desc') +
record.get('ccode') +
record.get('cdesc');
}
}
]
});
Combo
xtype: 'combobox',
name: 'Field2',
valueField: 'displayValue',
displayField: 'displayValue',
fieldLabel: 'Field2',
queryMode: 'local',
Full example.
I dont thing your question is clear enough for a clear answer...
I am unclear on your objective but if you want to have something display on store and behind it have the value on the file please take a look see if this examples helps
Store
this.data = Ext.create('Ext.data.JsonStore', {fields: ['id', 'data'],
data: [{id: 1, data: 'data1'},
{id: 2, data: 'data2'},
{id: 3, data: 'data3'},
{id: 4, data: 'data4'}]});
Combo
xtype:'Combobox',
name:'wtv',
displayField: 'data',
valueField: 'id'
It will display the combo with data but if you get the combo with the selection path and do for example
Selector
refs: [{
ref:Combo
selector:'Panel Combobox[name=wtv]'
}]
Later you can do something like
Panel.getCombo().getValue() and it will not
give you back the displayed field (Data) but it will give the id.
Sorry for bad formating! Hope it helps

Store dont load in renderer grid cell

I´m trying to renderer a string in my column instead of your id. I founded this solution: Grid cell is rendering wrong, but it´s don´t work with a dynamic store.
In my grid I put a store to load into the renderer:
columns: [
{xtype : 'gridcolumn', text: 'Id', dataIndex: 'id'},
{xtype : 'gridcolumn', text: 'Validade', dataIndex: 'dataValidade', renderer: function(object){return Ext.Date.format(object, 'd-m-Y');}},
{xtype : 'gridcolumn', text: 'Complexo', dataIndex: 'id', renderer: function(value) {
storeComplexoPorValidade.getProxy().url = caminhoContexto + "validade/complexoporid/" + value + "/dados.json";
storeComplexoPorValidade.load();
console.log(storeComplexoPorValidade);
var idx = storeComplexoPorValidade.find('id', value);
var rec = storeComplexoPorValidade.getAt(idx);
return rec.get('descricao');
}
},
But the store don´t load the data to var, although perform the request. And I catch the error: Cannot call method 'get' of undefined.
What´s wrong?
You're getting the error because the store load is asynch, so the "return" from the renderer will complete before the response of your proxy request is received, processed, and made available to even be assigned to the variable you're trying to set.
I would suggest that you rethink your approach here. A couple options:
Load the store you're trying to use for the custom rendering prior to the creation of the grid
Expand your underlying data model for the grid's store with the extra fields that you need to perform the custom rendering. You can always mark these extra fields as non-persistent, so they will not be included in any write actions that might be performed on the model instance.
Thanks for your help, I have a lot work to make my script work. And with your tips I founded two others solutions that help me to put my code working. How to wait until all stores are loaded in ExtJs? and http://www.sencha.com/forum/showthread.php?197265-Loading-stores-synchronously
It´s my code working:
Ext.define('validade', {
extend: 'Ext.data.Model',
fields: [{name: 'id', type: 'int'},
{name: 'dataValidade', type: 'date', dateFormat:'Y-m-d'},
{name: 'complexo', type: 'string', persist: 'false' }]
});
Ext.define('ValidadeStore',{
extend: 'Ext.data.Store',
model: 'validade',
pageSize: itemsPerPage,
remoteSort: true,
sorters: [{
property : 'id',
direction: 'DESC'
}],
proxy: {
type: 'ajax',
url : '/validade/grid/dados.json',
reader : {
type : 'json',
root : 'data'
},
},
autoLoad: {
callback: function(records, operation, success){
var i = 0
loadComplexo();
function loadComplexo(){
if (i < records.length) {
var rec = records[i];
i++;
storeComplexo.getProxy().url = "validade/complexoporid/" + rec.get('id') + "/dados.json";
storeComplexo.load({
callback: function(records, operation, success){
var rec_ = records[0];
rec.set('complexo',rec_.get('descricao'));
loadComplexo();
}
});
}
}
}
}
});
Thanks a lot!

ExtJS 4.1.3 filter combobox

This is a 2-part question:
Primary Question: Say I have a combobox (code below) that is pulling from a store. Say I have an array that I want to filter the data by so that only certain values actually show up in the dropdown. I can't seem to find the parameter that will do that... Is there a simple way to do this?
Secondary question: I also need to be able to disable and reenable items within the drop down based on actions of the user after the dropdown is created. Is there a function that causes items to be reenabled/disabled within the dropdown?
Note: By disabled I mean 'not present' aka removed in the dom, but still present in the non-filtered store.
{
xtype: 'combobox',
anchor: '100%',
name: 'Permission_id',
fieldLabel: 'Permissions',
hideLabel: false,
displayField: 'Name',
forceSelection: true,
store: 'PermissionStore',
typeAhead: true,
valueField: 'id',
valueNotFoundText: 'Add Permission'
}
I think all you need is work with filters. If you want to display only certain values in the dropdown, the only thing you have to do is filter the store and if what yo have is an array, you can use an ArrayStore for that.
About your second question, if you filter your store, original values are not lost, they are save internally as an snapshot then when you clear your filters, old values are displayed again in the dropdonlist.
Please take a look at this working example: http://jsfiddle.net/lontivero/Mz6S4/1/
First answer: the method is .filter(). For example:
var store = Ext.create('Ext.data.ArrayStore', {
fields: ['Id', 'Name'],
data: [
[ 1, 'Lucas' ],
[ 2, 'Pablo' ],
[ 3, 'Francisco' ]
]
});
Ext.create('Ext.form.field.ComboBox', {
fieldLabel: 'Permissions',
displayField: 'Name',
store: store,
valueField: 'Id',
renderTo: Ext.getBody()
});
//Hide Pablo in the dropdownlist
store.filter([
{filterFn: function(record) { return record.get('Name') !== 'Pablo'; }}
]);
// uncomment this to reenable hidden records (to display Pablo again)
//store.clearFilter();
Second answer: clear the store's filters
As lontivero said, filters solve your issue:
primary: array can contain data but filter will hide it from the dropdown
secondary: filters can be changed to hide and show in the dropdown
Your remaining problem, then, is how to change the filter from non-Ext code. Here you can use the fact that Ext is just javascript and can be called from any other javascript that has nothing to do with Ext.
So:
Add some functions that apply the filters to add and remove, in a place/scope that is accessible to the HTML DOM
Add them to the onclick handlers of the (plain HTML) buttons
The trick is really to access the store by using the Ext lookup by id.
So if the following code (extending on lontivero's jsfiddle) is directly in a js file (or in a script tag), it does what you're asking for:
(jsfiddle: http://jsfiddle.net/mCv6A/)
// functions that do the filtering
var noPablo = function(record) { return record.get('Name') !== 'Pablo' }
var noJames = function(record) { return record.get('Name') !== 'James' }
// the combination of functions we'll use
var withoutJamesFilter = [{ filterFn: noPablo }, { filterFn: noJames }]
var withJamesFilter = [{ filterFn: noPablo }]
function addJames()
{
var store = Ext.getStore('people')
store.clearFilter()
store.filter(withJamesFilter)
}
function delJames()
{
var store = Ext.getStore('people')
store.clearFilter()
store.filter(withoutJamesFilter)
}
Ext.onReady(function()
{
var store = Ext.create('Ext.data.ArrayStore', {
id: 'people',
fields: ['Id', 'Name'],
data: [
[ 1, 'Lucas' ],
[ 2, 'Pablo' ],
[ 3, 'Francisco' ],
[ 4 , 'James' ]
]
})
Ext.create('Ext.form.field.ComboBox', {
fieldLabel: 'Permissions',
displayField: 'Name',
store: store,
valueField: 'Id',
renderTo: Ext.getBody()
})
// start without james in list
store.filter(withoutJamesFilter)
})
Things to consider when using it for real (rather than in a simplified example):
wrapping addJames, delJames and the variables (noPablo, noJames etc) they use in an object or immediate function so the variables don't clutter the global (window) scope
rewriting the filter variables to better share implementation (something like a function that takes a list of names and generates a filter array or filter function that filters out those names would be sensible)

Resources