EXTJS Combobox not selecting by valueField after expand - javascript

I have written some code that works pretty well, but I have a strange bug
Here is an example...
PLEASE WATCH MY COMBOBOX BUG VIDEO
Like I said, this works well every time datachanged fires - the right index is selected and the displayField is displayed but, everytime after I type some text in the combobox, later, when the "datachanged" fires, it wont display the displayField. Instead, it displays the value from the setValue method I launch.
The strange thing is that if I don't ever type text and change the selection with the mouse there is no bug. Finally, this appears only when I type text in the combobox.
Has anyone heard of this bug, have a solution, or some wise advice?
The Code !
Two data stores :
ficheDataStore = new Ext.data.Store({
id: 'ficheDataStore',
autoLoad: true,
proxy: new Ext.data.HttpProxy({
url: 'ficheDetail.aspx', // File to connect to
method: 'GET'
}),
baseParams: { clientId: clientId, Type: 'Fiche' }, // this parameter asks for listing
reader: new Ext.data.JsonReader({ // we tell the datastore where to get his data from
root: 'results'
}, [
{ name: 'GUID', type: 'string', mapping: 'GUID' },
{ name: 'TagClient', type: 'string', mapping: 'TagClient' },
{ name: 'Nom', type: 'string', mapping: 'Nom' },
{ name: 'Compteur', type: 'string', mapping: 'CompteurCommunes' },
{ name: 'CompteurCommunesFacturation', type: 'string', mapping: 'CompteurCommunesFacturation' },
{ name: 'AdresseFacturation', type: 'string', mapping: 'AdresseFacturation' },
{ name: 'Codes', type: 'string', mapping: 'Codes' },
{ name: 'Observations', type: 'string', mapping: 'Observations' },
{ name: 'Adresse', type: 'string', mapping: 'Adresse' }
])
});
communesDataStore = new Ext.data.Store({
autoLoad: true,
proxy: new Ext.data.HttpProxy({ url: 'ficheDetail.aspx?Type=Communes' }),
reader: new Ext.data.JsonReader({ root: 'results' }, [{ name: 'Compteur' }, { name: 'Localisation'}])
});
Who return something like this for the
first:
{results:[{"Nom":"cercle interieur"},{"Observations":""},{"Codes":" "},{"Adresse":"dd"},{"CompteurCommunes"
:"1"},{"TagClient":"3-56"},{"GUID":"443609c6-d064-4676-a492-7baa7b4288d1"},{"AdresseFacturation":""}
,{"CompteurCommunesFacturation":"1"}]}
For the latter :
{"results":[{ "Compteur" : "1","Localisation" : "6200 ST ISIDORE"},{ "Compteur" : "2","Localisation"
: "21340 CHANGE"},{ "Compteur" : "3","Localisation" : "1200 ELOISE"},{ "Compteur" : "4","Localisation"
: "1200 ST GERMAIN SUR RHONE"},{ "Compteur" : "5","Localisation" : "75000 PARIS"},{ "Compteur" : "6"
,"Localisation" : "75001 PARIS 1ER ARRONDISSEMENT"}]}
a Combobox :
var comb = new Ext.form.ComboBox(
{
store: communesDataStore,
fieldLabel: 'Code postal',
// hiddenName: 'Compteur',
name: 'CompteurCommune',
id: 'CompteurCommunes',
width: 300,
typeAhead: true,
mode: 'local',
minChars: 0,
selecOnFocus: true,
forceSelection: true,
valueField: 'Compteur',
displayField: 'Localisation',
autocomplete: true,
emptyText: 'Selectionnez un code postal',
triggerAction: 'all',
value: ''
});
in a datachanged event i set the new value of the Combobox "CompteurCommunes" :
ficheDataStore.addListener('datachanged', handleDatachangedEvent);
function handleDatachangedEvent()
{
try {
comb.setValue(ficheDataStore.getAt(4).data.Compteur);
}
catch (err) { }
}

