I am using a Telerik Grid to display data to the client. I have to show priority values first and then display non priority values. When the user group base on priority, the priority values should group first follow by the non priority group. I have a default descending grouping. When the user first access the page, it works fine. However, if the user remove the default grouping and try to group that column again, the non priority values are shown in the first group following by the priority which is the opposite of what I want.
In addition, I tried to do it on the client side using jquery, but the grid variable is always return null.
$(function () {
var grid = $('#Shipping').data('tGrid);
alert(grid) // always return null.
});
Here is the client side code that I am using for that column.
#(Html.Telerik().Grid(Model)
.Name("Shipping")
.DataKeys(Keys =>
{
Keys.Add(c => c.ShippingID);
})
.DataBinding(databinding => databinding.Server())
.Columns(columns =>
{
columns.Bound(p => p.Priority)
.Title("Priority")
.HtmlAttributes(new { style = "text-align:left" })
.Width(50)
.Filterable(false)
.Sortable(true)
.Groupable(true) // I can't tell it group and sort it descending.
.GroupHeaderTemplate(#<text>
.Groupable(grouping => grouping.Groups(gr =>
{
//Here I can tell it that I want to sort it descending
gr.Add("Priority", typeof(Boolean), System.ComponentModel.ListSortDirection.Descending);
}))
Please help me or give me a hint on how to fix this issue?
Adding a client side event fixed the issue, it was the reason why the grid was always showing null value in the JQuery function. The Grouping is still an issue; however, the client agree that they could click on the sort button, and it will sort them properly.
Related
cy.get('.grid-content-container').find('.grid-row').its('length').should('be.visible')
is giving the correct length but not sure how to get the records from 'grid-content' in each row?
this is the html:
dom
You can use cy.each for looping through the items.
cy.get('.grid-content-container').find('.grid-row').each(gridRow => {
cy.wrap(gridRow).find('.grid-content').each((gridContent) => {
// do something with gridContent
})
})
I have a grid that shows a list of financial arrears grouped by the type of debt, known as Cash Type. These Cash Types can be categorised in a number of ways, or not, which is "Normal". When they are categorised I'm adding a lovely badge in the ClientGroupHeaderTemplate so it stands out to the user. All good so far.
#(Html.Kendo().Grid(Model.Arrears)
.Name("arrearsGrid-" + Model.LeaseId.ToString())
.HtmlAttributes(new { #class = "smallergrid" })
.Columns(columns =>
{
columns.Bound(p => p.InvoiceNumber)
.ClientTemplate("<a class=\"text-primary\" href=\"" + Url.Action("Invoice", "Arrear") + $"/#=InvoiceNumber#?buildingid=#=BuildingId#&leaseid={Model.LeaseId}\" target=\"_blank\">#=InvoiceNumber#</a>");
columns.Bound(p => p.InvoiceDescription);
columns.Bound(p => p.CashType)
.ClientGroupHeaderTemplate("Cash Type: #= getCashTypeName(data.value) # (#= getDebtCategoryName(data.items[0].DebtCategory) #)");
columns.Bound(p => p.InvoiceDate);
columns.Bound(p => p.TransactionDate);
columns.Bound(p => p.DaysOverdue);
columns.Bound(p => p.InvoiceGross)
.HtmlAttributes(new { #class = "text-right" });
columns.Bound(p => p.OutstandingGross)
.HtmlAttributes(new { #class = "text-right" });
})
.Sortable()
.Excel(excel => excel
.FileName("Kendo UI Grid Export.xlsx")
.Filterable(true)
.AllPages(true)
)
.DataSource(dataSource => dataSource
.Ajax()
.ServerOperation(false)
.Group(g => g.Add(p => p.CashType))
)
.NoRecords(x => x.Template("<div class='empty-grid'></div>"))
)
The problem is the Export to Excel function does no processing on the ClientGroupHeaderTemplate, and shows<span>Cash Type: Rent <span class="d-print-none badge debt-category-1 ml-2">In Query</span></span> in the spreadsheet.
Here's the options I see are available, and some I've discounted. Are these really the best options I have?
CSS (rejected)
As you can see, using the Bootstrap d-print-none does nothing. From all the posts on Export and hidding columns, it seems that Kendo is in no way using a print view of the page, so #media options aren't going to help.
excelExport event
Use the Kendo excelExport event to customise the generated Excel workbook, however, this example is document creation. If I'm going this route I suspect it might be easier to write one from scratch, rather than workaround Kendo limitations. Either way it's a lot of work to remove 1 line of HTML markup.
Use the ProxyURL
Under a Grids Excel object, you can set an endpoint to call when the Excel spreadsheet is created. This might give the chance to tweak a ready-to-save spreadsheet before it is saved. There's a demo of it here. The demo is wrong (the Controller name is not Grid, it's Excel_Export) and it doesn't work for me even when corrected and the standard .ToolBar(tools => tools.Excel()) back in. Maybe it's because I'm not AJAXing my data in, or maybe it's just as broken as the demo. Changing my grid to AJAX will is not beyond the realms of possibility, but it's not a small job either.
Edit: I got this to hit the event by also adding .ForceProxy(true) to the Excel definition. This isn't mentioned in the demo, and the statement that you need to add .ToolBar(tools => tools.Excel()) is also incorrect; you can fire the event from your own buttons that trigger the Excel export.
Give up and go simple
The last option I have is to give up on the badges and just have text. It's the quickest and surest option, but it's solving the problem by ignoring the problem.
So I managed this in yet another way. I'm sure there must be an MVC answer to this problem though.
I added an event to my Grid
#(Html.Kendo().Grid(Model)
...
.Events(e =>
{
e.ExcelExport("exportExcel");
}
...
)
and created a JavaScript middleware function to alter the Excel file before it is saved. Of course you can then do whatever JS allows you to do at this point.
In the code below are examples of setting a column width (4), removing HTML span markup, and converting a string to a number and adding a currency format to that number (7). The method of obtaining the currency symbol is not pretty, but it's a string inside some HTML markup, I don't think it can be pretty.
function exportExcel(e) {
var sheet = e.workbook.sheets[0];
var lastRow = sheet.rows[sheet.rows.length - 1];
var lastCell = lastRow.cells[lastRow.cells.length - 1];
var lastCellValue = lastCell.value.toString().replace(/<[^>]*>/, "").replace("</span>", "");
var currency = lastCellValue.substring(0, 1);
currency += "#,###,##0.00";
sheet.columns[4].autoWidth = false;
sheet.columns[4].width = 125;
$.each(sheet.rows, function (index, row) {
if (index > 0) {
if (row.cells[0] != undefined) {
if (row.cells[0].value != undefined) {
row.cells[0].value = row.cells[0].value.replace(/<[^>]*>/, "(").replace("</span>", ")");
}
}
if (row.cells[7] != undefined) {
if (row.cells[7].value != undefined) {
row.cells[7].value = row.cells[7].value.toString().replace(/<[^>]*>/, "").replace("</span>", "");
row.cells[7].value = Number(row.cells[7].value.toString().replace(/[^0-9\.-]+/g, ""));
row.cells[7].format = currency; // Why this prepends a backslash to the format I do not know
}
}
}
});
};
I'm trying to dynamically change (if it got clicked) a normal table header (which is a link) to another defined CSS class 'th.hilite'. This link simply sorts this column and the header should got highlighted every time a user sorts the list.
The view where the class in question should be changed, looks like this:
%table#mytable
%thead
%tr
%th= link_to 'Title', mytable_path(:sort => 'title'), :id => 'title_header'
My question is simply: How and where could I dynamically set the class to %th.hilite if the header is clicked?
You can bind it from the view directly:
%th{:class => ("hilite" if #sort == "title")}= link_to 'Movie Title'...
However, you will also have to declare the #sort instance variable in your controller, and set it to either title or release, according to which column you are sorting. (i.e. #sort = params[:sort])
Be careful not to put logic (even conditionals) in your views if you can find a way around it. In order to avoid this common mistake, you need to make good use of the params and session hashes and set appropriate variables in controllers
# In your view
%th{:class => #title_header}= link_to 'Title', my_path(:sort => 'title'), :id => 'title_header'
# In your controller
sort = params[:sort] || session[:sort]
if sort == 'title'
ordering = {:order => :title}
end
BTW as long as values of your param match column name - no need to code each one
def index
#by=params[:by]
#movies = Movie.order(params[:by]).all
end
Javascript or jquery is not required..
The following HAML will work
%th{:class=>('title' == #sortby)?'hilite':""}= link_to 'Movie Title', movies_path(:sort => 'title'), :id => 'title_header'
You should use JavaScript to do this. I think it's good idea for you to use jQuery instead of pure JavaScript. Here are some examples
http://api.jquery.com/click/
http://api.jquery.com/css/
I'm trying to set up a Master/Detail grid using the Grid control from Telerik's Extensions for ASP.NET MVC. My problem is setting up the server template.
The demo I'm following is the first one on this page, except I'm using Razor View Engine.
I have the grid displaying fine. The problem is that I cannot write any sort of a server template that doesn't throw a compiler error - aside from leaving it blank!
#(Html.Telerik().Grid(Model)
.Name("Grid")
.Columns(columns =>
{
columns.Bound(o => o.Date).Format("{0:MM/dd/yyyy}").Width(100);
columns.Bound(o => o.Title).Template(#<text> #item.Title</text>).Sortable(false);
columns.Bound(o => o.Publication).Width(120).Sortable(false);
})
.DetailView(detailView => detailView.Template(e =>
{
//Anything other than this comment will throw a compiler error
}))
.RowAction(row =>
{
// Expand initially the detail view of the first row
if (row.Index == 0)
{
row.DetailRow.Expanded = true;
}
})
.Sortable()
.Scrollable(scrolling => scrolling.Height(494)).Footer(false)
.ClientEvents(events => events.OnRowDataBound("onRowDataBound"))
)
See that comment "anything other than this comment..."? When I replace that with something like #<text> hello</text>, I get a compilation error:
CS1002: ; expected
That doesn't seem to make sense, but I humour myself and put a semicolon in like such #<text> hello</text>;. That gives me this error:
CS0201: Only assignment, call, increment, decrement, and new object expressions can be used as a statement
When I replace that with a portion of the template I really want, namely #<text><b>Slug</b>: #item.Slug</text>, I get the same errors; CS1002 with no semicolon, and CS0201 with a semicolon.
What am I missing here?
There are two ways you can approach this. If you just want to display some simple text and not really integrate any other components it would be the easiest to modify the code you have above to just do this:
.DetailView(detailView => detailView.Template(#<text>test</text>))
As you can see I removed the whole e => { ... } part and just put in #<text></text>.
However, if you want to look into getting more components in your detail view I think it would be better to look at the demo found here. Although the description mentions some WebForms code you don't need to worry, the rest is all in Razor :) It also explains things you have to keep in mind. One of the most important ones is that any components within the DetailTemplate will have to use { ... } as opposed to ( ... ) this is because you want to specifically call .Render(); (using the ( ... ) implicitly calls .Render but at the wrong point for these scenarios) at the end of those components' declaration to make sure they are all rendered correctly.
Can someone please throw some light on how to go about rendering an hyperlink in the cells of a particular column in ExtJS?
I have tried binding the column to a render function in my JS, from which I send back the html:
SELECT
However, with this, the problem is that, once I hit the controller through the link, the navigation is successful, but subsequent navigations to the data-grid show up only empty records.
The records get fetched from the DB successfully through the Spring MVC controller, I have checked.
Please note that this happens only once I use the row hyperlink in the extJS grid to navigate away from the grid. If I come to the grid, and navigate elsewhere and again come back to the grid, the data is displayed fine.
The problem only occurs in case of navigating away from the grid, using the hyperlink rendered in one/any of the cells.
Thanks for your help!
This is for ExtJS 4 and 5.
Use a renderer to make the contents look like a link:
renderer: function (value) {
return ''+value+'';
}
Then use the undocumented, dynamically generated View event cellclick to process the click:
viewConfig: {
listeners: {
cellclick: function (view, cell, cellIndex, record, row, rowIndex, e) {
var linkClicked = (e.target.tagName == 'A');
var clickedDataIndex =
view.panel.headerCt.getHeaderAtIndex(cellIndex).dataIndex;
if (linkClicked && clickedDataIndex == '...') {
alert(record.get('id'));
}
}
}
}
Try something like this:
Ext.define('Names', {
extend: 'Ext.data.Model',
fields: [
{ type: 'string', name: 'Id' },
{ type: 'string', name: 'Link' },
{ type: 'string', name: 'Name' }
]
});
var grid = Ext.create('Ext.grid.Panel', {
store: store,
columns: [
{
text: 'Id',
dataIndex: 'Id'
},
{
text: 'Name',
dataIndex: 'Name',
renderer: function (val, meta, record) {
return '' + val + '';
}
}
...
...
...
However my thanks to - ExtJS Data Grid Column renderer to have multiple values
Instead of using an anchor tag, I would probably use plain cell content styled to look like an anchor (using basic CSS) and handle the cellclick event of the GridPanel to handle the action. This avoids dealing with the anchor's default click behavior reloading the page (which is what I'm assuming is happening).
I created a renderer so it looked like you were clicking on it.
aRenderer: function (val, metaData, record, rowIndex, colIndex, store){
// Using CellClick to invoke
return "<a>View</a>";
},
But I used a cell event to manage the click.
cellclick: {
fn: function (o, idx, column, e) {
if (column == 1) // Doesn't prevent the user from moving the column
{
var store = o.getStore();
var record = store.getAt(idx);
// Do work
}
}
}
For these purposes I use CellActions or RowActions plugin depending on what I actually need and handle cell click through it.
If you want something that looks like an anchor, use <span> instead and do what #bmoeskau suggested.
You can use 'renderer' function to include any HTML you want into cell.
Thanks guys for your response.
AFter debugging the extJS-all.js script, I found the issue to be on the server side.
In my Spring MVC controller, I was setting the model to the session, which in the use-case I mentioned earlier, used to reset the "totalProperty" of Ext.data.XmlStore to 0, and hence subsequent hits to the grid, used to display empty records.
This is because, ext-JS grid, checks the "totalProperty" value, before it even iterates through the records from the dataStore. In my case, the dataStore had data, but the size was reset to null, and hence the issue showed up.
Thanks to all again for your inputs!