I'm building simple application using Ext Scheduler (http://www.bryntum.com/products/scheduler/).
I'm trying to add custom renderer for treecolumn that will have icon of person in tree.
I've created rendered for that column:
renderer: function (v, m, r) {
if (r.get('leaf')) {
return '<div style="float: left">' +
r.get('Name') +
'</div>'+
'<img data-qtip="<img src=iconUrl.png width=60px height=60px/>" '+
'src="iconUrl.png" '+
'width="20px" height="20px" style="float: right; margin-right: 5px"/>';
}
return v;
}
This looks OK, but for long names I get unwanted look:
Looking thrue code I found some unwanted markup:
What I need is almost identical renderer as in standard treecolumn, but I need to add extra icon on right of that column - employee image thumbnail.
I was searching over docs and I found out that there is private property called cellTpl (http://docs.sencha.com/extjs/4.2.1/source/Column2.html#Ext-tree-Column)
but I don't know how should I change this and should I change this at all because it is marked as private on top.
How should I change that renderer to get effect as on last image?
Here is some jsfiddle to play: http://jsfiddle.net/Misiu/gwFdr/
I ended extending Ext.tree.Column to instead of normal leaf icons it shows thumbnails.
Here's my code:
Ext.define('Grafik.component.tree.Column2', {
extend: 'Ext.tree.Column',
alias: 'widget.treecolumn2',
thumbnailsServiceUrl:'',
cellTpl: [
'<tpl for="lines">',
'<img src="{parent.blankUrl}" class="{parent.childCls} {parent.elbowCls}-img ',
'{parent.elbowCls}-<tpl if=".">line<tpl else>empty</tpl>"/>',
'</tpl>',
'<img src="{blankUrl}" class="{childCls} {elbowCls}-img {elbowCls}',
'<tpl if="isLast">-end</tpl><tpl if="expandable">-plus {expanderCls}</tpl>"/>',
'<tpl if="checked !== null">',
'<input type="button" role="checkbox" <tpl if="checked">aria-checked="true" </tpl>',
'class="{childCls} {checkboxCls}<tpl if="checked"> {checkboxCls}-checked</tpl>"/>',
'</tpl>',
'<img src="{loginUrl}" class="{childCls} {baseIconCls} ',
'{baseIconCls}-<tpl if="leaf">leaf<tpl else>parent</tpl> {iconCls}"',
'<tpl if="icon">style="background-image:url({icon})"</tpl>/>',
'<tpl if="href">',
'{value}',
'<tpl else>',
'<span class="{textCls} {childCls}">{value}</span>',
'</tpl>'
],
treeRenderer: function (value, metaData, record, rowIdx, colIdx, store, view) {
var me = this,
cls = record.get('cls'),
renderer = me.origRenderer,
data = record.data,
parent = record.parentNode,
rootVisible = view.rootVisible,
lines = [],
parentData;
if (cls) {
metaData.tdCls += ' ' + cls;
}
while (parent && (rootVisible || parent.data.depth > 0)) {
parentData = parent.data;
lines[rootVisible ? parentData.depth : parentData.depth - 1] =
parentData.isLast ? 0 : 1;
parent = parent.parentNode;
}
return me.getTpl('cellTpl').apply({
record: record,
baseIconCls: me.iconCls,
iconCls: data.iconCls,
icon: data.icon,
checkboxCls: me.checkboxCls,
checked: data.checked,
elbowCls: me.elbowCls,
expanderCls: me.expanderCls,
textCls: me.textCls,
leaf: data.leaf,
expandable: record.isExpandable(),
isLast: data.isLast,
blankUrl: Ext.BLANK_IMAGE_URL,
loginUrl: data.leaf ? me.thumbnailsServiceUrl + record.get('Login') : Ext.BLANK_IMAGE_URL,
href: data.href,
hrefTarget: data.hrefTarget,
lines: lines,
metaData: metaData,
// subclasses or overrides can implement a getChildCls() method, which can
// return an extra class to add to all of the cell's child elements (icon,
// expander, elbow, checkbox). This is used by the rtl override to add the
// "x-rtl" class to these elements.
childCls: me.getChildCls ? me.getChildCls() + ' ' : '',
value: renderer ? renderer.apply(me.origScope, arguments) : value
});
}
});
And usage looks like this:
{
text: 'Users',
dataIndex: 'Name',
hideable: false,
width: 260,
xtype: 'treecolumn2',
sortable: true,
resizable: false,
thumbnailsServiceUrl: window.appUrl + 'api/Thumbnails/'
}
Where api/Thumbnails is service that takes login and returns image.
Simple solution, hope it helps someone :)
Related
I was referring to the Highcharts doc but I don't quite understand how to add an HTMl inside an EXT js loop inside the labelFormatter func.
Here is the code:
getLegendConfig: function() {
var ct = this;
return {
align: 'right',
verticalAlign: 'top',
layout: 'vertical',
x: -25,
y: 25,
borderWidth: 0,
floating: type === 'column',
itemMarginBottom: 5,
labelFormatter: function() {
var count = this.y || 0;
Ext.each(ct.data.series, function(ct) {
ct.data.map(function(pt) {
if (pt !== null) {
return [
'<div>',
Ext.util.Format.ellipsis("some name", 48),
'<span class="qx-highchart-legend-item-count">',
Ext.util.Format.number(pt.count, '0,000'),
'</span>',
'</div>'
].join('');
}
})
});
},
useHTML: true
};
}.createDelegate(this)
With the above code, nothing gets populated inside the element, not sure how to get this running. Any ideas?
Here is the fiddle:
https://fiddle.sencha.com/#view/editor&fiddle/2k5v
Thanks
Since you do not have in your fiddle the actual highchart but only the getLegendConfig portion it would be tricky to give exact answer. However this goes back to the previous question you asked about the panel with values. So this is a similar example where the values are rendered on a panel: https://fiddle.sencha.com/#view/editor&fiddle/2k60
Here is the main part:
series.map((x) => {
return x.data.map(function(pt) {
if (pt !== null) {
return [
'<div>',
Ext.util.Format.ellipsis("name", 48),
'<span class="qx-highchart-legend-item-count">',
Ext.util.Format.number(pt.count, '0,000'),
'</span>',
'</div>'
].join('');
}
})
}),
You have to compose that with your code and see if you get the output you want. Based on the example I gave above this will render on a simple panel the values you wanted so it should be working with your pieces. Let me know how it goes.
I want the icon to change from accept to delete when it is clicked on.
Set the iconflag = 1 because on page load I want the accept icon to be seen.
The code below doesn't satisfy the expectation
{
xtype: 'actioncolumn',
width: 30,
items: [{
flex: 1,
tooltip: 'Click to expand',
icon: iconflag == '1' ? "shared/icons/fam/accept.gif" : "shared/icons/fam/delete.gif",
getClass: this.getActionClass,
handler: function() {
if (iconflag == '1') {
iconflag = '0';
} else if (iconflag == '0') {
iconflag = '1';
}
alert(iconflag);
}
}]
},
Using EXTJs 4.2
In your handler function, iconflag is a local variable that loses its value outside the handler function. Where you use iconflag outside the handler function, it is evaluated only ONCE, before you click on the icon.
What you want to do is store your variable in a place that automatically forces the grid to update. Which is why you want to store the iconflag on the records in the store.
Add to your model another field:
{
name: 'iconflag',
type: 'bool',
defaultValue: false,
persist: false
}
Move the icons into CSS:
Ext.util.CSS.createStyleSheet([
'.iconflag-accept {',
' background-image: url(\'shared/icons/fam/accept.gif\')',
'}',
'.iconflag-delete {'
' background-image: url(\'shared/icons/fam/delete.gif\')',
'}'
].join(''));
Update the column to use the field:
dataIndex: 'iconflag',
getClass: function(iconflag) {
if(iconflag) return 'iconflag-delete';
else return 'iconflag-accept';
},
handler: function(view, colindex, rowindex, item, e, record) {
record.set('iconflag', !record.get('iconflag'));
}
Fiddle: https://fiddle.sencha.com/#view/editor&fiddle/205l
I am using angular ui-grid to show list of rows from database table(Users).
I am doing a back end call and NodeJS fetches the data from DB and returns.
This data is being shown in angular ui-grid.
I want to enable or disable few html elements ., view/edit/delete based on the accessibility of the current user.
If the current user is ADMIN then all the links are enabled. If he is BASIC user then VIEW is enabled and EDIT and DELETE are disabled.
Project accessibility is also returned from the server . I just need to check this flag and disable/enable the links.
Please let me know , how to do this?
id name actions
1 AAA view edit delete
2 BBB view edit delete
3 CCC view edit delete
4 DDD view edit delete
<div class="box">
<div class="box-content box-table">
<div ui-grid="gridUsers" ui-grid-pagination>
</div>
</div>
</div>
$scope.gridUsers = {
paginationPageSizes: [15, 30, 45],
paginationPageSize: 15,
enableColumnMenus: false,
data: $scope.users,
filterOptions: $scope.filterOptions,
columnDefs: [{ field: 'id', displayName: 'Id', width: '20%'},
{ field: 'name', displayName: 'Name', width: '25%', enableFiltering: true},
{ name: 'Actions', displayName: 'Actions', width: '55%', cellTemplate:
'<div class="grid-action-cell action-btns">'+
'<span class="btn-small"><span style="color:#214c77;">view</span> </a>' +
'<a ng-click="grid.appScope.edit(row.entity.id)" class="btn-small btn-link"><span style="color:#80bb41;">edit</span> </a>' +
'<a ng-click="grid.appScope.delete(row.entity.id)" class="btn-small btn-link"> <span style="color:#e15829;">delete</span> </a>'
'</div>'}
]
};
Service.GetAllUsers(function (response) {
if (response.length != 0) {
$scope.users = response;
$scope.gridUsers.data = $scope.users;
}
});
I had the same problema.
In order to resolve it, I call a function after retrieving columns :
function updateColumnsDefs() {
columnsDefs
.forEach(function(column) {
switch (column.field) {
case 'status' :
columnVal = '<span ng-if="c.' + column.filterBy + '">{{c.' + column.filterBy + '}}</span>';
column.cellTemplate = '<div class="ui-grid-cell-contents">' + columnVal + '</span></div>';
break;
default :
break;
}
}
Look how I made a dynamic cellTemplate using ng-if.
After that, I apply the updated columnsDefs to gridOptions:
updateColumnsDefs();
vm.gridOptions = {
...
columnDefs : columnsDefs,
...
};
You should pay attention if use lazy loading or filter. In that case remember to recall updateColumnsDefs every time your data model changes.
I have defined a conditional cell template for one of the column. Its displaying the data correctly but I am not able to search for the text in the cell template.
Here is my plunkr:
https://plnkr.co/edit/TDX5jtPord1hkzCVaw3L?p=preview
var template1 = '<div class="">' +
'<div class="" ng-if="COL_FIELD > 30">Greater </div> ' +
'<div class="" ng-if="COL_FIELD < 30"> Lesser </div> ' +
'</div>';
In the template I have put the condition that.. if COL_FIELD > 30 then then write Greater.. or else write Lesser. And now I should be able to search for the Greater or Lesser in Number column.
A solution could be to add a property on your data like :
$http.get('data.json').success(function(data) {
data.map(function(item) {
item.greaterLesser = item.amount > 30 ? 'Greater' : 'Lesser';
});
$scope.gridOptions.data = data;
});
and then instead of using the amount with a template, just bind on this property.
$scope.gridOptions = {
enableFiltering: true,
columnDefs: [{
field: 'name',
width: 70
}, {
field: 'greaterLesser',
name: 'Number',
width: 90,
}, {
field: 'amount',
name: 'Currency',
cellFilter: 'currencyFilter:this',
}]
};
Here is the updated plunker
Edit
If you want to use the template, you could implement the search function yourself. You can add the filter option to your field and implement the condition function. Something like:
filter: {
condition: function(searchTerm, cellValue) {
var value = cellValue > 30 ? 'greater' : 'lesser';
var result = value.search(searchTerm.toLowerCase());
return result > -1;
}
}
Here I used the search function but you could use match or some other function.
Here is a demo plunker
I used the below code to make search work
field: 'EmpId',
displayName: 'Employee Type',
cellTemplate: '<div style="cursor:pointer" class="ui-grid-cell-contents">{{grid.appScope.GetEmployeeType(row)}}</div>',
filter: {
condition: function (searchTerm, cellValue,row,column) {
var value = $scope.GetEmployeeType(row);//same function that you use to display value in cell Template
return (value.toLocaleLowerCase().indexOf(searchTerm.toLocaleLowerCase())>-1);
}
}
I'm just trying to create a custom ComboBox to reduce some boilerplate:
Ext.define('App.AutoComboBox', {
extend: 'Ext.form.field.ComboBox',
alias: 'widget.autocombobox',
states: null,
initComponent: function() {
this.callParent(arguments);
if (!this.states) {
this.queryMode = 'remote';
} else {
this.queryMode = 'local';
this.bindStore(Ext.create('Ext.data.Store', {
type: 'array',
fields: ['_placeholder_'],
data: _.map(this.states, function(state) {
return {_placeholder_ : state}; })
}));
this.displayField = this.valueField = '_placeholder_'
}
this.validator = function(v) {
var field = this.displayField,
index = this.getStore().findExact(field, v);
return (index!==-1) ? true : 'Invalid selection';
};
},
listeners: {
select: function(combo, records) {
console.log(combo.getStore().indexOf(records[0])); // !== -1
}
}
});
So that I can use it like:
requires: ['App.AutoComboBox'],
...
items: [{
xtype: 'autocombobox',
name: 'test_local',
fieldLabel: 'test_local',
states: [ 'cat', 'dog' ] // local
}, {
xtype: 'autocombobox',
name: 'test_remote',
fieldLabel: 'test_remote',
store: 'Chipmunks', // a remote store
displayField: 'chipmunk_name'
}]
...
But something is amiss. The AutoComboBox renders OK, shows dropdown of records fine, but when I select an item from the dropdown, the combobox's display field is not set. The store seems to find the selected record (as seen by the select listener), but the value is still not set...
Help? thanks.
Edit: FIXED by moving this.callParent(arguments) after the new store is bound. Now accepting answers that explain why this fixes it... (I don't know why it works.. but it does)
In the parent initComponent method, the displayField is used to create the displayTpl:
if (!me.displayTpl) {
me.displayTpl = new Ext.XTemplate(
'<tpl for=".">' +
'{[typeof values === "string" ? values : values["' + me.displayField + '"]]}' +
'<tpl if="xindex < xcount">' + me.delimiter + '</tpl>' +
'</tpl>'
);
} else if (Ext.isString(me.displayTpl)) {
me.displayTpl = new Ext.XTemplate(me.displayTpl);
}
The bindStore call has probably nothing to do with it, I believe that this is this line that must be put before the call to the parent method:
this.displayField = this.valueField = '_placeholder_';