It's probably because when you type random data into combo, it may not locate correct fieldValue every time. Then it stucks at the last non-existing value.
Try to set ComboBox to any existing value (in combo's datastore) before doing new setValue() in your datachanged event handler. Or you can try to use clearValue() method to reset the previous (undefined) valueField.
There also initList() method existing to reset combo to initial state.
EDIT: After some testing, I found that:
combo.store.clearFilter(); must be used before setValue in the external event handler.

function formatComboBox(value, metaData, record, rowIndex, colIndex, store) {
myStore = Ext.getCmp('myComboBox');
myStore.clearFilter();
var idx = myStore.find('value',value);
return (idx != '-1') ? myStore.getAt(idx).data.label : value;
}

First, the Ext JS combobox should automatically apply the value and display when an item is selected, barring you've assigned a store and told Ext the field requires a value.
The value you appear to be asking for (CompteurCommunes) does not appear in your reader definitions, so it would be a part of the records in the data store.
What is the underlying reason for why you are trying to set this value for the ComboBox?

You can have a look at hiddenName and hiddenId parameter of Ext.form.ComboBox. If you set the value of hidden form field linked with combobox then combobox would render the label of that value instead of the value itself.
This is useful when you need to set the value at server end and direct the user to the page.
Another useful method is selectByValue. This method would select the element that has the value equal to the passed argument.
In your dataChangedListener instead of setting the value of combobox, you should set the value hidden form field associated with ComboBox. Also after setting the value of hidden field you might have to fire selectByValue method.
You can have a look at ExtJS API Documentation for further reference.

In case anybody - like me - get here through google because it's the most similar to their ComboBox ft. setValue() problem:
After an hour of stepping in out and over Ext's internals, I found that I needed to set lazyInit: false for the combo boxes. It defaults to true and being true may cause unlogical behaviour if you don't know about this. And why would you? Anything else seems to be not lazy by default.

Related

How to save, load and delete from database using a single ExtJS model?

Using ExtJS 6 one can have a store bind to the model and use the methods sync to save or load to load data.
I imagine that if a data is removed from store, upon calling sync the data will be removed from database too.
In my use case, I have different URLs and mandatory Ajax query fields for each action of create/update, load and delete data.
I have only seen examples showing load or save to storage, how can I declare the load, save and delete using Ajax in the same model?
Another doubt I have is that stores themselves can have a proxy, so they can perform those operations too, at least the load operation that I have seen in use. What's the difference between having these on the model or store? What's the best practice?
Example model from Sencha docs (is this only for read?):
Ext.define('MyApp.model.Base', {
extend: 'Ext.data.Model',
fields: [{
name: 'id',
type: 'int'
}],
schema: {
namespace: 'MyApp.model', // generate auto entityName
proxy: { // Ext.util.ObjectTemplate
type: 'ajax',
url: '{entityName}.json',
reader: {
type: 'json',
rootProperty: '{entityName:lowercase}'
}
}
}
});
Another example I found on https://examples.sencha.com/extjs/6.0.1/examples/classic/writer/writer.html using the proxy config, this seems more like what I would need as it specifies a URL for each operation:
var store = Ext.create('Ext.data.Store', {
model: 'Writer.Person',
autoLoad: true,
autoSync: true,
proxy: {
type: 'ajax',
api: {
read: 'app.php/users/view',
create: 'app.php/users/create',
update: 'app.php/users/update',
destroy: 'app.php/users/destroy'
},
reader: {
type: 'json',
successProperty: 'success',
root: 'data',
messageProperty: 'message'
},
writer: {
type: 'json',
writeAllFields: false,
root: 'data'
},
listeners: {
exception: function(proxy, response, operation){
Ext.MessageBox.show({
title: 'REMOTE EXCEPTION',
msg: operation.getError(),
icon: Ext.MessageBox.ERROR,
buttons: Ext.Msg.OK
});
}
}
},
listeners: {
write: function(proxy, operation){
if (operation.action == 'destroy') {
main.child('#form').setActiveRecord(null);
}
Ext.example.msg(operation.action, operation.getResultSet().message);
}
}
});
I believe I can have something like this in my case (this is just an example not tested!):
Ext.define('My.Person.Model', {
proxy: {
type: 'ajax',
api: {
read: 'http://myapiserver/getuser',
create: 'http://myapiserver/upsertuser',
update: 'http://myapiserver/upsertuser',
destroy: 'http://myapiserver/removeuser'
},
reader: {
type: 'json',
successProperty: 'success',
root: 'data',
messageProperty: 'message'
},
writer: {
type: 'json',
writeAllFields: false,
root: 'data'
},
// How can I have the parameters for each one?
extraParams : {
isuserUnderage : ' '
, query : '%'
}
}
});
I have no idea how to do this, specially specifying parameters for each type of Ajax request (read, create, update, destroy), I can have an upsert request that will send all fields, but the remove request will require only the ID, the get request can have optional fields for filtering, like filtering persons by name.
Example to be more clear of the problem.
Example data:
[
{
"id": "1",
"name": "Fred",
"age": 21,
"sex": "m"
},
{
"id": "2",
"name": "Susan",
"age": 12,
"sex": "f"
},
{
"id": "3",
"name": "Marcus",
"age": 22,
"sex": "m"
},
{
"id": "4",
"name": "Alex",
"age": 32,
"sex": "m"
}
]
Endpoints example:
Endpoints have parameters, these are mandatory, this means that calling an enpoint without a parameter will cause a server error, also passing a parameter that is not specified will cause a server error! If a parameter is not necessary one can pass a string with a single whitespace .
To read:
Endpoint: http://myapiserver/getuser?query={query}
Name is a filter by name, for example http://myapiserver/getuser?query=fred will bring users with name that has the string fred.
To write, we usually have an upsert, so it works for both insert and update:
Endpoint: http://myapiserver/upsertuser?id={id}&name={name}&age={age}&sex={sex}
So to update we can pass the ID: http://myapiserver/upsertuser?id=1&name=Frederick&age=21&sex=m and to insert we pass an empty string for ID: http://myapiserver/upsertuser?id= &name=Maurice&age=41&sex=m
To remove:
Endpoint: http://myapiserver/removeuser?id={id}
Example: http://myapiserver/removeuser?id=1, removes person with ID 1.
Because you say it's mandatory to use GETs with query params, I would encourage you to rethink your tech stack because the RESTful verbs really make it more clear what your action is, and you remove the actual action from your URL routes. However, I know sometimes this is totally out of our control, so I'll try my best here... I have to say, I've never experienced something like this, so I don't know if what I'm showing here is a best practice.
I can't show a true implementation because Sencha Fiddle is a simple sandbox, not meant for actual server-side implementations. I'm also assuming that you're using the classic toolkit, but if you need it in modern, it's a fairly easy port that you can do.
I prefer the proxy inside of the model for several reasons... if I need to use this model in several different stores throughout my app, then each store will inherit the same proxy. If I want to use the same model, but I don't want its proxy, I can simply override it when defining the store. Also, if the proxy doesn't exist on the model, then the framework assumes what your URL should be, which doesn't work when I want to use models individually.
I think I've come up with what you're asking for in this Fiddle. Really the core of what you want is in GETUser.js.
// We need to create our own proxy that will handle this for us
Ext.define('AjaxGet', {
extend: 'Ext.data.proxy.Ajax',
alias: 'proxy.ajaxGet',
// Per your requirement, we want to send individual requests
batchActions: false,
createOperation: function (action, config) {
// This means we're doing an action against one of our records
if (config && config.records) {
if (action === 'destroy') {
config.params = config.records[0].getDeleteParams();
} else if (action === 'create' || action === 'update') {
config.params = config.records[0].getUpsertParams();
}
}
return this.callParent(arguments);
}
});
// This is the desired, "GET" User model that uses GETs and query params for all actions
Ext.define('GETUser', {
extend: 'Ext.data.Model',
idProperty: 'Id',
fields: [{
name: 'Name',
type: 'string'
}, {
name: 'Id',
type: 'int'
}, {
name: 'Age',
type: 'int'
}, {
name: 'Sex',
type: 'string'
}],
proxy: {
type: 'ajaxGet',
api: {
read: 'Users',
create: 'upsertuser',
update: 'upsertuser',
destroy: 'removeuser'
},
actionMethods: {
create: 'GET',
update: 'GET',
destroy: 'GET'
}
},
getUpsertParams: function () {
const data = this.getData();
// Means this record hasn't been saved, so we're in the CREATE state
if (this.phantom) {
// We don't want to send the ID with what the framework sets as the ID
data.Id = undefined;
}
return data;
},
getDeleteParams: function () {
return {
Id: this.get('Id')
};
}
});
So what I ended up doing was creating a custom proxy that overrides the createOperation method to check which operation we're doing... based on that operation, we use the methods in the model to retrieve the params we want to send to the API. You need actionMethods in the proxy because otherwise, they default to POSTs.

ExtJs deleting element from store doesn't work

I'm working with ExtJs 6.2.0 and Java Spring MVC for the REST API.
I'm trying to delete an object from one of my store but I'm having a problem : instead of using my id named idCamp, extjs is using the field named id that contains an extjs generated id (for example: extModel47-1).
I'm working on the delete part but I didn't try to update a camp nor fetch one, but I think the configuration is the same for these three operations that need the id.
Here is my store:
Ext.define('XXXXXX.store.Camps', {
extend: 'Ext.data.Store',
alias: 'store.camps',
model: 'XXXXXX.model.Camp',
fields: [
'idCamp', // More irrelevant fields
],
autoLoad : true,
autoSync: true,
storeId: 'storeCamp',
proxy: {
type: 'rest',
idParam: 'idCamp',
url: // irrelevant,
reader: {
type: 'json',
rootProperty: 'data'
},
writer: {
type: 'json'
}
}
});
Here is my model:
Ext.define('XXXXXX.model.Camp', {
extend: 'Ext.data.Model',
idProperty: 'idCamp',
fields: [
{ name: 'idCamp', type: 'int' },
// More irrelevant fields
]
});
I also tried to put an idProperty inside the writer/reader inside the proxy but it didn't do anything.
Forgive my poor usage of the English language since I'm a French people.
Best regards,
Morony
The issue is using a combination of fields/model in your store definition. They are in competition with each other. Remove the fields definition.

How to add a property to an entity dynamically?

How do you add a property to an entity dynamically? I've been looking, but haven't found anything.
For example, I have this model definition (I'm using the WebSQL provider):
$data.Entity.extend('$db.Types.Person', {
id: { type: 'int', key: true, computed: true },
name: { type: 'string' }
});
$data.EntityContext.extend('$db.Types.DBContext', {
Persons: { type: $data.EntitySet, elementType: $db.Types.Person},
});
At some point I need to extend my model with new properties. Initially I don't know these properties' names.
The syntax is very simple for this, but the background info is more important, please read the whole answer before you reuse the snippet.
The YourType can be extended with new fields using the YourType.addMember() function. See this example snippet:
$data.Entity.extend('Product', {
id: { type: 'int', key: true, computed: true },
Name: { type: 'string' }
});
$data.EntityContext.extend('Northwind', {
Products: { type: $data.EntitySet, elementType: Product},
});
Product.addMember('Description', {
type:'string',
key: false,
computed: false,
required: false
});
var context = new Northwind({provider: 'webSql', databaseName: 'Northwind'});
context.onReady(function() {
var product1 = new Product({ Name: 'Beer', Description: 'tasty'});
context.Products.add(product1);
context.saveChanges(function(result) {
//check the content of WebSQL DB
console.log(product1);
});
});
You can user the addMember() only before creating an instance of the context.
Important info:
There is no data migration/merge by in the library, and the default behavior on schema modification for webSql is to drop&re-create the DB. As IndexedDB isn't bound to a schema, the existing records won't be dropped. Make a try by running this code and adding more fields, here is a working JSFiddle.
The real solution is to use Schema Evolution module of JayData Pro to manage the changes in your data model.

Save nested form data back to server in ExtJS

I have a Model that contains an association to another Model. I am able to display the nested data into a form by using the mapping attribute on the field. Example:
Ext.define('Example.model.Request', {
extend: 'Ext.data.Model',
fields: [
{
name: 'id',
type: Ext.data.Types.NUMBER,
useNull: false
}
{
name: 'plan_surveyor',
mapping: 'plan.surveyor',
type: Ext.data.Types.STRING
}
],
associations: [
{type: 'hasOne', associationKey: 'plan', getterName:'getPlan', model: 'Specs.model.Plan'}
],
proxy: {
type: 'direct',
api: {
read: requestController.load,
update: requestController.update,
},
reader: {
type: 'json',
root: 'records'
},
writer: {
type: 'json',
writeAllFields: true,
nameProperty: 'mapping'
}
}
});
Using this method, I can display the plan.surveyor value in the form by reference plan_surveyor. I call Form.loadRecord(model) to pull the data from the model into the form.
However, now that I'm trying to send the data back to the server, I get the error:
Error performing action. Please report the following: "Unrecognized field "plan.surveyor"
I am attempting to save to the server by first calling Form.updateRecord(model), then model.save(). Is there a way to have the Writer understand that 'plan.surveyor' is not a property name but instead to properly handle nesting?
Am I doing this the right way to start with, or should I just be handling the setting of the form data and loading back into the model in a more manual fashion? It seems that nested data is not all that well supported in general - any recommendations?
Ext.define('Example.model.Request', {
extend: 'Ext.data.Model',
fields: [
{
name: 'id',
type: Ext.data.Types.NUMBER,
useNull: false
}
{
name: 'plan_surveyor',
mapping: 'plan.surveyor',//change to 'plan_surveyor'
type: Ext.data.Types.STRING
}
],
change that show in comment ,because data index is given in above format because ur give ur format thata time that is not dataindex it's a column or extjs preparatory ,so please change that may it's work well
it's not work u will send hole code

ExtJS4 Changing data store parameters

If I have a data store on a grid like so:
store = Ext.create('Ext.data.Store', {
fields: [{
name: 'id'
}, {
name: 'filename'
}
// other fields here ...
],
proxy: {
type: 'ajax',
url: 'http://myniftyurl.com/blah',
simpleSortMode: true,
reader: {
type: 'json',
totalProperty: 'total',
root: 'result'
},
extraParams: {
'limit': Ext.get('itemsPerPage').getValue(),
'to': Ext.get('to_date').getValue()
// other params
}
},
model: 'Page',
remoteFilter: true,
remoteSort: true
});
The 'limit' and 'to' value will change based on user input, therein lies the problem. The data store keeps using the original parameters instead of the new inputs. How can I fix this?
Thanks in advance!
I normally load my grids manually by executing the store.load() in the controller.
That way prior to it I can change the store's params like so:
//getForm() retrieves the Ext.basic.Form (from Ext.panel.Form)
var params = this.getForm().getValues();
//Write over
grid.getStore().getProxy().extraParams = params;
//load
grid.getStore().load();
I'm using buffered grids which required a decent amount of rework to get completely working in 4.0.7. But that should work for you.
Another options is to use a beforeload listener but I'm not sure if changing the extraParams by then will do anything. You may be able to modified the Ext.data.Operation object that gets passed to the event handler?
Let me know how that works out for you.
Good luck!

Categories