ExtJS - using a custom TriggerField as a GridEditor - javascript

So I posted this last week to the ExtJS forums, but no one has responded and I'm going a bit crazy trying to figure it out:
I'm fairly new to ExtJS (just learned it last week for work), but I've been working with other JavaScript libraries for quite some time now. I'm making a custom control for editing a list of attributes (currently populated by a JSON request). I'm using PropertyGrid's with custom GridEditor's (created by extending various Ext.form fields). All of my custom fields work except one, which is a repeating value editor. Basically the field is going to be passed a simple 2d key/value pair array by the JSON request, which it displays in an EditorGridPanel (inside of a Ext.Window that I've created).
Here is the section of the JSON request that defines the repeating value editor:
{
key: 'Repeating',
type: 'repeating',
category: 'Category A',
options: {
dataArray: [
{key: 'key A', value: 'value A'},
{key: 'key B', value: 'value B'},
{key: 'key C', value: 'value C'}
]
}
}
The key is the name for the field (displayed on the left column of the PropertyGrid).
The type tells the function which creates all of the grid editors what type of custom editor to use.
The category is used to determine which PropertyGrid the GridEditor is added to (I have multiple PropertyGird's, all contained in a Panel with layout: 'acordion').
Anything in options is added to the extended Ext.form field when it is created.
So dataArray is attached to my repeating value editor for setting up the initial key/value pairs and to store the array passed back to the GridEditor by the Ext.Window used for editing it.
After some experimenting I decided to use a TriggerField as the GridEditor for my repeating value type. Here is the code for the definition of the repeating value field:
Ext.form.customFields = {
'repeating': Ext.extend(Ext.form.TriggerField, {
triggerClass: 'x-form-edit-trigger',
enableKeyEvents: true
})
};
And here is the code that sets it up:
Ext.form.customFields['repeating'] = Ext.extend(Ext.form.customFields['repeating'], {
onTriggerClick: function()
{
this.editorWindow.show();
},
listeners: {
'render': function(field)
{
field.editorWindow = new Ext.MultiSelectWindow({
data: field.dataArray,
parent: field
});
},
'keydown': function(field, event)
{
event.stopEvent();
},
'beforerender': function()
{
for (i in this.opt) {
if (i != 'store') {
this[i] = this.opt[i];
}
else {
this.store.loadData(this.opt.store);
}
}
if (this.regex != undefined) {
this.validator = function(value)
{
return this.regex.test(value);
};
}
}
}
});
And finally, here is the code for the custom editor window:
Ext.MultiSelectWindow = function(args)
{
var obj = this;
obj.args = args;
obj.KeyValue = new Ext.data.Record.create([{
name: 'key'
}, {
name: 'value'
}]);
obj.gridStore = new Ext.data.Store({
data: obj.args.data,
reader: new Ext.data.JsonReader({}, obj.KeyValue),
autoLoad: true
});
obj.cm = new Ext.grid.ColumnModel([{
id: 'key',
header: "Key",
dataIndex: 'key',
editor: new Ext.form.TextField({
allowBlank: false
}),
hideable: false,
sortable: false,
menuDisabled: true,
css: 'font-weight: bold;'
}, {
id: 'value',
header: "Value",
dataIndex: 'value',
editor: new Ext.form.TextField({}),
hideable: false,
sortable: false,
menuDisabled: true
}]);
obj.gridEditor = new Ext.grid.EditorGridPanel({
cm: obj.cm,
height: 280,
store: obj.gridStore,
autoExpandColumn: 'value',
listeners: {
'render': function()
{
// set up local aliases
obj.a = new Array();
obj.a.grid = obj.gridEditor;
obj.a.store = obj.a.grid.getStore();
obj.a.sel = obj.a.grid.getSelectionModel();
}
},
bbar: [{
text: 'Add',
cls: 'x-btn-text-icon',
icon: '/lib/images/add.png',
listeners: {
'click': function()
{
var kv = new obj.KeyValue({
key: '',
value: ''
});
var row = obj.a.store.data.items.length;
obj.a.grid.stopEditing();
obj.a.store.insert(row, kv);
obj.a.grid.startEditing(row, 0);
}
}
}, {
text: 'Delete',
cls: 'x-btn-text-icon',
icon: '/lib/images/delete.png',
listeners: {
'click': function()
{
if (obj.a.sel.selection)
obj.a.store.remove(obj.a.sel.selection.record);
}
}
}]
});
obj.panelAll = new Ext.Panel({
border: false,
layout: 'absolute',
items: [new Ext.Panel({
width: 250,
border: false,
x: 0,
y: 0,
items: obj.gridEditor
}), new Ext.Panel({
border: false,
x: 254,
y: 0,
items: [new Ext.Button({
cls: 'x-btn-icon-side',
icon: '/lib/images/arrow_up.png',
listeners: {
'click': function()
{
if (obj.a.sel.selection) {
var row = obj.a.sel.selection.cell[0];
var rec = obj.a.store.getAt(row);
if (row >= 1) {
obj.a.store.remove(rec);
obj.a.store.insert(row - 1, rec);
obj.a.grid.startEditing(row - 1, 0);
}
}
}
}
}), new Ext.Button({
cls: 'x-btn-icon-side',
icon: '/lib/images/arrow_down.png',
listeners: {
'click': function()
{
if (obj.a.sel.selection) {
var row = obj.a.sel.selection.cell[0];
var rec = obj.a.store.getAt(row);
var len = obj.a.store.data.items.length;
if (row < len - 1) {
obj.a.store.remove(rec);
obj.a.store.insert(row + 1, rec);
obj.a.grid.startEditing(row + 1, 0);
}
}
}
}
})]
})]
});
obj.win = new Ext.Window({
title: 'Repeating Value Editor',
layout: 'fit',
closeAction: 'hide',
border: false,
items: obj.panelAll,
width: 300,
height: 350,
resizable: false,
shadow: false,
buttonAlign: 'left',
buttons: [{
text: 'OK',
handler: function()
{
// reset the repeating field data array
obj.args.parent.dataArray = [];
for (r in obj.a.store.data.items)
obj.args.parent.dataArray[r] = obj.a.store.data.items[r].data;
obj.args.parent.setRawValue(attrValueToString(obj.args.parent.dataArray));
obj.win.hide();
}
}, {
text: 'Cancel',
handler: function()
{
obj.win.hide();
}
}]
});
obj.show = function()
{
obj.win.show();
obj.a.store.loadData(obj.args.parent.dataArray);
}
}
Now for my problem: all of this works fine, except for the 7th line of the window's 'OK' button handler ( obj.args.parent.setRawValue(attrValueToString(obj.args.parent.dataArray)); ).
obj is a self-alias.
obj.args.parent is an alias for the field that opened the repeating value editor window.
attrValueToString() is a function that takes in a 2d array and converts it to a string with special formatting so it can be displayed in a readable, meaningful manner in the TriggerField's textbox.
The data is loaded back into the field's dataArray variable and if you open the editor again, it will have the new data included in the view. I can't, however, manage to get any sort of value to be displayed in the TriggerField after it has been created. I have tried both obj.args.parent.setValue('abc') and obj.args.parent.setRawValue('abc') . No exception is thrown, yet the value displayed in the TriggerField does not change. I even tried creating a custom function for setting the value from within the TriggerField - something like this:
Ext.form.customFields['repeating'] = Ext.extend(Ext.form.customFields['repeating'], {
setFieldValue: function(value){
this.setValue(value);
}
});
This custom function works if called from within the TriggerField, but not when called from somewhere else (i.e. the editor window's 'OK' button handler). The function can be called successfuly from anywhere and does not produce any exceptions, however, it only sets the value correctly if called from within the TriggerField.
The custom field works perfectly when instantiated as a basic form field:
var sample = new Ext.form.customFields['repeating']({
renderTo: Ext.getBody(),
dataArray: [
{key: 'key A', value: 'value A'},
{key: 'key B', value: 'value B'},
{key: 'key C', value: 'value C'}
]
});
I have scoured the ExtJS API documentation and done every possible google search I can think of. I found a few forum posts that seem to be from people having a similar problem, but they never get a clear answer.
Any help with this matter would be most appreciated - thanks in advance!

I think you should use Ext.override for onTriggerClick handler function instead of redefining it in your superclass.
You also could set it right after triggerField creation (possibly in the 'render' event handler) by assigning it to a function name, i.e. trigger.onTriggerClick = somefunction.

Related

Selectize dropdown within slickgrid is hidden beyond slick grid boundary. How can I make it to come up at top?

I am using selectize to provide inline cell editing in slickgrid. I am able to load this component within cell. But when dropdown options container pops up and it goes beyond the slickgrid viewport, dropdown options container is getting truncated by slickgrid boundary. It should come over the grid. How can I bring the dropdown options container to the top.
var grid;
var columns = [
{ id: 'title', name: 'Title', field: 'title' },
{ id: 'duration', name: 'Duration', field: 'duration' },
{ id: '%', name: '% Complete', field: 'percentComplete' },
{ id: 'start', name: 'Start', field: 'start' },
{ id: 'finish', name: 'Finish', field: 'finish' },
{
id: 'effort-driven',
name: 'Effort Driven',
field: 'effortDriven',
editor: IEditor
},
];
var options = {
enableCellNavigation: true,
enableColumnReorder: false,
editable: true,
autoHeight: true
};
function IEditor(args) {
var selectElement = $('<input type="text"/>');
args.container.append(selectElement[0]);
selectElement.selectize({
create: false,
maxElements: 1,
options: [
{
name: 'A',
value: 'a'
},
{
name: 'B',
value: 'b'
},
{
name: 'C',
value: 'c'
},
{
name: 'D',
value: 'd'
},
{
name: 'E',
value: 'e'
},
{
name: 'F',
value: 'f'
},
],
labelField: 'name',
valueField: 'value'
});
/*********** REQUIRED METHODS ***********/
this.destroy = function() {
// remove all data, events & dom elements created in the constructor
};
this.focus = function() {
// set the focus on the main input control (if any)
};
this.isValueChanged = function() {
// return true if the value(s) being edited by the user has/have been changed
return false;
};
this.serializeValue = function() {
return '';
};
this.loadValue = function(item) {
};
this.applyValue = function(item, state) {
};
this.validate = function() {
return { valid: false, msg: 'This field is required' };
};
}
$(function() {
var data = [];
for (var i = 0; i < 3; i++) {
data[i] = {
title: 'Task ' + i,
duration: '5 days',
percentComplete: Math.round(Math.random() * 100),
start: '01/01/2009',
finish: '01/05/2009',
effortDriven: i % 5 == 0,
};
}
grid = new Slick.Grid('#myGrid', data, columns, options);
grid.init();
});
I have added selectize drop down in Effort Driven column in this plunker
I used to use chosen as my enhanced select but I ran into exactly this problem, and it wasn't solvable due to the HTML it used.
I had to jump ship to Select2. There are examples for this here - check out 'Select2 javascript drop-down editor' and 'Select2 Multiselect javascript drop-down editor'.
Make sure your z-index is bigger than the ones SlickGrid uses. If I remember correctly SlickGrid's highest one was 11 or 12, so with a 13 you should be good to go. Also check the overflow of the cells' css classes; it might happen that your context menu is simply cut off.

Set the prohibition on editing the field in Ext.window.Window provided

I need to set a ban on editing the text field in Ext.window.Window, provided that the value of the drop-down list is set to Postponed.
I'm trying to do this in the filterCombo function with:
var inp = this.up ('window'). down ('# MyTextField');
inp.disable ();
but in the console I get the error:
TypeError: this.up is not a function
What am I doing wrong?
Below is my code:
var store = Ext.create('Ext.data.Store', {
fields: ['order', 'id', 'name'],
storeId: 'DoubleBookStore',
data : [
{"id": 23, name: "New", order_install: 1},
{"id": 24, name: "In Work", order_install: 2},
{"id": 29, name: "Postponed", order_install: 3},
{"id": 34, name: "Shipped", order_install: 4},
{"id": 31, name: "In_transit", order_install: 5}
]
});
function filterCombo(combobox, records) {
if(records.data.name == 'Postponed'){
var inp = this.up('window').down('#MyTextField');
console.log(inp);
inp.disable();
}
index = records.data.order_install;
store = combobox.getStore();
store.clearFilter();
store.filterBy(
function(record) {
if ((record.internalId == index - 1) || (record.internalId == index) || (record.internalId == index + 1)) {
return true;
} else {
return false;
}
}
);
};
var window = Ext.create('Ext.window.Window', {
title: 'Приложение',
width: 300,
height: 200,
items:[{
xtype: 'combobox',
fieldLabel: 'Status',
name: 'status',
store: store,
valueField: 'id',
displayField: 'name',
typeAhead: true,
queryMode: 'local',
value: 24,
listeners: {
select : function(combo, records) {
filterCombo(combo, records);
}
}
},
{
xtype: 'textfield',
fieldLabel: 'Ваше имя:',
itemId:'MyTextField',
name: 'name'
}]
});
window.show();
You cant use "this" outside the scope of the select function, you are already passing "this" as the parameter "combobox", so use it like this:
function filterCombo(combobox, records) {
var inp = combobox.up('window').down('#MyTextField');
if(records.data.name == 'Postponed'){
inp.disable();
} else {
inp.enable();
}
...
When you define the filterCombo method as you define it takes this as global scope. Thats why there is no this.up as this here is global.
To make your code working you need to pass the scope when calling your function, just replace
listeners: {
select : function(combo, records) {
filterCombo(combo, records);
}
}
with
listeners: {
select : function(combo, records) {
filterCombo.apply(this,[combo, records]);
}
}
Notice the use of apply to alter the behavior of this in your method.
Solution:
function filterCombo(combobox, records) {
if (records.data.name == 'Postponed'){
var inp = combobox.up('window').getComponent('MyTextField');
console.log(inp);
inp.disable();
}
...
});
Explanation:
What you want to do is to get reference to your textfield and then disable it.
your texfield config uses itemId, so you must use texfield's container getComponent() method
to get textfield container use combobox.up('window'), not this.up('window')

How to enable Extjs filters on page load

I want to filter my grid with different buttons that active certain filters. However they do not work on page load. Only when you select/deselect a filter option and then click the button, does the filter actually kick in.
When the page first loads and you click a button, you get the following error:
TypeError: filter is undefined
How do I enable these filter settings when the page first loads?
To recreate the error. Load the fiddle and try clicking the buttons and notice they don't work. Then activate one of the filters and try again. The buttons will work after a filter is activated.
Fiddle
Buttons
var openButton = Ext.create('Ext.Button', {
text: 'Open Topics',
handler: function () {
var filter = grid[uniqueId].filters.getFilter('TopicStateValue');
filter.setActive(true);
filter.setValue('Open/Current');
}
});
var holdButton = Ext.create('Ext.Button', {
text: 'On Hold Topics',
handler: function () {
var filter = grid[uniqueId].filters.getFilter('TopicStateValue');
filter.setActive(true);
filter.setValue('Hold');
}
});
var closedButton = Ext.create('Ext.Button', {
text: 'Closed Topics',
handler: function () {
var filter = grid[uniqueId].filters.getFilter('TopicStateValue');
filter.setActive(true);
filter.setValue('Archived/Closed');
}
});
Columns
columns: [{
text: 'Title',
width: 260,
dataIndex: 'Title',
filterable: true,
filter: {
type: 'string'
// specify disabled to disable the filter menu
//, disabled: true
}
}, {
text: 'Description',
flex: 1,
dataIndex: 'Description',
filter: {
type: 'string'
// specify disabled to disable the filter menu
//, disabled: true
}
}, {
text: 'Modified',
width: 90,
dataIndex: 'Modified',
xtype: 'datecolumn',
format: 'm/d/Y',
filter: true
}, {
text: 'Status',
width: 100,
dataIndex: 'TopicStateValue',
filter: {
active: true,
type: 'list',
value: 'Open/Current',
options: ['Open/Current', 'Archived/Closed', 'Hold']
}
}]
Just check if the filter exists before pass value to it, if not, add it like this:
var openButton = Ext.create('Ext.Button', {
text: 'Open Topics',
handler: function () {
var filter = grid[uniqueId].filters.getFilter('TopicStateValue');
if (!filter) {
filter = grid[uniqueId].filters
.addFilter({
type : 'string',
dataIndex : 'TopicStateValue'
});
}
filter.setActive(true);
filter.setValue('Open/Current');
}
});
var holdButton = Ext.create('Ext.Button', {
text: 'On Hold Topics',
handler: function () {
var filter = grid[uniqueId].filters.getFilter('TopicStateValue');
if (!filter) {
filter = grid[uniqueId].filters
.addFilter({
type : 'string',
dataIndex : 'TopicStateValue'
});
}
filter.setActive(true);
filter.setValue('Hold');
}
});
var closedButton = Ext.create('Ext.Button', {
text: 'Closed Topics',
handler: function () {
var filter = grid[uniqueId].filters.getFilter('TopicStateValue');
if (!filter) {
filter = grid[uniqueId].filters
.addFilter({
type : 'string',
dataIndex : 'TopicStateValue'
});
}
filter.setActive(true);
filter.setValue('Archived/Closed');
}
});
You need to initialize the filters manually before you use them.
Please add the below code to your grid panel.
listeners: {
beforeRender:function() {
this.filters.createFilters();
}
},
I tried that on your fiddle and it works.

Passing an argument to a declared function

I have the following function declaration
var exportStore = function (exportVar) {
// Process export .cfc
var params_JSON = {
<cfoutput>
l_companyid: '#url.companyid#',
l_start: '#l_start#',
l_end: '#l_end#'
</cfoutput>
};
if(exportVar == 'results') {
var exportQuery = Ext.getCmp('query');
var query = exportQuery.getValue();
params_JSON.query = query;
}
<cfoutput>var url = 'some url parameters' + Ext.JSON.encode(params_JSON)';</cfoutput>
//ajax call here returns link to export file
// display export link
var myForm = new Ext.form.Panel({
title: 'Export File',
width: 300,
height: 200,
floating: true,
closable : true,
layout: {
type: 'hbox',
pack: 'center'
},
items: [{
xtype: 'displayfield',
name: 'export_file',
value: 'Click here to download file'
}],
buttons: [{
margin: '0 10 10 0',
text: 'Close',
handler: function() {
this.up('form').destroy();
}
}]
});
I am attempting call this function from a button that has a drop down selection.
{
text: 'Export All',
handler: exportStore
},
{
text: 'Export Search Results',
handler: exportStore
}
My question is can you pass a parameter to a function declared as a variable? I know I can just give both buttons their own handler, but that handler is going to contain quite a bit of code and im attempting to simplify... just wanted to know if a paramerter can be passed to exportStore in some form for example.....
{
text: 'Export All',
handler: [
exportStore,
extraParams: { exportVar: 'all' }
]
}
You could use Ext.bind() to achieve this like below:
var exportStore = function (exportVar) {
// exportVar will have your 'all' or 'search' value as per the
// button clicked
...
}
// In definition
{
text: 'Export All',
handler: Ext.bind(exportStore, undefined, ['all'])
}, {
text: 'Export Search Results',
handler: Ext.bind(exportStore, undefined, ['search'])
}
You can refer this fiddle for the usage.

In Rally SDK2, is there an example of adding data to the ReleaseComboBox or IterationComboBox?

I want to be able to add an option for "--ANY--".
This does not work:
Ext.create('Rally.ui.ReleaseComboBox', {
hideLabel: false,
fieldLabel: "Release: ",
width: 275,
allowBlank: true,
listeners: {
beforerender: function( dd, opts ) {
var store = dd.getStore();
store.add( {
Name: '--ANY--'
});
etc...
Something like this example should work. Try listening to the store load and then use the loadData method to load the data directly into the store, ensuring that the passed in data is the correct format for the ReleaseComboBox.
Also, set the append parameter to true for loadData(). Otherwise, it will remove the existing records from the store and then add the data.
Ext.create('Ext.Container', {
items: [{
xtype: 'rallyreleasecombobox',
storeConfig: {
listeners: {
load: function(store) {
store.loadData([{formattedName: '--ANY--',
formattedStartDate: 'n/a',
formattedEndDate: 'n/a',
isSelected: false}],
true);
store.sort('formattedStartDate', 'DESC');
}
}
}
}],
renderTo: Ext.getBody().dom
});

Categories