multi column headers iggrid with knockout issue - javascript

I am using IgniteUI to place a grid on a site. The grid is bound using KnockoutJS. I've used this setup for many grids in my application, but I've run into a weird issue.
I need to have a grid that has a multi column header, and another column after that. I've used multi column headers and they work fine. In this instance, though, the grid will place the info from the column that should be after the group into the first column of the group, as seen in this fiddle: http://jsfiddle.net/rc5a4vbs/3/. The ColumnY column should have a bunch of Ys in it on both rows, as seen in the Javascript.
function ViewModel() {
var self = this;
self.value = ko.observable(false);
self.data = ko.observableArray([
{ "ColumnA": ko.observable(1), "ColumnD": ko.observable(21), "ColumnE": ko.observable("dkifhugb"),
"ColumnF": ko.observable(true), "ColumnG": ko.observable("false"),
"Procedure": ko.observable("Final Column"),
"TestConditionGroup": {
"ColumnY": ko.observable("YYYYYYYYYYYY"), "ColumnZ": ko.observable("ZZZZZZZZZ")
}
},
{ "ColumnA": ko.observable(2), "ColumnD": ko.observable(14), "ColumnE": ko.observable("5347347"),
"ColumnF": ko.observable(false), "ColumnG": ko.observable("string"),
"Procedure": ko.observable("Final Column"),
"TestConditionGroup": {
"ColumnY": ko.observable("yyyyyyyyyyyy"), "ColumnZ": ko.observable("zzzzzzzzzzz")
}
}
]);
self.getColumns = function() {
//debugger;
var columns = [
{ key: 'ColumnA', headerText: '', width: '0px', hidden: true },
{ key: 'ColumnD', headerText: 'Sequence', width: '100px' },
{ key: 'ColumnE', headerText: 'Iteration', width: '100px' },
{ key: 'ColumnF', headerText: 'Risk', width: '100px' },
{ key: 'ColumnG', headerText: 'Sub-Chapter Title', width: '200px' }
];
var columns2 = [
{ key: 'TestConditionGroup', headerText: 'ColumnY', width: '100px',
formatter: function(val){
return val[this.headerText];
}
},
{ key: 'TestConditionGroup', headerText: 'ColumnZ', width: '100px',
formatter: function(val){
return val[this.headerText];
}
}
];
columns.push({ key: 'TestConditionGroup', headerText: 'Test Conditions', group: columns2 });
columns.push({ key: 'Procedure', headerText: 'Procedure', width: '200px'});
return columns;
}
}
The grid was working correctly until I made the data that is in the grouped columns into an object within the row object. This is how the server will be sending me the info. When all of the columns were on the top level it worked fine.
How can I get the data to appear correctly with this set up for the supplied data? Any help would be greatly appreciated.

The problem is not caused by the Multi-Column Headers, but from the complex object property that you have in your data.
If you want to bind the ColumnY and ColumnZ then you have to declare them as unbound columns and in the formatter function extract their values from the TestConditionGroup property of the data. You do that by using a second parameter in the formatter function which will give you a reference to the current row data.
var columns2 = [
{ key: 'ColumnY', headerText: 'ColumnY', width: '100px', unbound: true,
formatter: function(val, row){
return row["TestConditionGroup"][this.headerText];
}
},
{ key: 'ColumnZ', headerText: 'ColumnZ', width: '100px', unbound: true,
formatter: function(val, row){
return row["TestConditionGroup"][this.headerText];
}
}];
Also to make the data from the TestConditionGroup column available in the formatter functions you have to configure localSchemaTransform to false.
The last thing you need to do is to set the autoGenerateColumns to false, because it's true by default.
Here is the link to the updated fiddle:
http://jsfiddle.net/rc5a4vbs/4/

Related

VueGoodTable filter dropdown options vue2

