I'm trying to play around with various admin templates and ran on to an old Bootstrap 3 one which has jqGrid support. While the demo is working great but it uses the commercial version and not the free jqGrid.
In the link to the repository of source of the demo here (Ace Admin Template), the main file is call jqgrid.html, if I use the most recent free jqGrid as shown below, then the attributes of the button images are no longer working. See the attached pictures.
Tests with commercial jqGrid:
Tests with free jqGrid
I replace the below lines
<script src="assets/js/jquery.jqGrid.min.js"></script>
by these one
<link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/css/bootstrap.min.css">
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/4.7.0/css/font-awesome.css">
<link rel="stylesheet" href="https://rawgit.com/free-jqgrid/jqGrid/master/css/ui.jqgrid.min.css ">
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/jqueryui/1.11.4/themes/redmond/jquery-ui.min.css">
<script src="https://rawgit.com/free-jqgrid/jqGrid/master/js/jquery.jqgrid.min.js"></script>
So my question is what is the new code I should replace to fix this, since the below code is called in beforeShowForm?
//update buttons classes
var buttons = form.next().find('.EditButton .fm-button');
buttons.addClass('btn btn-sm').find('[class*="-icon"]').hide();//ui-icon, s-icon
buttons.eq(0).addClass('btn-primary').prepend('<i class="ace-icon fa fa-check"></i>');
buttons.eq(1).prepend('<i class="ace-icon fa fa-times"></i>')
With the premium version (Guriddo jqGrid JS - v5.0.2 - 2016-01-18), it works like a charm, see working premium images and free jqGrid images, but when I switched to free jqGrid, the buttons text are not working making hard to read action texts.
This great admin template is a nice add-on to free jQgrid to complete my side project. Not sure where to buy it since it is no longer available for purchase Ace Admin Template Info.
Updated
I still have one small display issue on the header, below is the screen shot
I used one of your demo code so you can reproduce it.
<script type="text/javascript">
jQuery(function($) {
var grid_selector = "#grid-table";
var pager_selector = "#grid-pager";
var parent_column = $(grid_selector).closest('[class*="col-"]');
//resize to fit page size
$(window).on('resize.jqGrid', function () {
$(grid_selector).jqGrid( 'setGridWidth', parent_column.width() );
})
//resize on sidebar collapse/expand
$(document).on('settings.ace.jqGrid' , function(ev, event_name, collapsed) {
if( event_name === 'sidebar_collapsed' || event_name === 'main_container_fixed' ) {
//setTimeout is for webkit only to give time for DOM changes and then redraw!!!
setTimeout(function() {
$(grid_selector).jqGrid( 'setGridWidth', parent_column.width() );
}, 20);
}
})
//if your grid is inside another element, for example a tab pane, you should use its parent's width:
/**
$(window).on('resize.jqGrid', function () {
var parent_width = $(grid_selector).closest('.tab-pane').width();
$(grid_selector).jqGrid( 'setGridWidth', parent_width );
})
//and also set width when tab pane becomes visible
$('#myTab a[data-toggle="tab"]').on('shown.bs.tab', function (e) {
if($(e.target).attr('href') == '#mygrid') {
var parent_width = $(grid_selector).closest('.tab-pane').width();
$(grid_selector).jqGrid( 'setGridWidth', parent_width );
}
})
*/
$.jgrid.icons.aceFontAwesome = $.extend(true, {},
$.jgrid.icons.fontAwesome,
{
nav: {
add: "fa-plus-circle",
view: "fa-search-plus",
},
actions: {
save: "fa-check",
cancel: "fa-times"
},
pager: {
first: "fa-angle-double-left",
prev: "fa-angle-left",
next: "fa-angle-right",
last: "fa-angle-double-right"
},
form: {
prev: "fa-angle-left",
next: "fa-angle-right",
save: "fa-check",
cancel: "fa-times"
}
}
);
$.jgrid.icons.aceFontAwesome = $.extend(true, {},
$.jgrid.icons.fontAwesome,
{
nav: {
add: "fa-plus-circle",
view: "fa-search-plus",
},
actions: {
save: "fa-check",
cancel: "fa-times"
},
pager: {
first: "fa-angle-double-left",
prev: "fa-angle-left",
next: "fa-angle-right",
last: "fa-angle-double-right"
},
form: {
prev: "fa-angle-left",
next: "fa-angle-right",
save: "fa-check",
cancel: "fa-times"
}
}
);
var data = [
{code:"A", name:"Project A",
jan2017:1, feb2017:0, mar2017:0, apr2017:0,
may2017:0, jun2017:0, jul2017:0, aug2017:0,
sep2017:0, oct2017:0, nov2017:0, dec2017:1},
{code:"A", name:"Project A",
jan2017:1, feb2017:1, mar2017:0, apr2017:0,
may2017:1, jun2017:0, jul2017:0, aug2017:0,
sep2017:0, oct2017:1, nov2017:0, dec2017:0}
],
intTemplate = {
width: 20, template: "integer",
align: "center", editable: true
};
jQuery(grid_selector).jqGrid({
colModel: [
{ name: "code", label: "Code", width: 50, align: "center" },
{ name: "name", label: "Name", width: 70 },
{ name: "jan2017", label: "Jan", template: intTemplate },
{ name: "feb2017", label: "Feb", template: intTemplate },
{ name: "mar2017", label: "Mar", template: intTemplate },
{ name: "apr2017", label: "Apr", template: intTemplate },
{ name: "may2017", label: "May", template: intTemplate },
{ name: "jun2017", label: "Jun", template: intTemplate },
{ name: "jul2017", label: "Jul", template: intTemplate },
{ name: "aug2017", label: "Aug", template: intTemplate },
{ name: "sep2017", label: "Sep", template: intTemplate },
{ name: "oct2017", label: "Oct", template: intTemplate },
{ name: "nov2017", label: "Nov", template: intTemplate },
{ name: "dec2017", label: "Dec", template: intTemplate },
{ name: "jan2018", label: "Jan", template: intTemplate },
{ name: "feb2018", label: "Feb", template: intTemplate },
{ name: "mar2018", label: "Mar", template: intTemplate },
{ name: "apr2018", label: "Apr", template: intTemplate },
{ name: "may2018", label: "May", template: intTemplate },
{ name: "jun2018", label: "Jun", template: intTemplate },
{ name: "jul2018", label: "Jul", template: intTemplate },
{ name: "aug2018", label: "Aug", template: intTemplate },
{ name: "sep2018", label: "Sep", template: intTemplate },
{ name: "oct2018", label: "Oct", template: intTemplate },
{ name: "nov2018", label: "Nov", template: intTemplate },
{ name: "dec2018", label: "Dec", template: intTemplate }
],
cmTemplate: { autoResizable: true },
autoResizing: { compact: true },
viewrecords: true,
data: data,
iconSet: "fontAwesome",
rownumbers: true,
sortname: "invdate",
sortorder: "desc",
pager: true,
iconSet: "aceFontAwesome", //"fontAwesome",
grouping: true,
rowNum: 10,
rowList: [5, 10, 20, "10000:All"],
groupingView: {
groupField: ["name"],
groupText: ["<b>{0}</b>"]
},
loadComplete : function() {
var table = this;
var parent_column = $(grid_selector).closest('[class*="col-"]');
setTimeout(function(){
$(grid_selector).jqGrid( 'setGridWidth', parent_column.width() );
}, 0);
},
sortname : 'invid',
inlineEditing: {
keys: true
},
navOptions: {
add: false,
edit: false,
del: false,
search: false
},
inlineNavOptions: {
add: true,
edit: true
},
caption: "Test"
}).jqGrid("navGrid")
.jqGrid("inlineNav")
.jqGrid("rotateColumnHeaders",
["jan2017", "feb2017", "mar2017", "apr2017", "may2017", "jun2017",
"jul2017", "aug2017", "sep2017", "oct2017", "nov2017", "dec2017",
"jan2018", "feb2018", "mar2018", "apr2018", "may2018", "jun2018",
"jul2018", "aug2018", "sep2018", "oct2018", "nov2018", "dec2018"])
.jqGrid('setGroupHeaders', {
useColSpanStyle: true,
groupHeaders: [
{ startColumnName: 'code', numberOfColumns: 2, titleText: '<i>Project</i>' },
{ startColumnName: 'jan2017', numberOfColumns: 12, titleText: '2017' },
{ startColumnName: 'jan2018', numberOfColumns: 12, titleText: '2018' }
]
});
});
I replaced the above code in jqgrid.html. I don't know what really causes it. Could it be rotateColumnHeaders which breaks it?
Pic shows moving code after setgroupheader. The vertical lines are still cut.
More updates
After investigation and trial by error, I found out the issue but it masks an another one, I no longer have header issues but the buttons do not display nicely. Is there anyway to overwrite the css to make them look like the one without using the line : guistyle:bootstrap, seems like jqueryUI is conflicting some how with ace css.
Fix header, by adding : guiStyle: "bootstrap", action buttons do not look good. Blue color header is also gone along with button colors
Removing guiStyle: "bootstrap" breaks header, blue color header, action button look nicely
I've tried to reproduce with jsfiddle but no luck yet.
I looked through the Ace Admin template. One can see that it's created fror old jqGrid, which don't supports Font Awesome and Bootstrap. Free jqGrid supports both (see here and here). One more wiki article describes how one can use other Font Awesome icons to create his own iconSet. For example, one can define
$.jgrid.icons.aceFontAwesome = $.extend(true, {},
$.jgrid.icons.fontAwesome,
{
nav: {
add: "fa-plus-circle",
view: "fa-search-plus",
},
actions: {
save: "fa-check",
cancel: "fa-times"
},
pager: {
first: "fa-angle-double-left",
prev: "fa-angle-left",
next: "fa-angle-right",
last: "fa-angle-double-right"
},
form: {
prev: "fa-angle-left",
next: "fa-angle-right",
save: "fa-check",
cancel: "fa-times"
}
}
);
to use some other icons as defaults (see here). After that one can use iconSet: "aceFontAwesome" option instead of iconSet: "fontAwesome" used typically.
All other CSS settings of Ace Admin template are just customization of the standard CSS. I personally find Ace Admin CSS very nice, but one needs to invest some time to make free jqGrid looks exactly like Ace Admin. One needs no jqGrid knowledge for that. It's enough to use Developer Tools of Chrome to examine CSS used on http://ace.jeka.by/jqgrid.html and to implement the same (or close) settings on free jqGrid. I created the demo http://jsfiddle.net/OlegKi/jj0qbhbt/ which shows how one can do that. One needs just expend CSS settings, which I included in the demo.
I want to color the available date in my calendar datepicker [26-11-2015, 28-11-2015, 30-11-2015] I select.
Can someone help me ?
$("#datepicker").datepicker({
inline: true,
dayNamesMin: [ "Dim", "Lun", "Mar", "Mer", "Jeu", "Ven", "Sam" ],
monthNames: [ "Janvier", "Février", "Mars", "Avril", "Mai", "Juin", "Juillet", "Auôt", "Septembre", "Octobre", "Novembre", "Décembre" ],
firstDay: 1,
minDate: 0,
beforeShowDay: function(date){
var day = date.getDay();
return [(day != +7)];
/* var string = jQuery.datepicker.formatDate('yy-mm-dd', date);
return [ array.indexOf(string) == -1 ];*/
},
closeText: 'Fermer',
// The hidden field to receive the date
altField: "#dateHidden",
prevText: '<',
nextText: '>',
// The format you want
altFormat: "yymmdd",
// The format the user actually sees
dateFormat: "yymmdd",
isRTL: false,
showMonthAfterYear: false,
yearSuffix: '',
onSelect: selectDate,
});
You can use CSS to change the colors.
Here is an example to change the color of available enabled dates:
.ui-datepicker .ui-state-default {
color: deepskyblue;
background: ghostwhite;
}
And for disabled dates:
.ui-datepicker td.ui-state-disabled>span {
color: tomato;
}
So I'm trying to display the number of rows (records) returned from a grid. Here's the code:
Ext.define('AM.view.user.List' ,{
extend: 'Ext.grid.Panel',
alias: 'widget.userlist',
title: '<center>Data Management</center>',
store: 'Users',
dockedItems: [{
xtype: 'toolbar',
dock: 'bottom',
items: [
{ xtype: 'tbtext', text: 'Number of Records\:' + ***code that will return number of records*** },
{ xtype: 'tbfill' },
{ text: 'Print' },
{ text: 'Export' }
]
}],
...
I'm not sure how to use the getCount() method to return the number of rows from this grid (or store?). Any ideas?
Heres: my store:
Ext.define('AM.store.Users', {
extend: 'Ext.data.Store',
model: 'AM.model.User',
fields: ['field1', 'field2', 'field3'],
data: [
{field1: 'Data 1', field2: 'Data 2', field3: 'Data 3'},
{field1: 'Data 1', field2: 'Data 2', field3: 'Data 3'},
{field1: 'Data 1', field2: 'Data 2', field3: 'Data 3'}
]
});
You won't be able to do this dynamically, where it updates on its own, so you will have to use a placeholder and update the panel when the store is loaded.
{ xtype: 'tbtext', itemId: 'numRecords' }
Then:
listeners: {
render: function(store) {
store.on('load', function(records) {
var count = records.length; //or store.getTotalCount(), if that's what you want
grid.down('#numRecords').setText('Number of Records: ' + count);
});
}
}
You can try to add a paging toolbar and use the properties displayInfo & displayMsg.
Try this code :
bbar : {
xtype : 'pagingtoolbar',
displayInfo: true,
store:'your store',
displayMsg : 'Total rows {2}'// defaultvalue = 'Displaying {0} - {1} of {2}'
}
Assuming your store is loaded before the grid is rendered, this will probably work:
Ext.data.StoreManager.lookup("Users").getCount();
If the store loads dynamically, you will need to attach an event to the store's load event to update your grid, comment if the above code does not work and I can probably help you.
As it has already been said, you have to wait for the store to be loaded before using getCount. Except in the case you'd really use a memory store like the one in your example, but I doubt you'd want to display a dynamic number of record for that use case...
So, you have to listen for the load event of the store and update your text item then. The load event will fire each time the store is loaded, reloaded, etc., which may occur multiple times if your grid is paged or allow for filtering, etc. That means that our number of records will be kept in sync with the actual content of the store. Good.
Now, how to install that listener? One very common place for putting that kind of treatment is in the initComponent method of your component.
Here's the code. See the comments for a crash course in overriding initComponent (see another answer for a lecture on the topic).
Ext.define('AM.view.user.List' ,{
extend: 'Ext.grid.Panel',
alias: 'widget.userlist',
title: '<center>ECRIS-MetaData Management</center>',
store: 'Users',
dockedItems: [{
xtype: 'toolbar',
dock: 'bottom',
items: [
// Give an itemId to this component to make it easy to
// reference later.
{ xtype: 'tbtext', text: 'Loading...', itemId: 'recordNumberItem' },
{ xtype: 'tbfill' },
{ text: 'Print' },
{ text: 'Export' }
]
}],
initComponent: function() {
// We're overriding an existing method, so that's very important to call
// the parent method, or the component will break in awful sufferings
this.callParent(arguments);
// I'm putting the code *after* callParent, so that the store is available
var store = this.getStore(),
// Using ComponentQuery to retrieve the text item
textItem = this.down('#recordNumberItem');
// Using `mon` instead of `on` for better memory management (the listener
// will be removed from the store automatically when the component is
// destroyed).
this.mon(store, 'load', function() {
// We're left with the easy part...
textItem.setText("Number of records: " + store.getCount());
});
}
// ...
});
I wanted to know how to customize the ListItem content by combining different JSON data fields.
I have three JSON fields: {caption},{subCaption},{source}.
So far, I have been able to use dataMap and use custom classes to wrap additional text and styling around each. However, the only way I have been able to add content is to do so sequentially with the use of apply/update functions. As a result, my ListItems are simply {caption},{subCaption},{source} in their own lines.
Here's how I would like each ListItem to look like:
Combine {caption} and {subCaption} text and create a short story and add this as a panel to the ListItem
Render {source} in a small panel docked at the bottom right of the panel created in step 1.
How can I do the above? The distilled question would be: How can I access and combine the data from different JSON fields and render into ListItem?
My current code for ListItem is copied below for reference.
As always, any help is greatly appreciated! Thanks!
Mohammad
San Jose, CA
Ext.define('qxtapp.view.ResultsListItem', {
extend: 'Ext.dataview.component.ListItem',
requires: [
'qxtapp.view.ResultsListItemCaption'
],
xtype : 'resultslistitem',
alias : 'widget.resultslistitem',
config: {
caption: true,
subCaption: true,
source: true,
dataMap: {
getCaption: {
setHtml: 'caption'
},
getSubCaption: {
setHtml: 'subCaption'
},
getSource: {
setHtml: 'source'
}
},
layout: {
type: 'vbox'
}
},
applyCaption: function(config) {
return Ext.factory(config, qxtapp.view.ResultsListItemCaption, this.getCaption());
},
updateCaption: function(newCaption) {
if (newCaption) {
this.add(newCaption);
}
},
applySubCaption: function(config) {
return Ext.factory(config, Ext.Component, this.getSubCaption());
},
updateSubCaption: function(newSubCaption) {
if (newSubCaption) {
this.add(newSubCaption);
}
},
applySource: function(config) {
return Ext.factory(config, Ext.Component, this.getSource());
},
updateSource: function(newSource) {
if (newSource) {
this.add(newSource);
}
}
});
Ext.define('qxtapp.view.ResultsListItemCaption', {
extend: 'Ext.Component',
applyHtml: function(caption) {
// do some customization to caption and return it
return caption;
}
});
I'm not sure why you need to go through all that trouble, why not use an item template in a simple list?
Ext.define('qxtapp.view.ResultsList', {
extend: 'Ext.dataview.List',
alias: 'widget.resultslist',
config: {
...
itemTpl: new Ext.XTemplate(
"<div class='result-item'>",
"<p class='result-story'>",
"{[this.getStoryHtml(values.caption, values.subCaption)]}",
"</p>",
"<img src='{source}' alt='{caption}' />",
"</div>",
{
// This is a new function on the template created above and can be called
// from within the template html
getStoryHtml: function(caption, subCaption) {
// customize the text to your needs, then return the html to insert
}
}
),
...
}
});
Of course, you would then need to style these items using CSS, but that should be the easy part. ;)