I currently have a rather big Grid and am successfully using the RowExpander plugin to display complementary informations on certain rows. My problem is that it's not all rows that contain the aforementioned complementary informations and I do not wish the RowExpander to be active nor to show it's "+" icon if a particular data store's entry is empty. I tried using the conventional "renderer" property on the RowExpander object, but it did not work.
So basically, how can you have the RowExpander's icon and double click shown and activated only if a certain data store's field != ""?
Thanks in advance! =)
EDIT: I found a solution
As e-zinc stated it, part of the solution (for me at least) was to provide a custom renderer that would check my conditional field. Here is my RowExpander:
this.rowExpander = new Ext.ux.grid.RowExpander({
tpl: ...
renderer: function(v, p, record) {
if (record.get('listeRetourChaqueJour') != "") {
p.cellAttr = 'rowspan="2"';
return '<div class="x-grid3-row-expander"></div>';
} else {
p.id = '';
return ' ';
}
},
expandOnEnter: false,
expandOnDblClick: false
});
Now, the trick here is that for this particular Grid, I chose not to allow the expandOnEnter and expanOnDblClick since the RowExpander will sometimes not be rendered. Also, the CSS class of the grid cell that will hold the "+" icon is 'x-grid3-td-expander'. This is caused by the fact that the CSS class is automatically set to x-grid3-td-[id-of-column]. So, by setting the id to '' only when I'm not rendering the rowExpander, I'm also removing the gray background of the un-rendered cells. So, no double click, no enter, no icon, no gray-background. It really becomes as if there is strictly no RowExpander involved for the columns where my data store field is empty (when I want no RowExpander).
That did the trick for me. For someone that wishes to preserve the ID of the cell, or that wishes to keep the double click and enter events working, there is nothing else to do other than extending the class I guess. Hope this can help other people stuck in the position I was!
As e-zinc stated it, part of the solution (for me at least) was to provide a custom renderer that would check my conditional field. Here is my RowExpander:
this.rowExpander = new Ext.ux.grid.RowExpander({
tpl: ...
renderer: function(v, p, record) {
if (record.get('listeRetourChaqueJour') != "") {
p.cellAttr = 'rowspan="2"';
return '<div class="x-grid3-row-expander"></div>';
} else {
p.id = '';
return ' ';
}
},
expandOnEnter: false,
expandOnDblClick: false
});
Now, the trick here is that for this particular Grid, I chose not to allow the expandOnEnter and expandOnDblClick specifically since the RowExpander will sometimes not be rendered. Also, the CSS class of the grid cell that will hold the "+" icon is 'x-grid3-td-expander'. This is caused by the fact that the CSS class is automatically set to x-grid3-td-[id-of-column]. So, by setting the id to an empty string only when I'm not rendering the rowExpander, I'm also removing the gray background of the cells that won't offer any expanding. So, no double click, no enter, no icon, no gray-background. It really becomes as if there is strictly no RowExpander involved for the columns where my data store field is empty (when I want no RowExpander).
That did the trick for me. For someone that wishes to preserve the ID of the cell, or that wishes to keep the double click and enter events working, there is nothing else to do other than extending the RowExpander class in my opinion. Of course, one could also use Ext.override(), but then all instances of RowExpander would be hit by the override.
I have the same task, there is my solution
var rowExpander = new Ext.ux.grid.RowExpander({
renderer : function(v, p, record){
return record.get('relatedPageCount') > 0 ? '<div class="x-grid3-row-expander"> </div>' : ' ';
}
});
I have overridden render method which test relatedPageCount field in store and render + or white space.
I think I've found a cleaner solution.Give me a feedback pls :)
I extend the toggleRow method of RowExpander and if I match a condition avoid to toggle the row.Otherwise the standard flow continues
Ext.create('customplugins.grid.plugin.ClickRowExpander',{
pluginId : 'rowexpander',
rowBodyTpl : new Ext.XTemplate(
'<p><b>Last Modified By:</b> {usermodify}</p>',
'<p><b>User data:</b> {userdata}</p>',
'<p><b>Correlation ID:</b> {correlationid}</p>',
'<p><b>Description:</b> {descr}</p>'
),
toggleRow : function(rowIdx, record) {
if(record.get('directory')) return false;
customplugins.grid.plugin.ClickRowExpander.prototype.toggleRow.apply(this, arguments);
}
})
This version works in Ext JS 5 and 6 (classic)
One thing is to remove the +/- icon, which can be done via grid viewConfig:
getRowClass: function (record, rowIndex, rowParams, store) {
var yourFieldofChoice = record.get('yourFieldofChoice');
if (yourFieldofChoice == null) {
return 'hide-row-expander';
}
},
Define css for hide-row-expander:
.hide-row-expander .x-grid-row-expander {
visibility: hidden;
}
Now you disable expanding on enter key ('expandOnEnter' config is no longer supported in Ext JS 6) or double click by overriding toggleRow, or if you do not wish the override you create your custom rowexpander built on existing plugin:
Ext.define('RowExpander', {
extend: 'Ext.grid.plugin.RowExpander',
alias: 'plugin.myExpander',
init: function (grid) {
var me = this;
me.grid = grid;
me.callParent(arguments);
},
requiredFields: ['yourFieldofChoice'],
hasRequiredFields: function (rec) {
var valid = false;
Ext.each(this.requiredFields, function (field) {
if (!Ext.isEmpty(rec.get(field))) {
valid = true;
}
});
return valid;
},
toggleRow: function (rowIdx, record) {
var me = this, rec;
rec = Ext.isNumeric(rowIdx)? me.view.getStore().getAt(rowIdx) : me.view.getRecord(rowIdx);
if (me.hasRequiredFields(rec)) {
me.callParent(arguments);
}
}
});
I have handled the beforeexpand event inside the listeners of Ext.ux.grid.RowExpander. beforeexpand method got the whole row data injected. Checking the data conditionally we can return true or false. If we return false it wont expand otherwise it will do.
var expander = new Ext.ux.grid.RowExpander({
tpl: '<div class="ux-row-expander"></div>',
listeners: {
beforeexpand : function(expander, record, body, rowIndex){
var gpdata = record.data.GROUP_VALUES[1].COLUMN_VALUE
if(gpdata == null){
return false;
}
else{
return true;
}
}
}
});
Related
I'm new using kendo grid UI, i'm trying to make a non editable column (when updating) using a simple code :
schema: {
id: 'ID',
fields: {
id: { editable: false }
}
}
This default schema, makes by default non editable id column, and i can't even create a new row with id .
I want to make it non editable (when updating) but i want the possibility to create a row and assign an id from user (when creating).
Any ideas ?
Edit :
PS : the proprety is not related to only id, it can be on every column (can't update but can create)
The editable required a function instead of a value.
columns: [
{ field: 'value', editable: function () { return false; } }
],
Checkout here:
https://dojo.telerik.com/oROJayAd
I always doubt about that model editable option. It never really worked for me. It should have something very deep in the setup to make it work which I never realized what it. So this is a way to acomplish what you need that I know it indeed works: To cancel the edit event. Check it out:
edit: function(e) {
// Cancels a new row
if (arguments, e.model.isNew()) {
this.cancelRow(e.container.parent());
}
else { // Cancels a cell editing
this.closeCell(e.container);
}
}
Demo
Now, if you like to add a condition in that event based on what you have set in your model, you can access it within event as well:
edit: function(e) {
let currentColumn = this.options.columns[e.container.index()].field,
model = this.dataSource.options.schema.model.fields[currentColumn];
if (model.editable === false) {
// Cancels a new row
if (arguments, e.model.isNew()) {
this.cancelRow(e.container.parent());
}
else { // Cancels a cell editing
this.closeCell(e.container);
}
}
}
Demo
You can add an option yourself in the model to set if the column can be updated or only created, and handle that information inside the event, canceling the editing whenever you like.
This is how I just did it, though there are other ways.
In columns option if you remove the field option from a column it doesn't know from where to bind it.
Then use the template option to show(bind) the id. Thus making it readonly
columns: [
{
title: 'Id', width: "40px",
template: "#= id #",
},
...]
I am rendering a component into a grid rowExpander plugin rowBodyTpl:
plugins: [{
ptype: 'rowexpander',
rowBodyTpl : [
'<div id="contact-row-{Id}"> </div>'
]
}],
var row = 'contact-row-' + record.get('Id'),
grid = Ext.getCmp(gridId),
if(!grid) me.createSubGrid(record,gridId).render(row);
This works fine. But when the grid receives an update event for a record with certain modifiedFields, that record's rowExpander is rerendered using the rowTpl only, and the inner grid is dangling somewhere. I can access the grid using Ext.getCmp, and grid.rendered is true, but it is not shown. So I guess I have to reattach the grid after the row has been rerendered.
I think I can attach to the update event, and find out whether the rowTpl has been rerendered without the child grid using
if(!grid.container.contains(grid.el))
But is there a way to put the grid back into the dom?
I have tried
grid.container.insertFirst(grid.el);
but this does not work. The element is inserted but the grid is not displayed.
Any ideas?
The way you are doing leads to memory leaks (because your component is not destroy when the grid is re-render).
I suggest to check at : Component Template (http://skirtlesden.com/ux/ctemplate) or if you try to render a nested grid, there was several attempt to build something correct and I think a working one is the following : http://blogs.walkingtree.in/2015/06/30/nested-grid-in-sencha-ext-js/
With my trials, I already was on the right track, and the error was somewhere else entirely. The problem was that my update event fn which did the reattach was executed before the update event fn that broke everything, because the fn was fired on the same event and had the same priority, but was attached to the store earlier. Changing the priority was one option, but I just put the creation into the boxready event, which is executed after the view has attached its events to the store:
xtype:'grid',
listeners:{
boxready:function(grid) {
var store = grid.getStore();
...
store.on({
update:function(store, record) {
me.restoreSubgridDom(record);
},
beforeload:function(store) {
var records = store.getRange();
Ext.each(records,me.destroySubgrid);
}
});
}
}
with the two functions:
restoreSubgridDom: function(record) {
var row = 'contact-row-' + record.get('Id'),
gridId = 'ContactRow'+record.get('Id')+'Grid',
grid = Ext.getCmp(gridId);
if(grid && !grid.container.contains(grid.el))
{
grid.container.insertFirst(grid.el);
}
},
destroySubgrid: function(record) {
var gridId = 'ContactRow'+record.get('Id')+'Grid',
grid = Ext.getCmp(gridId);
if(grid) grid.destroy();
}
I was hoping to detect when a Kendo grid's row changes, by navigation as opposed to selecting.
By this I mean I would have a grid with selectable: false, in batch edit mode, and I would like to update the data source (in code) when the user tabs to a new row (just as Access does).
I have looked at this example and changed the following properties..
selectable: false,
navigatable: true,
editable: true,
Unfortunately the changed event does not when seem to fire for tabs or arrow keys (when in navigation mode).
Would anyone know any other way I can do as described above (ie know when we have changed row via navigation)
Thanks in advance for any help!
You can use the edit event to determine whether you're in a new row.
Here you go:
selectable: false,
navigatable: true,
editable: true,
edit: function(e) {
if (e.sender.cellIndex($(e.container)) === 0 &&
$(e.container).closest("tr").index() !== 0) {
console.log("next row; update DS");
}
},
You could also store the last row you were in and determine the change using that, if switching between rows in other ways than by tabbing (or when tabbing backwards) is relevant.
If you don't want the grid to be editable, it's more difficult. Here's a quick hack:
var grid = $("#grid").data("kendoGrid");
var elem = $(grid.table)[0];
var handlers = $._data(elem, "events")["keydown"][2];
var oldHandler = handlers.handler;
// replace the existing event handler attached by kendo grid
var newHandler = function (e) {
oldHandler(e);
var current = grid.current();
var closestRow = $(current).closest("tr");
var rowIndex = $(closestRow).index();
if (rowIndex !== grid._lastNavRowIndex) {
if (typeof grid._lastNavRowIndex !== "undefined") {
kendoConsole.log("we just changed to row " + rowIndex);
}
grid._lastNavRowIndex = rowIndex;
}
};
handlers.handler = newHandler;
});
Try it here.
This is probably what you are looking for. When you want events related to the data, you have to look for the DataSource events. When you want events related to the UI, than you look at the Grid events.
I am building a pretty combobox with checkboxes and conditional entries. Everything works out alright, except for two features that I cannot figure out how to implement.
1) I would like to move the label inside the combobox, make it shift the values to the right, and appear in a slightly gray color.
2) I would like the value to ignore certain entries (group headers) selected. Those entries are there for functionality only - to select/unselect groups of other entries.
The entire project is in the zip file. You don't need a server, it's a client base app. Just download the archive, unpack, and launch app.html in your browser.
http://filesave.me/file/30586/project-zip.html
And here's a snapshot of what I would like to achieve.
Regarding your second issue, the best way I see is to override combobox onListSelectionChange to filter the values you don't want:
onListSelectionChange: function(list, selectedRecords) {
//Add the following line
selectedRecords = Ext.Array.filter(selectedRecords, function(rec){
return rec.data.parent!=0;
});
//Original code unchanged from here
var me = this,
isMulti = me.multiSelect,
hasRecords = selectedRecords.length > 0;
// Only react to selection if it is not called from setValue, and if our list is
// expanded (ignores changes to the selection model triggered elsewhere)
if (!me.ignoreSelection && me.isExpanded) {
if (!isMulti) {
Ext.defer(me.collapse, 1, me);
}
/*
* Only set the value here if we're in multi selection mode or we have
* a selection. Otherwise setValue will be called with an empty value
* which will cause the change event to fire twice.
*/
if (isMulti || hasRecords) {
me.setValue(selectedRecords, false);
}
if (hasRecords) {
me.fireEvent('select', me, selectedRecords);
}
me.inputEl.focus();
}
},
And change your onBoundlistItemClick to only select and deselect items in the boundlist not to setValue of the combo:
onBoundlistItemClick: function(dataview, record, item, index, e, eOpts) {
var chk = item.className.toString().indexOf('x-boundlist-selected') == -1;
if ( ! record.data.parent) {
var d = dataview.dataSource.data.items;
for (var i in d) {
var s = d[i].data;
if (s.parent == record.data.id) {
if (chk) { // select
dataview.getSelectionModel().select(d[i],true);
} else { // deselect
dataview.getSelectionModel().deselect(d[i]);
}
}
}
}
},
Regarding your first issue, it is easy to add the label using the displayTpl config option. But this will only add the text you need, without any style (grey color, etc). The combo is using a text input, which does not accept html tags. If you don't need the user to type text, than you may want to change the combo basic behavior and use another element instead of the text input.
I've developed web applications with Dojo for more than one year, and I've used dojox grid a lot, but there is no way to add customize buttons on DataGrid or EnhancedGrid, as I know that ExtJS, or EasyUI, jQuery jqgrid are capable doing this.
So I want to ask if there is any way that can add buttons or other HTML DOM in the dojox.DataGrid?
at least, you can add dojo.form.Button's to it. simly add an element to the structure-property of your DataGrid like that (sorry, due to no-time i just copy pasted it from an actual project of mine...):
{
name: ' ',
field: 'idx',
type: dojox.grid.cells._Widget,
editable: false,
formatter: function (idx) {
return new dijit.form.Button({
_destroyOnRemove: true,
label: 'Bearbeiten',
onClick: function () {
dojo.byId('clickedItemIdx').value = idx + '';
if (reports.entries[idx].type == 'Rufbereitschaft') {
dojo.byId('addOrEditEntry_OCD_btn').click();
} else {
dojo.byId('addOrEditEntry_ASS_btn').click();
}
}
});
}
},
note that my data contains an idx-field which i commit to the onclick-function in order to know which element was clicked. This is the only way i got this to work.
As you may know, you can add multiple of those structure-elements referring to the same field.