I'm trying to populate possible dropdown options on vue good table. The idea is that I conduct an API call to the server to bring back what values can possibly go into the drop down and I'm trying to assign it to the column filter. However, I can't seem to get it to work.
<vue-good-table
:paginationOptions="paginationOptions"
:sort-options="sortOptions"
:isLoading.sync="isTableLoading"
:rows="rows"
:columns="columns"
:lineNumbers="true"
mode="remote"
:totalRows="totalRecords"
#on-row-click="onRowClick"
#on-page-change="onPageChange"
#on-sort-change="onSortChange"
#on-column-filter="onColumnFilter"
#on-per-page-change="onPerPageChange"
#on-search="onSearch"
:search-options="{
enabled: true
}"
styleClass="vgt-table striped bordered"
ref="table"
>
Fairly standard vgt set up.
columns: [
{
label: 'some column',
field: 'column1'
},
{
label: 'Customer',
field: 'customerName',
filterOptions: {
enabled: true,
placeholder: 'All',
filterDropdownItems: Object.values(this.customers)
}
},
{
label: 'other columns',
field: 'column234'
}
]
and the API call
methods: {
async load () {
await this.getTableOptions()
},
async getTableOptions () {
try {
var response = await axios.get(APICallUrl)
this.customers= []
for (var i = 0; i < response.data.customers.length; i++) {
this.customers.push({ value: response.data.customers[i].customerId, text: response.data.customers[i].customerName})
}
} catch (e) {
console.log('e', e)
}
}
The only thing that I thought of is that the table has finished rendering before the load method is complete. However just creating a static object in my data and assigning it to a filterDropDownItems yielded no better results. The result whenever I try to set it to an object is that the box is a type-in box rather than a dropdown.
You can make the table update after it's rendered by making columns a computed property. The other problem you have is this.customers is an Array but Object.values() expects an Object. You could use the Array.map function instead
this.customers.map(c => c.value)
Although according to the VueGoodTable docs an array of objects like you have should work just fine
computed: {
columns() {
return [
{
label: 'some column',
field: 'column1'
},
{
label: 'Customer',
field: 'customerName',
filterOptions: {
enabled: true,
placeholder: 'All',
filterDropdownItems: this.customers
}
},
{
label: 'other columns',
field: 'column234'
}
];
}
}

Store calculated data in Column of Kendo Grid

What I'm trying to do is store some data in a specific column that is calculated by using the data from another column.
I currently have a function that returns the number of available licenses for the given Id in JSON
function getAvailableLicenses(id) {
var url = "/Host/Organization/AvailableLicenses/" + id;
$.get(url, function (data) {
return data.AvailableLicenses;
});
}
How do I go about storing this number in a column named "AvailableLicenses"?
Here is my current Grid:
$("#OrganizationGrid").kendoGrid({
dataSource: viewModel.get("orgDataSource"),
filterable: {
extra: false
},
sortable: true,
pageable: true,
columns: [
{ field: "Id", hidden: true },
{ field: "Name", template: "<a href='/Host/Organization/Detail/#:Id#'>#:Name#</a>" },
{ field: "LicenseNumber", title: "Number of Licenses" },
{ field: null, title: "Available Licenses", template: "#= getAvailableLicenses(Id) #" },
{ field: "LicenseExpiration", title: "License Expiration", format: "{0:MM/dd/yyyy}" },
{ field: "State" },
{ field: "Active" }
],
editable: false
});
As you can see, I tried to create a null column with a template that calls the function for the given Id.
By using Fiddler I can see that the function is indeed being called for all of the rows, but the AvailableLicenses column just displays Undefined for every row.
Is there something I'm missing here to get this to work?
I think the better way to do this is on dataSource parse() function
First: you column configuration must change like this:
{ field: "AvalableLicenses", title: "Available Licenses" },
You alaways can use you template .
And second, inside your dataSource() you can add:
schema: {
parse: function(response) {
for (var i = 0; i < response.length; i++) {
response[i].AvalableLicenses= null;
response[i].AvalableLicenses = getAvailableLicenses(response[i].Id)
}
return response;
}
}
EDIT:
If you prefer using you way, I dont see any problem in your configuration, probably your $.get is returning undefined, or something you don't expect.
For conviniance I did an example working.
http://jsfiddle.net/jwocf897/
Hope this help

How can I set a max-width property to individual kendo grid columns?

Here's a jsfiddle with a sample kendo grid:
http://jsfiddle.net/owlstack/Sbb5Z/1619/
Here's where I set the kendo columns:
$(document).ready(function () {
var grid = $("#grid").kendoGrid({
dataSource: {
data: createRandomData(10),
schema: {
model: {
fields: {
FirstName: {
type: "string"
},
LastName: {
type: "string"
},
City: {
type: "string"
},
Title: {
type: "string"
},
BirthDate: {
type: "date"
},
Age: {
type: "number"
}
}
}
},
pageSize: 10
},
height: 500,
scrollable: true,
sortable: true,
selectable: true,
change: onChangeSelection,
filterable: true,
pageable: true,
columns: [{
field: "FirstName",
title: "First Name",
width: "100px",
}, {
field: "LastName",
title: "Last Name",
width: "100px",
}, {
field: "City",
width: "100px",
}, {
field: "Title",
width: "105px"
}, {
field: "BirthDate",
title: "Birth Date",
template: '#= kendo.toString(BirthDate,"MM/dd/yyyy") #',
width: "90px"
}, {
field: "Age",
width: "50px"
}]
}).data("kendoGrid");
applyTooltip();
});
I added individual widths to each of the columns. However, I would also like to set a max-width to each kendo grid column. Is it possible to set a max-width (not a set one for all columns but max-width for individual columns?)?
There is no property like max-width exposed for kendo grid. One of the round about solution will be to set Width of Column as autosize and then use template where you use css properties to maintain max width.
Following is the example. Not sure if you are looking for the same answer.
<style>.intro { max-width: 100px ;width: 20px ;background-color: yellow;</style>
<script>
$(document).ready(function () {
$("#grid").kendoGrid({
dataSource: {
type: "odata",
transport: {
read: "http://demos.telerik.com/kendo-ui/service/Northwind.svc/Customers"
},
pageSize: 20
},
height: 550,
groupable: true,
sortable: true,
pageable: {
refresh: true,
pageSizes: true,
buttonCount: 5
},
columns: [{
field: "ContactName",
title: "Contact Name",
template : '<span class=intro>#:ContactName#</span>'
}, {
field: "ContactTitle",
title: "Contact Title"
}, {
field: "CompanyName",
title: "Company Name"
}, {
field: "Country",
width: 150
}]
});
});
</script>
I have JavaScript code that will help achieve this functionality. It provides a way for a (command) column to retain a fixed size.
On the one hand, it acts as a minimum size for a column, but if the grid were to increase in size, the original size is enforced. This allows for other grid cells to increase in size by a larger amount.
Check out this dojo that shows this code in action.
Note that all columns need to have a specified width (this effectively becomes a minimum width).
The browser resize event is used to trigger the size recalculation of the grids.
This function supports master/detail and grouping.
I have not considered column resizing at this point.
If you have any ideas for improvements then go for it!
.k-grid td
white-space: nowrap;
text-overflow: ellipsis;
max-width:150px;
}
YES....
You can set a textbox-style MAX WIDTH for column values in a Kendo Grid...and you can do a lot more too. However, you must do so by-hand using the grids Before Edit event.
FOR EXAMPLE:
// ELEMENTS
var $gridDiscipline = $('#gridDiscipline')
var grid = $gridDiscipline.kendoGrid();
// EVENTS
grid.bind('beforeEdit', beforeEdit);
// IMPLEMENTATION
function beforeEdit(e)
{
// Edit Master Row
// This event raises when ADD_NEW or EDIT is pressed
// Optionally, I add a slight timeout to ensure the MASTER ROW is "open"
// Obviously, the elements in your row will be different from mine
// As you will see...you can do all kinds of late-bound configuration practices here (whatever makes sense for you)
setTimeout(function () {
var $container = $('.k-master-row', e.sender.tbody);
// Elements
var $cmbContactEmployee = $container.find("#EmployeeId");
var $txtDisciplineName = $container.find("#DisciplineName");
var $txtTags = $container.find("#Tags");
// Instances
var cmbContactEmployee = $cmbContactEmployee.data('kendoComboBox');
// Configuration
cmbContactEmployee.dataSource.transport.options.read.data = function (e) {
var text = e.filter.filters[0].value;
return { searchText: text };
};
// If a DataAnnotation value exists...you can enforce it
var maxlength = $txtDisciplineName.attr('data-val-length-max');
if (maxlength) {
$txtDisciplineName.attr('maxlength', maxlength);
}
// Or you can set something outright
$txtTags.attr('maxlength', 100);
}, 500);
}

dynamically hide ExtJS 4 grid column when grouping on column

I have an ExtJS 4 gridPanel as below:
var grid = Ext.create('Ext.grid.Panel', {
store: store,
title: 'grid',
columns: [
{text: 'column 1', sortable: true, width: 70, dataIndex: 'column1'},
{text: 'column 2', sortable: true, width: 70, dataIndex: 'column2'},
{text: 'column 3', sortable: true, width: 70, dataIndex: 'column3'},
{text: 'column 4', sortable: true, width: 70, dataIndex: 'column4'},
],
renderTo: Ext.get('gridDiv'),
width: 800,
height: 600,
listeners: {
},
features: [{
ftype: 'grouping'
}]
});
I'm required to dynamically hide the column that the grid is being grouped by whenever the grid is grouped, and show all other columns - reshowing any previously hidden columns.
I understand that this would be a listener handler that catches the grouping changing, which would then fire column.hide().
Some pseudo-code:
...
listeners: {
groupchange: function(store, groupers, eOpts) {
groupers.forEach(function(group) {
grid.columns.forEach(function(column) {
if(column==group)
column.hide();
if(column==group)
column.show();
}
}
}
},
...
I'm not sure from the API documentation whether groupchange is an event that can be caught by the grid, rather than the store, how to access the contents of groupers, and what comparators are available to determine if a column is in groupers.
How should this listener event be structured? What are the correct events/methods/properties to use?
Found that the hiding is done on a store listener, despite the grid panel API making it look as though it can be done as a grid listener. I've worked off the first keys array value within the groupers, although you could just as easily use items[0].property value.
var store = Ext.create('Ext.data.Store', {
...
listeners:
{
groupchange: function(store, groupers, eOpts){
var column = groupers.keys[0];
productionItemGrid.columns.forEach(function(entry){
if(entry.dataIndex == column)
{
entry.setVisible(false);
}
else
{
entry.setVisible(true);
}
});
}
}
...
});

Checkbox Selection + Grid Panel with Summary Row in ExtJS 4

given the following Grid Panel:
Ext.onReady(function() {
var sm = new Ext.selection.CheckboxModel( {
listeners:{
selectionchange: function(selectionModel, selected, options){
// Bunch of code to update store
}
}
});
var grid = Ext.create('Ext.grid.Panel', {
features: [{
ftype: 'summary'
}],
store: store,
defaults: { // defaults are applied to items, not the container
sortable:true
},
selModel: sm,
columns: [
{header: 'Column 1', flex: 1, dataIndex: 'd1', summaryRenderer: function(value, summaryData, dataIndex) { return "Selected Data"} },
{header: 'C2', width: 150, dataIndex: 'd2', summaryType: 'sum'},
{header: 'C3', width: 150, dataIndex: 'd3', renderer: dRenderer, summaryType: 'sum'},
{header: 'C4', width: 150, dataIndex: 'd4', renderer: dRenderer, summaryType: 'sum'},
{header: 'C5', width: 150, renderer: total, summaryRenderer: grandTotal}
],
width: "100%",
title: 'Grid',
renderTo: 'grid',
viewConfig: {
stripeRows: true
}
});
});
How does this need to be refactored to summarize the data of only those rows that are selected? I know that I probably need to override the sum function of the Summary Feature, but haven't been able to find an example or proper syntax in the docs.
Thanks!
Here was my attempt at a solution (posted on the Sencha 4.x help forum w/ no responses):
Ext.define('Ext.grid.feature.SelectedSummary', {
extend: 'Ext.grid.feature.Summary',
alias: 'feature.selectsummary',
generateSummaryData: function(){
var me = this,
data = {},
store = me.view.store,
selectedRecords = me.view.selModel.selected,
columns = me.view.headerCt.getColumnsForTpl(),
i = 0,
length = columns.length,
fieldData,
key,
comp;
for (i = 0, length = columns.length; i < length; ++i) {
comp = Ext.getCmp(columns[i].id);
val = 0;
for(j = 0, numRecs = selectedRecords.items.length; j < numRecs; j++ ) {
field = columns[i].dataIndex;
rec = selectedRecords.items[j];
console.log(rec.get(field));
val += rec.get(field);
}
data[comp.dataIndex] = val;//me.getSummary(store, comp.summaryType, comp.dataIndex, false);
}
return data;
}
});
But this only renders correctly on initial render and only renders when I step through the code with a debugger. There's some kind of race condition where the row gets rendered without the calculations being completed.
Any ideas? Is my question too specific? Perhaps just simple pointers on writing my own feature and adding it to a Grid Panel.
Solution from a colleague:
The summary feature has the capability to take a function as the summaryType. Because the column config does not get passed to this function by default you need to define a closure that will hold on to the dataIndex.
var sm = new Ext.selection.CheckboxModel( {
listeners:{
selectionchange: function(selectionModel, selected, options){
// Must refresh the view after every selection
myGrid.getView().refresh();
// other code for this listener
}
}
});
var getSelectedSumFn = function(column){
return function(){
var records = myGrid.getSelectionModel().getSelection(),
result = 0;
Ext.each(records, function(record){
result += record.get(column) * 1;
});
return result;
};
}
// create the Grid
var myGrid = Ext.create('Ext.grid.Panel', {
autoScroll:true,
features: [{
ftype: 'summary'
}],
store: myStore,
defaults: { // defaults are applied to items, not the container
sortable:true
},
selModel: sm,
columns: [
{header: 'h0', flex: 1, dataIndex: 'groupValue'},
{header: 'h1', width: 150, dataIndex: 'd1', summaryType: getSelectedSumFn('d1')},
{header: 'h2', width: 150, dataIndex: 'd2', renderer: r, summaryType: getSelectedSumFn('d2')},
{header: 'h3', width: 150, dataIndex: 'd3', renderer: r, summaryType: getSelectedSumFn('d3')},
{header: 'h4', width: 150, dataIndex: 'd4', renderer: r, summaryType: getSelectedSumFn('d4')}
],
width: "100%",
height: "300",
title: 'Data',
renderTo: 'data',
viewConfig: {
stripeRows: true
}
});
A little bit updated previous solution:
var sm = new Ext.selection.CheckboxModel({
listeners: {
selectionchange: function (selectionModel, selected, options) {
// Must refresh the view after every selection
selectionModel.view.refresh();
}
}
});
var getSelectedSumFn = function (column, selModel) {
return function () {
var records = selModel.getSelection(),
result = 0;
Ext.each(records, function (record) {
result += record.get(column) * 1;
});
return result;
};
};
and then in grid use following line:
summaryType: getSelectedSumFn('d4', sm)}

Categories

Resources