I'd like to display the persistent fields (those defined in my model file) in a property grid.
Property Grid:
Ext.define('ATCOM.view.InspectorProperties', {
extend : 'Ext.grid.property.Grid',
alias : 'widget.inspectorProperties',
cls : 'property-grid',
height : 150,
listeners : {
beforerender : function() {
// Rename the first column
var cols = this.getView().getHeaderCt().getGridColumns();
cols[0].setText("Property");
},
beforeedit : function(e) {
// Read-only
return false;
}
},
source : {} // Start with no items
});
I load items like so in a select event (in a controller), where record is our model object and getInfo() is the property grid:
var source = {};
source.id = record.get('id');
source.start = record.get('start');
source.end = record.get('end');
this.getInfo().setSource(source);
Model:
Ext.define('ATCOM.model.Shift', {
extend : 'Ext.data.Model',
fields : [ 'id', {
name : 'start',
type : 'date',
}, {
name : 'end',
type : 'date',
}, 'position', 'controller' ],
hasMany : {
model : 'ATCOM.model.ShiftAlloc',
name : 'allocations'
}
});
Is there a better way to go about this so all non-associative fields (excluding allocations in my case) are automatically sent to the property grid? It might also be possible to read the fields with ATCOM.model.Shift.getFields() and iterate over those checking for persistent:false; to keep the remaining keys, but how do I get the class reference from an instance - as in, how do I get ATCOM.model.Shift from one of its instances so I can call getFields()?
EDIT:
For finding the class name: http://docs.sencha.com/ext-js/4-1/#!/api/Ext.Base-static-method-getName
It may work to say setSource(record.data). I am just playing with this now; it seems to show the right information, though you may lose control over the details of which fields to enable for editing, etc.
Related
I have a combobox whose values I need to populate via the json formatted string I am retrieving via AJAX call.
Here's the string which I have retrieved (stored in data)
{
"a2m":
[
"a2mMeeting",
"sugar"
]
}
The combobox must contain a2m (this is the only name in the string as of now)
Here's what I have been trying:
Approach 1:
this.initAjaxCall = Ext.Ajax.request({
url : 'indigo/restproxy/get/v1/applications/list',
method:'GET',
scope : this,
success : function(result, request) {
var data = Ext.decode(result.responseText);
Object.keys(data).forEach(function(key) {
console.log(key);
Ext.getCmp('appCombo').getStore().add({appName : key});
})
}
});
this.appcombo = Ext.create('Ext.form.field.ComboBox', {
id : 'appCombo',
store: this.appStore
});
this.appStore = Ext.create('Ext.data.Store', {
fields : [
{
name : 'appName',
type : 'string'
}
]
});
Approach 2:
this.appStore = Ext.create('Ext.data.Store', {
fields: ['appName'],
proxy: {
type: 'ajax',
url: 'indigo/restproxy/get/v1/applications/list',
method : 'GET'
},
listeners: {
load: function(store, records, successful, eOpts ) {
store.insert(0, {
'appName' : 'yellow' //Trying to populate randomly using this method.
})
}
}
})
this.appcombo = Ext.create('Ext.form.field.ComboBox', {
id : 'appCombo',
store: this.appStore
});
In both the cases the combobox doesn't load any value and I am unable to see any value in the dropdown menu.
EDIT 1: When I printed the store.getCount() in the console after adding one element, the value is shown as 1 (checked by adding two elements and it does show 2). This implies the values are being added to the store but not being shown in the combobox dropdown menu. Kindly suggest how to resolve this.
SOLUTION:
The solution as it turns out was by adding a statement queryMode : 'local'. I don't understand why this was creating an issue. Anyone willing to shed light on this is welcome to do so.
As you have proven the store is being populated. However you haven't set a display field on the combo box.
this.appcombo = Ext.create('Ext.form.field.ComboBox', {
id : 'appCombo',
store: this.appStore,
displayField: 'appName'
});
displayField: The underlying data field name to bind to this ComboBox. Defaults to: 'text'
valueField: The underlying data value name to bind to this ComboBox. Defaults to match the value of the displayField config.
How to apply conditions in bind?
In view let
{
xtype : 'label',
bind : {
text : '{//--- set text as per the condition }',
hidden : '{//should be true if 'param' in VM is 1 or 2 else should
be false}'
}
}
in view model, 'param' is a data variable. If value of
param=1, text should be one,
param=2, text should be two,
param=3, text should be three.
Is this possible without formula by applying conditions directly in view?
{
xtype : 'label',
bind :
{
text : '{textVal}'==0?'Test':'TEST1234',
hidden : ('{param}'==1 || '{param}'==2)?true:false
}
}
In the associated viewModel, if one has the property param in data config, then one can use it for binding along with condition checks as mentioned above. If the param value is being changed dynamically, (i.e, this.getViewModel().setData('param', 1)) then still the code will work in hiding the component dynamically. The same applies with the other config, viewModel -> data:{textVal:0,param:1}. If one has an object inside the data, like data:{ config:{ textVal:0 }, param:1 }, One can use bind:{ text:'{config.textVal}' //along with ur condition check }
I'm trying to bind (two ways bind) some field of my form with data provided by my view model.
When I bind a simple string, it works perfectly. But when I try to bind a form field to an array field inside my viewmodel, modifications are not considered by the view model.
This is my model
Ext.define('Front.model.User', {
extend : 'Ext.data.Model',
fields : [{
name : 'id',
type : 'int'
}, {
name : 'name',
type : 'string'
}, {
name : 'email',
type : 'string'
}, {
name : 'last_connection',
type : 'date',
dateFormat : 'Y-m-d H:i:s'
}, {
name : 'birth',
type : 'date',
dateFormat : 'Y-m-d H:i:s'
}, {
name : 'schedule',
type : 'auto'
}]
});
This is my ViewModel
Ext.define('Front.view.user.SheetModel', {
extend : 'Ext.app.ViewModel',
alias : 'viewmodel.sheet',
requires : ['Front.model.User'],
data : {
currentUser:null
},
model : 'Front.model.User'
});
This is my Form
Ext.define('Front.view.user.Sheet', {
extend : 'Ext.form.Panel',
requires : ['Front.view.user.SheetController'],
controller : 'sheet',
viewModel : {
type : 'sheet'
},
config : {
currentUser : null
}, {
xtype : 'textfield',
fieldLabel : 'De',
name : 'morningstarttest',
width : 60,
bind : {
value : '{currentUser.schedule.lundi.am.start}'
}
}
});
The bind displays the correct value the first time but doesn't update if I change the value.
The 'schedule' field in a model is an array.
Any ideas how I could solve this problem?
How can your code know which item in the array to bind against? You'd need to do something like:
{currentUser.schedule[0].lundi.am.start}
In order to specify an array index. I can't remember offhand if you can do this, and that's because this just feels like a bad idea. I'd suggest rethinking what you're attempting to do. Rather than have schedule as a field on your model, look into associations.
What patterns have been successful when mapping a model with a few repeating structures.
I have to model and create a grid view a record with has a few columns which are basically the same repeating structure.
Ext.define('Ep.mm.model.MyComparison', {
var derived = function(val,)
extend: 'Ext.data.Model',
fields : [
{name:'id',type:'string'},
{name:'count',type:'number'},
{name:'special',type:'number'},
{name:'A_a',type:'number'},
{name:'A_b',type:'number'}
{name:'A_derived',renderer = function(val,meta,rec){return rec.get('A_b')-rec.get('A_a')}}
{name:'B_a',type:'number'},
{name:'B_b',type:'number'}
{name:'B_derived',renderer = function(val,meta,rec){return rec.get('B_b')-rec.get('B_a')}}
{name:'C_a',type:'number'},
{name:'C_b',type:'number'}
{name:'C_derived',renderer = function(val,meta,rec){return rec.get('C_b')-rec.get('C_a')}}
]
});
I am likely to end up with Server models/stores that differ only in the number of times this structure repeats (A) (A,B) (A,B,C,D). Is there a way to use a common model for each of A,B,C,D and just re-use the common model as the type. This is something akin to a 'nested' structure.
Ext.define('MyType',{
extend :'Ext.data.Model',
fields : [
{name : 'a', type:'number'},
{name : 'b', type:'number'},
{ name : 'derived',
convert : function(value, record) {
return record.get('a') - record.get('b');
}
}
]
}
Then MyComparison would be defined as
Ext.define('Ep.mm.model.MyComparison',{
extend : 'Ext'data.Model',
fields : [
{name:'A', type:'MyType'}
{name:'B', type:'MyType'}
{name:'C', type:'MyType'}
]
});
I know this isn't quite right, but this is sort of the functionality I am trying to create.
Perhaps using the 'hasOne' association type?
Yes, you can extend your own model classes:
Ext.define('Base', {
extend: 'Ext.data.Model',
fields: ['name']
});
Ext.define('Child', {
extend: 'Base',
fields: ['age']
});
var o = new Child({
name: 'Foo',
age: 100
});
console.log(rec.get('name'), rec.get('age'));
As a sidenote, models don't have renderers. You're probably looking for the convert function on the field, here.
I'd say you want functionality of a field type:
Ext.data.Types.A_DERIVED = {
convert: function(v, data) {
return rec.get('A_b')-rec.get('A_a');
},
type: 'number'
};
Then use it as a type of A_derived field:
Ext.define('Ep.mm.model.MyComparison', {
extend: 'Ext.data.Model',
fields: [
{name:'id',type:'string'},
{name:'A_a',type:'number'},
{name:'A_b',type:'number'},
{name:'A_derived', type: 'Ext.data.Types.A_DERIVED'}
]
});
Afterthoughts:
- Maybe you would like to aggregate all A_* data into one field? See documentation for an example.
- There is a trouble when you want to use the same mechanism with B_derived because there is no way of supplying B into convert function. A dirty hack might be to set the defaultValue to B and to bind the field to null data. Then v would be read from defaultValue.
I am trying to draw a grid where each line is a stock's performance for a single day. In my data structures, I have a Date, a Stock, and a Stock Price resource. The store attached to my grid is the Stock Price store.
So, to the best of my understanding, my biggest problem is that when the grid cell renderers, I need to already have the value, or I need to have a blocking function to get a value.
When I use the getStore() magic function, I'm told the record doesn't know about it (undefined method). I'm assuming it's 'cause the record doesn't have the same functionality as a standalone model.
I see a few ways out of this:
Customise the grid and/or store so that when a load happens, all the related rows are loaded at the same time.
Create a callback in the renderer, and change the cell value afterwards, but I'm ot exactly sure how to do this. I don't actually want to change the cell value (StockId), just the visible output (Symbol).
Change my API to match my view.
Summing these up: #1 seems like a lot of work for a seemingly simple outcome. I keep trying to use the associations, but I'm finding they're not really useful for anything aside from little things here and there, and certainly not for lots of data. #2 I don't quite know where to begin at the moment; and #3 seems like massive overkill, and will generally ruin my server side as I will mean a few more joins, and more complexity when saving records as well.
So, two part question:
Does anyone know how to load a value from an associated model in a grid?
If not, to pique my curiosity, what sort of things are associations used for in any case where there's lots of data to deal with on screen? Lot's of data seems to be the reason to use Ext vs jQueryUI or some other UI framework, so I'm wondering what the associations are for.
Model - Stock Price
Ext.define('MyApp.model.StockPrice', {
extend : 'Ext.data.Model',
idProperty : 'StockPriceId',
fields : [ {
name : 'StockId',
type : 'int'
}, {
name : 'Open',
type : 'float'
}, {
name : 'Close',
type : 'float'
}, {
name : 'DateId',
type : 'date'
}],
proxy : {
type : 'rest',
url : '/api/stock.price'
},
reader : {
type : 'json'
},
associations : [ {
type : 'hasOne',
model : 'MyApp.model.Date',
primaryKey : 'DateId',
foreignKey: 'DateId'
},{
type : 'hasOne',
model : 'MyApp.model.Stock',
primaryKey : 'StockId',
foreignKey : 'StockId'
} ]
});
Model - Stock
Ext.define('MyApp.model.Stock', {
extend : 'Ext.data.Model',
idProperty : 'StockId',
fields : [ {
name : 'StockId',
type : 'int'
}, {
name : 'Symbol',
type : 'string'
} ],
proxy : {
type : 'rest',
url : '/api/stock'
},
reader : {
type : 'json'
},
associations : [ {
type : 'hasMany',
model : 'MyApp.model.StockPick',
primaryKey : 'StockId',
foreignKey : 'StockId'
}]
});
Model - Date
Ext.define('MyApp.model.Date', {
extend : 'Ext.data.Model',
fields : [ 'DateId', 'Date' ]
});
Store - Stock Price
Ext.define('MyApp.store.StockPrice', {
extend : 'Ext.data.Store',
model : 'MyApp.model.StockPrice',
remoteSort : true,
remoteFilter : true,
pageSize : 5,
autoLoad : true
});
View - Stock Price
Ext.define('MyApp.panel.StockData', {
extend : 'Ext.grid.Panel',
store : 'MyApp.store.StockPrice',
columns : [
{
text : 'Symbol',
flex : 1,
sortable : false,
hideable : false,
dataIndex : 'StockId',
renderer : function(stockId, metadata, stockPriceRecord) {
// What goes in here? PROBLEM POINT
MyApp.model.Stock.load(stockId, function() {
// ... some callback
});
// OR
return stockPriceRecord.getStock().get('Symbol');
}
},
{
text : 'Open',
flex : 1,
dataIndex : 'Open',
renderer : 'usMoney'
},
{
text : 'Close',
flex : 1,
dataIndex : 'Close',
renderer : 'usMoney'
},
{
text : 'Volume',
flex : 1,
dataIndex : 'Volume'
}]
});
Your only real option to display data from an associated model in a grid is to use a custom renderer function on the column. This will not change any values; it will simply render the desired output into the cell.
Now, as for implementing that renderer function: I would start by removing the proxies from the models and instead create stores for each model and allow the stores to manage the proxies -- then, attach the store for Stock as a listener on the store for StockPrice to listen for the datachanged event. When the data of the StockPrice store changes, you should grab every unique referenced Stock id and then tell the Stock store to request a payload of stocks with those ids.
That may mean altering your API a little bit to support a SQL IN(...) behind the scenes, but by leaving the joins to the client side you will put less stress on your server side.
In short, you need to use a little bit of all three ideas you came up with in order to best achieve your goal.