ExtJS 6. Pickerfield with treepanel - extjs

I do not know the right way of creating a treepanel pickerfield. Now I do it like so:
{
xtype:'pickerfield',
width:'34%',
emptyText: 'Choose layer',
createPicker: function(){
return Ext.create('Ext.tree.Panel',{
hidden: true,
floating: true,
minHeight: 300,
pickerField: this,
root: {
text: 'Root',
expanded: true,
children:[{
text: 'Standard',
expanded: true,
children: [{
text: 'Open Street Map',
checked: true,
leaf: true,
itemId: 'OMS'
},{
text: 'MapQuest',
leaf: false,
children:[{
text: 'Road',
leaf:true,
checked: false,
itemId: 'MapQuest',
val: 'osm'
},{
text: 'Satellite',
leaf:true,
checked: false,
itemId: 'MapQuest',
val: 'sat'
},{
text: 'Hybride',
leaf:true,
checked: false,
itemId: 'MapQuest',
val: 'hyb'
}]
}]
}]
}
});
}
}
It works partially. When I first hit the picker, it shows me a treepanel, but when I expand some nodes (leaf=false), then it gets closed. The same happens, when I check a tree leaf - I do not want that, because, the user is allowed to check multiple nodes. Beside, even when I check some leaves, the value of the picker stays unchanged (it keeps showing emptyText value). So, how to fix all this?

You can use following sample:
Ext.define('Ext.ux.TreeCombo',
{
extend: 'Ext.form.field.Picker',
alias: 'widget.treecombo',
tree: false,
constructor: function(config)
{
this.addEvents(
{
"itemclick" : true
});
this.listeners = config.listeners;
this.callParent(arguments);
},
records: [],
recursiveRecords: [],
ids: [],
selectChildren: true,
canSelectFolders: true,
multiselect: false,
displayField: 'text',
valueField: 'id',
treeWidth: 300,
matchFieldWidth: false,
treeHeight: 400,
masN: 0,
recursivePush: function(node, setIds)
{
var me = this;
me.addRecRecord(node);
if(setIds) me.addIds(node);
node.eachChild(function(nodesingle)
{
if(nodesingle.hasChildNodes() == true)
{
me.recursivePush(nodesingle, setIds);
}
else
{
me.addRecRecord(nodesingle);
if(setIds) me.addIds(nodesingle);
}
});
},
recursiveUnPush: function(node)
{
var me = this;
me.removeIds(node);
node.eachChild(function(nodesingle)
{
if(nodesingle.hasChildNodes() == true)
{
me.recursiveUnPush(nodesingle);
}
else me.removeIds(nodesingle);
});
},
addRecRecord: function(record)
{
var me = this;
for(var i=0,j=me.recursiveRecords.length;i<j;i++)
{
var item = me.recursiveRecords[i];
if(item)
{
if(item.getId() == record.getId()) return;
}
}
me.recursiveRecords.push(record);
},
afterLoadSetValue: false,
setValue: function(valueInit)
{
if(typeof valueInit == 'undefined') return;
var me = this,
tree = this.tree,
values = (valueInit == '') ? [] : valueInit.split(','),
valueFin = [];
inputEl = me.inputEl;
if(tree.store.isLoading())
{
me.afterLoadSetValue = valueInit;
}
if(inputEl && me.emptyText && !Ext.isEmpty(values))
{
inputEl.removeCls(me.emptyCls);
}
if(tree == false) return false;
var node = tree.getRootNode();
if(node == null) return false;
me.recursiveRecords = [];
me.recursivePush(node, false);
me.records = [];
Ext.each(me.recursiveRecords, function(record)
{
var id = record.get(me.valueField),
index = values.indexOf(''+id);
if(me.multiselect == true) record.set('checked', false);
if(index != -1)
{
valueFin.push(record.get(me.displayField));
if(me.multiselect == true) record.set('checked', true);
me.addRecord(record);
}
});
me.value = valueInit;
me.setRawValue(valueFin.join(', '));
me.checkChange();
me.applyEmptyText();
return me;
},
getValue: function()
{
return this.value;
},
getSubmitValue: function()
{
return this.value;
},
checkParentNodes: function(node)
{
if(node == null) return;
var me = this,
checkedAll = true;
node.eachChild(function(nodesingle)
{
var id = nodesingle.getId(),
index = me.ids.indexOf(''+id);
if(index == -1) checkedAll = false;
});
if(checkedAll == true)
{
me.addIds(node);
me.checkParentNodes(node.parentNode);
}
else
{
me.removeIds(node);
me.checkParentNodes(node.parentNode);
}
},
initComponent: function()
{
var me = this;
me.tree = Ext.create('Ext.tree.Panel',
{
alias: 'widget.assetstree',
hidden: true,
minHeight: 300,
rootVisible: (typeof me.rootVisible != 'undefined') ? me.rootVisible : true,
floating: true,
useArrows: true,
width: me.treeWidth,
autoScroll: true,
height: me.treeHeight,
store: me.store,
listeners:
{
load: function(store, records)
{
if(me.afterLoadSetValue != false)
{
me.setValue(me.afterLoadSetValue);
}
},
itemclick: function(view, record, item, index, e, eOpts)
{
me.itemTreeClick(view, record, item, index, e, eOpts, me)
}
}
});
if(me.tree.getRootNode().get('checked') != null) me.multiselect = true;
this.createPicker = function()
{
var me = this;
return me.tree;
};
this.callParent(arguments);
},
addIds: function(record)
{
var me = this;
if(me.ids.indexOf(''+record.getId()) == -1) me.ids.push(''+record.get(me.valueField));
},
removeIds: function(record)
{
var me = this,
index = me.ids.indexOf(''+record.getId());
if(index != -1)
{
me.ids.splice(index, 1);
}
},
addRecord: function(record)
{
var me = this;
for(var i=0,j=me.records.length;i<j;i++)
{
var item = me.records[i];
if(item)
{
if(item.getId() == record.getId()) return;
}
}
me.records.push(record);
},
removeRecord: function(record)
{
var me = this;
for(var i=0,j=me.records.length;i<j;i++)
{
var item = me.records[i];
if(item && item.getId() == record.getId()) delete(me.records[i]);
}
},
itemTreeClick: function(view, record, item, index, e, eOpts, treeCombo)
{
var me = treeCombo,
checked = !record.get('checked');//it is still not checked if will be checked in this event
if(me.multiselect == true) record.set('checked', checked);//check record
var node = me.tree.getRootNode().findChild(me.valueField, record.get(me.valueField), true);
if(node == null)
{
if(me.tree.getRootNode().get(me.valueField) == record.get(me.valueField)) node = me.tree.getRootNode();
else return false;
}
if(me.multiselect == false) me.ids = [];
//if it can't select folders and it is a folder check existing values and return false
if(me.canSelectFolders == false && record.get('leaf') == false)
{
me.setRecordsValue(view, record, item, index, e, eOpts, treeCombo);
return false;
}
//if record is leaf
if(record.get('leaf') == true)
{
if(checked == true)
{
me.addIds(record);
}
else
{
me.removeIds(record);
}
}
else //it's a directory
{
me.recursiveRecords = [];
if(checked == true)
{
if(me.multiselect == false)
{
if(me.canSelectFolders == true) me.addIds(record);
}
else
{
if(me.canSelectFolders == true)
{
me.recursivePush(node, true);
}
}
}
else
{
if(me.multiselect == false)
{
if(me.canSelectFolders == true) me.recursiveUnPush(node);
else me.removeIds(record);
}
else me.recursiveUnPush(node);
}
}
//this will check every parent node that has his all children selected
if(me.canSelectFolders == true && me.multiselect == true) me.checkParentNodes(node.parentNode);
me.setRecordsValue(view, record, item, index, e, eOpts, treeCombo);
},
fixIds: function()
{
var me = this;
for(var i=0,j=me.ids.length;i<j;i++)
{
if(me.ids[i] == 'NaN') me.ids.splice(i, 1);
}
},
setRecordsValue: function(view, record, item, index, e, eOpts, treeCombo)
{
var me = treeCombo;
me.fixIds();
me.setValue(me.ids.join(','));
me.fireEvent('itemclick', me, record, item, index, e, eOpts, me.records, me.ids);
if(me.multiselect == false) me.onTriggerClick();
}
});
var storeMenu = Ext.create('Ext.data.TreeStore',
{
root:
{
text: 'Root',
id: '0',
expanded: true,
children:
[
{id: '1', text: 'First node', leaf: false, children:
[
{id: '3', text: 'First child node', leaf: true},
{id: '4', text: 'Second child node', leaf: true}
]
},
{id: '2', text: 'Second node', leaf: true}
]
},
folderSort: false
});
Ext.onReady(function() {
Ext.create('Ext.ux.TreeCombo', {
margin:10,
width:120,
height: 10,
treeHeight: 10,
treeWidth: 240,
renderTo: 'treecombo3',
store: storeMenu,
selectChildren: false,
canSelectFolders: true
,itemTreeClick: function(view, record, item, index, e, eOpts, treeCombo)
{
var id = record.data.id;
}
});
});

Related

Bind Property to Custom List Item

I created a Custom ListItem, which has some ChildWidgets. One of these is a Combobox Widget.
I want to set the Model by a Controller, for this I used qx.data.controller.List.
With the bindItem and controller.bindProperty("", "model", null, item, index); I bind my Model to the List.
My Problem is, that I have one Property in my Model (text) which should be binded to the Combobox Value Property.
I tried controller.bindProperty("text", "value", null, item.getChildControl("combobox"), index); but I didn't get it to work.
What am I doing wrong?
Here's the final answer to your question, including the ability to delete items:
qx.Class.define("CustomListItem", {
extend: qx.ui.core.Widget,
include: [qx.ui.form.MModelProperty],
properties: {
isDistribution: {
init: true,
check: "Boolean",
event: "distributionChange"
},
isFilter: {
init: false,
check: "Boolean",
event: "symbolEvent"
},
isColumn: {
init: false,
check: "Boolean",
event: "symbolEvent"
},
isRow: {
init: false,
check: "Boolean",
event: "changeRow"
},
isFilterPatientCases: {
init: true,
check: "Boolean",
event: "symbolEvent"
},
isShow: {
init: true,
check: "Boolean",
event: "symbolEvent"
},
isUnkownFilter: {
init: true,
check: "Boolean",
event: "symbolEvent"
},
value: {
init: "",
event: "changeValue"
}
},
members: {
_createChildControlImpl: function(id) {
var control;
switch (id) {
case "alertimage":
control = new qx.ui.basic.Image();
control.setWidth(16);
this._add(control);
break;
case "suchecombobox":
control = new qx.ui.form.ComboBox();
this._add(control, {
flex: 1
});
break;
case "deletebutton":
control = new qx.ui.form.Button("Del");
control.setMaxWidth(40);
this._add(control);
break;
case "imagecomposite":
control = new qx.ui.container.Composite(new qx.ui.layout.HBox(0));
this._add(control);
break;
}
return control || this.base(arguments, id);
}
},
construct: function() {
this.base(arguments);
this._setLayout(new qx.ui.layout.HBox(0));
this._showImage = new qx.ui.basic.Image();
this._showImage.setMaxHeight(25);
this._showImage.setMaxWidth(25);
this._showImage.setScale(true);
this._filterImage = new qx.ui.basic.Image();
this._filterImage.setMaxHeight(25);
this._filterImage.setMaxWidth(25);
this._filterImage.setScale(true);
this._createChildControl("alertimage");
this._createChildControl("suchecombobox");
this._createChildControl("imagecomposite");
this._createChildControl("deletebutton");
this.getChildControl("deletebutton").addListener("execute", function(e) {
var itemModel = this.getModel();
data.remove(itemModel);
}, this);
}
});
var dataRaw = {
isColumn: false,
isFilter: false,
isFilterPatientCases: true,
isRow: true,
isShow: true,
isUnkownFilter: true,
position: "row",
queryText: "Dia:I50_:_Herzinsuffizienz",
textType: ""
};
var data = qx.data.marshal.Json.createModel([dataRaw]);
var list = new qx.ui.form.List();
list.setWidth(200);
var listController = new qx.data.controller.List(null, list);
listController.setDelegate({
bindItem: function(controller, item, index) {
controller.bindProperty("", "model", null, item, index);
controller.bindProperty("queryText", "value", null, item.getChildControl("suchecombobox"), index);
controller.bindProperty("isFilter", "isFilter", null, item, index);
controller.bindProperty("isColumn", "isColumn", null, item, index);
controller.bindProperty("isRow", "isRow", null, item, index);
controller.bindProperty("isFilterPatientCases", "isFilterPatientCases", null, item, index);
controller.bindProperty("isShow", "isShow", null, item, index);
controller.bindProperty("isUnkownFilter", "isUnkownFilter", null, item, index);
controller.bindProperty("queryText", "value", null, item, index);
},
createItem: function() {
return new CustomListItem();
}
});
listController.setModel(data);
listController.addListener("changeSelection", function(e) {
console.log(e.getData().toArray());
}, this);
var doc = this.getRoot();
var button = new qx.ui.form.Button("AddItem");
var newIndex = 1;
button.addListener("execute", function(e) {
dataRaw.queryText = "New (" + (newIndex++) + ")";
data.append(qx.data.marshal.Json.createModel([dataRaw]));
}, this);
doc.add(list, {
left: 0,
top: 0
});
doc.add(button, {
left: 200,
top: 0
});

Can you tell me PageSize Change Event in Kendo Angular grid

Can you tell me page size Change event in Kendo-Angular grid as i am new in kendo grid control. Please help me. Thank you in advance.
app.controller("dataController", function ($compile, dataFactory, $scope, $timeout) {
$scope.obj = [];
$scope.DistrictList = [];
$scope.DistrictTextToShow = "Select District";
$scope.GetDistrict = function () {
dataFactory.getdistrictList().success(function (data) {
$scope.DistrictList = data;
}).error(function (data) {
$.toaster({ priority: 'error', title: 'Error', message: 'Error while fetching data' });
});
};
if ($("#ddldistrict").val() == '') {
$scope.ddldistrict = GuidEmpty;
}
else {
$scope.ddldistrict = $("#ddldistrict").val();
}
$scope.gridData = new kendo.data.DataSource({
serverPaging: true,
serverSorting: true,
transport: {
read: {
url: baselocation + "api/Customer/GetAllCustomerByDistrictId",
data: { DistrictId: $scope.ddldistrict, isactive: $("#ddlstatus").val() }
}
},
schema: {
data: function (data) {
return data.Rows;
},
total: function (data) {
return data.TotalRows;
}
},
pageSize: 5
});
$scope.detailGridOptions = {
sortable: true,
pageable: {
"pageSizes": true,
change: function (e) {
if ($("#ddldistrict").val() == '') {
$scope.ddldistrict = GuidEmpty;
}
else {
$scope.ddldistrict = $("#ddldistrict").val();
}
//$("#grid1").data('kendoGrid').dataSource.pageSize(parseInt(this.value()));
$("#grid1").data('kendoGrid').dataSource.read({ DistrictId: $scope.ddldistrict, isactive: $("#ddlstatus").val() });
$("#grid1").data('kendoGrid').refresh();
}
},
datasource: $scope.gridData,
groupable: true,
scrollable: true,
columns: [{
field: "Customername",
title: "Customer Name",
width: "150px"
}, {
field: "mobile",
title: "Mobile",
width: "120px"
}, {
field: "email",
title: "Email",
width: "120px"
}, {
field: "Districtname",
title: "District",
width: "120px"
}]
};
$scope.GetDistrict();
$scope.BindData = function () {
if ($("#ddldistrict").val() == '') {
$scope.ddldistrict = GuidEmpty;
}
else {
$scope.ddldistrict = $("#ddldistrict").val();
}
$("#grid1").data('kendoGrid').dataSource.read({ DistrictId: $scope.ddldistrict, isactive: $("#ddlstatus").val() });
$("#grid1").data('kendoGrid').refresh();
}
//$("#grid1").kendoPager({
//});
//$scope.gridData.read();
});

I wrote CardBoard Rally sdk 2 app to which I added filter but I want to add two different filter, one for releases and one for backlog

I wrote CardBoard Rally sdk 2 app to which I added filter which apply to Backlog and Releases too. But I want to add two different filters, one for releases and one for backlog
See in image attached, u can see one column for Backlog and other for releases
So I should be able to say Now filter Backlog features, and Now filter Releases features
Below is my some of code snippet
Ext.define('CustomApp', {
extend: 'Rally.app.App',
componentCls: 'app',
autoScroll: true,
all_releases: [],
items:[{ xtype: 'container', itemId: 'filter_box', padding: 5},{xtype:'container',itemId:'button_box', padding: 5}],
logger: new Rally.technicalservices.logger(),
launch: function() {
//var allReleases = this._getReleases();
this._drawCardBoard(this, filters= null);
this.down('#button_box').add({
xtype: 'rallybutton',
text: 'Filter Criteria',
itemId: 'run-button',
scope: this,
handler: this._run,
margin: '0 0 0 10'
});
this.down('#button_box').add({
xtype: 'rallybutton',
text: 'Prev',
itemId: 'prev-button',
scope: this,
handler: this._setPrevReleaseDate,
margin: '0 0 0 100'
});
this.down('#button_box').add({
xtype: 'rallybutton',
text: 'Next',
itemId: 'next-button',
scope: this,
handler: this._setNextReleaseDate,
margin: '0 0 0 650'
});
},
_setPrevReleaseDate: function() {
if (this.globalVar == undefined) {
this.globalVar = new Date();
} else {
this.globalVar = new Date(this.globalVar);
};
this.globalVar = Rally.util.DateTime.formatWithDefault(Ext.Date.subtract(this.globalVar, Ext.Date.DAY, 224));
this._drawCardBoard(this, filters= null);
},
_setNextReleaseDate: function() {
if (this.globalVar == undefined) {
this.globalVar = new Date();
} else {
this.globalVar = new Date(this.globalVar);
};
this.globalVar = Rally.util.DateTime.formatWithDefault(Ext.Date.add(this.globalVar, Ext.Date.DAY, 224));
this._drawCardBoard(this, filters= null);
},
_getFilters: function(records) {
var filters = null;
if (records.length >= 1) {
if (records[0].data.PortfolioItemTypeName == "MMF") {
filters = Ext.create('Rally.data.QueryFilter',{
property: 'Parent',
operator: '=',
value: records[0].get("_ref")
});
} else if (records[0].data.PortfolioItemTypeName == "Epic") {
filters = Ext.create('Rally.data.QueryFilter',{
property: 'Parent.Parent',
operator: '=',
value: records[0].get("_ref")
});
} else if (records[0].data.PortfolioItemTypeName == "Program") {
filters = Ext.create('Rally.data.QueryFilter',{
property: 'Parent.Parent.Parent',
operator: '=',
value: records[0].get("_ref")
});
}
for ( var i=1;i<records.length;i++ ) {
if (records[i].data.PortfolioItemTypeName == "MMF") {
filters = filters.or(Ext.create('Rally.data.QueryFilter',{
property: 'Parent',
operator: '=',
value: records[i].get("_ref")
}));
} else if (records[i].data.PortfolioItemTypeName == "Epic") {
filters = filters.or(Ext.create('Rally.data.QueryFilter',{
property: 'Parent.Parent',
operator: '=',
value: records[i].get("_ref")
}));
} else if (records[i].data.PortfolioItemTypeName == "Program") {
filters = filters.or(Ext.create('Rally.data.QueryFilter',{
property: 'Parent.Parent.Parent',
operator: '=',
value: records[i].get("_ref")
}));
}
}
}
return filters;
},
_drawCardBoard: function(that, filters){
if (that.cardboard) {
that.cardboard.destroy();
};
var me = that;
if (filters == null) {
filters = [];
}
me.cardboard = Ext.create('Rally.ui.cardboard.CardBoard',{
types: ['PortfolioItem/Feature'],
attribute: 'Release',
config: {globalVar: this.globalVar},
columnConfig: {
xtype: 'rallycardboardcolumn',
displayField: 'Name',
valueField: '_ref',
plugins: [
{ptype:'rallycolumndropcontroller'},
{ptype:'rallycardboardcardrecordprocessor'},
{ptype:'tscolumnheaderupdater'} /*,
{ptype:'tscolumnheaderupdater', field_to_aggregate: 'LeafStoryPlanEstimateTotal'}*/
],
storeConfig: {
filters: filters,
context: this.getContext().getDataContext()
}
},
addColumn: function (columnConfig, index) {
console.log(columnConfig, index,"columnConfig, index");
var column = this._createColumnDefinition(columnConfig);
Ext.Array.insert(this.columnDefinitions, Ext.isNumber(index) ? index : this.columnDefinitions.length, [column]);
return column;
},
cardConfig: {
editable: true,
showIconsAndHighlightBorder: false,
showReadyIcon: true,
showBlockedIcon: true,
showColorIcon: true,
showPlusIcon: true,
showGearIcon: true,
fields: [
'FormattedID',
'Name',
'Parent',
'ReleaseDate',
'ReleaseStartDate',
{ name: 'Project', renderer: me._renderProject },
{ name: 'PercentDoneByStoryPlanEstimate' },
{ name: 'PreliminaryEstimate', fetch: ['PreliminaryEstimate', 'Name'], renderTpl: Ext.create('Ext.XTemplate', '{PreliminaryEstimate.Name}')},
{ name: 'LeafStoryPlanEstimateTotal', fetch: ['LeafStoryPlanEstimateTotal'], renderTpl: Ext.create('Ext.XTemplate', 'Plan Estimate Total: {LeafStoryPlanEstimateTotal}')}
],
},
listeners: {
beforeShow: this._onTeamMembersLoaded,
added: function(card,container){
//card.set('DisplayColor', "blue");
me.logger.log(this,card,container);
},
fieldClick: function(eOpts) {
me.logger.log(this,eOpts);
if ( eOpts == "PercentDoneByStoryPlanEstimate" ) {
me._showDoneTooltip(eOpts,this);
}
},
scope: this
}
});
_getLocalReleases: function(retrievedColumns, today_iso) {
var me = this;
if (today_iso == undefined) {
today_iso = Rally.util.DateTime.toIsoString(new Date(),false);
}
var filters = [{property:'ReleaseDate',operator:'>',value:today_iso}];
var iteration_names = [];
Ext.create('Rally.data.WsapiDataStore',{
model:me.attribute,
autoLoad: true,
filters: filters,
context: { projectScopeUp: false, projectScopeDown: false },
sorters: [
{
property: 'ReleaseDate',
direction: 'ASC'
}
],
//limit: Infinity,
pageSize: 4,
//buffered: true,
//purgePageCount: 4,
fetch: ['Name','ReleaseStartDate','ReleaseDate','PlannedVelocity'],
listeners: {
load: function(store,records) {
Ext.Array.each(records, function(record){
var start_date = Rally.util.DateTime.formatWithNoYearWithDefault(record.get('ReleaseStartDate'));
var end_date = Rally.util.DateTime.formatWithNoYearWithDefault(record.get('ReleaseDate'));
iteration_names.push(record.get('Name'));
//iteration_names.push(record.get('ReleaseDate'));
retrievedColumns.push({
value: record,
_planned_velocity: 0,
_missing_estimate: false,
columnHeaderConfig: {
headerTpl: "{name}<br/>{start_date} - {end_date}",
headerData: {
name: record.get('Name'),
start_date: start_date,
end_date: end_date,
planned_velocity: 0,
missing_estimate: false
}
}
});
});
this._getAllReleases(retrievedColumns,iteration_names);
},
scope: this
}
});
},
applyLocalFilters: function() {
this.applyFilters(this.localFilters);
},
_getAllReleases: function(retrievedColumns,iteration_names, today_iso) {
var me = this;
if (today_iso == undefined) {
today_iso = Rally.util.DateTime.toIsoString(new Date(),false);
}
var filters = [{property:'ReleaseDate',operator:'>',value:today_iso}];
Ext.create('Rally.data.WsapiDataStore',{
model:me.attribute,
autoLoad: true,
filters: filters,
sorters: [
{
property: 'ReleaseDate',
direction: 'ASC'
}
],
fetch: ['Name','Project','PlannedVelocity'],
listeners: {
load: function(store,records) {
Ext.Array.each(records, function(record){
var planned_velocity = record.get('PlannedVelocity') || 0;
var index = Ext.Array.indexOf(iteration_names[0],record.get('Name'));
if (planned_velocity == 0 ) {
retrievedColumns[index+1]._missing_estimate = true;
}
retrievedColumns[index+1]._planned_velocity += planned_velocity;
});
this.fireEvent('columnsretrieved',this,retrievedColumns);
this.columnDefinitions = [];
_.map(retrievedColumns,this.addColumn,this);
this._renderColumns();
},
scope: this
}
});
},
_createColumnDefinition: function (columnConfig) {
var config = Ext.merge({
enableCrossColumnRanking: this.enableCrossColumnRanking
}, this.columnConfig, columnConfig);
var enableRanking = this.enableRanking;
if (this.context) {
var workspace = this.context.getWorkspace();
if (workspace) {
enableRanking = enableRanking && workspace.WorkspaceConfiguration.DragDropRankingEnabled;
}
}
var listenersConfig = {
ready: this._onColumnReady,
select: this._onCardSelect,
deselect: this._onCardDeselect,
cardinvalid: this._onCardInvalid,
cardready: this._onCardReady,
scope: this
};
if (!this.serverSideFiltering) {
listenersConfig.filter = this.applyLocalFilters;
}
Ext.merge(config, {
cardConfig: Ext.clone(this.cardConfig),
columnHeaderConfig: Ext.clone(this.columnHeaderConfig),
model: this.models,
attribute: this.attribute,
storeConfig: Ext.clone(this.storeConfig),
enableRanking: enableRanking,
filterCollection: this.filterCollection ? this.filterCollection.clone() : undefined,
ownerCardboard: this,
listeners: listenersConfig,
ddGroup: this.ddGroup
});
if (this.readOnly) {
config.dropControllerConfig = false;
}
//merge configs, unioning collections
var cardConfig = config.cardConfig;
if (columnConfig.cardConfig) {
Ext.Object.merge(cardConfig, columnConfig.cardConfig);
cardConfig.fields = Ext.Array.merge(columnConfig.cardConfig.fields || [], this.cardConfig.fields || []);
}
var storeConfig = config.storeConfig;
if (columnConfig.storeConfig) {
Ext.Object.merge(storeConfig, columnConfig.storeConfig);
storeConfig.filters = Ext.Array.merge(columnConfig.storeConfig.filters || [], this.storeConfig.filters || []);
}
console.log("columnConfig", Ext.clone(columnConfig));
console.log("storeConfig", Ext.clone(storeConfig));
return Ext.widget(config.xtype, config);
},
});
Ext.override(Rally.ui.cardboard.Card,{
_setupPlugins: function() {
var cardContentRightPlugin = {ptype: 'rallycardcontentright'};
this.plugins.push(cardContentRightPlugin);
this.plugins.push({ptype: 'rallycardcontentleft'});
if (this.record.get('updatable')) {
if (this.editable) {
this.addCls('editable');
this.plugins.push({ptype: 'rallycardediting'});
var predicateFn = Rally.predicate.RecordPredicates.hasField('PlanEstimate');
if (predicateFn(this.record) && Ext.Array.contains(this.getFields(), 'PlanEstimate')) {
cardContentRightPlugin.showPlanEstimate = true;
}
if (this.enableValidationUi) {
this.plugins.push({ptype: 'rallycardvalidation'});
this.plugins.push({ptype: 'rallycardvalidationui', notificationFieldNames: ['PlanEstimate']});
}
}
if (this.showIconsAndHighlightBorder) {
this.plugins.push({
ptype: 'rallycardicons',
showMenus: this.showIconMenus,
showColorPopover: this.showColorPopover
});
}
}
if (this.showAge > -1) {
this.plugins.push({ptype: 'rallycardage'});
}
this.plugins.push({ptype:'tscardreleasealignment'});
}
}),
Ext.override(Rally.ui.cardboard.Column,{
getStoreFilter: function(model) {
var property = this.attribute;
var value = this.getValue();
if ( this.attribute == "Release" ) {
property = "Release.Name";
if ( value ) {
value = value.get('Name');
}
}
return {
property:property,
operator: '=',
value: value
};
},
isMatchingRecord: function(record) {
var recordValue = record.get(this.attribute);
if (recordValue) {
recordValue = recordValue.Name;
}
var columnValue = this.getValue();
if ( columnValue ) {
columnValue = columnValue.get('Name');
}
return (columnValue === recordValue );
},
addCard: function(card, index, highlight) {
var record = card.getRecord();
var target_value = this.getValue();
if ( target_value && typeof(target_value.get) === "function" ) {
target_value = this.getValue().get('_ref');
}
record.set(this.attribute,target_value);
if (target_value) {
record.set("PlannedStartDate",this.getValue().get('ReleaseStartDate'));
record.set("PlannedEndDate",this.getValue().get('ReleaseDate'));
} else {
record.set("PlannedStartDate",null);
record.set("PlannedEndDate",null);
}
if (!Ext.isNumber(index)) {
//find where it should go
var records = Ext.clone(this.getRecords());
records.push(record);
this._sortRecords(records);
var recordIndex = 0;
for (var iIndex = 0, l = records.length; iIndex < l; iIndex++) {
var i = records[iIndex];
if (i.get("ObjectID") === record.get("ObjectID")) {
recordIndex = iIndex;
break;
}
}
index = recordIndex;
}
this._renderCard(card, index);
if (highlight) {
card.highlight();
}
this.fireEvent('addcard');
card.fireEvent('ready', card);
},

ExtJS Two and more separators on numberfields

Tell me, please, how you can use two types of separator digital input field? You can use only one standard methods, but at a different keyboard layout, there is a need to use another, keeping the data in one format, that is the '.'
Input: 10,789 or 10.789
Save: 10.789
I use Ext.form.NumberField for editing integral field.
Part of my code:
var editor = new Ext.ux.grid.RowEditor({
saveText: LANG['update'],
listeners: {
afteredit: function(object, changes, r, rowIndex) {
Ext.MessageBox.alert(LANG['alert_info'], LANG['memory']); }
}
}
});
var userGrid = new Ext.grid.GridPanel({
id: 'status-form',
region:'center',
margins: '5 5 5 5',
store: Gstore,
iconCls: 'icon-grid',
plugins: [editor, summary],
cm: new Ext.grid.ColumnModel([
{header: "ID", width: 30, sortable: true, dataIndex: 'idb', renderer: formatID},
{xtype: 'datecolumn', header: LANG['date'], width: 70, sortable: true, dataIndex: 'date',
groupRenderer: Ext.util.Format.dateRenderer('M Y'),
format: 'd/m/Y',
editor: new Ext.form.DateField({
value: (new Date()).format('d/m/Y'),
//format: 'd/m/Y',
minValue: '01/01/2010',
//minText: 'Please Check Correct Data',
maxValue: (new Date()).format('d/m/Y'),
editable: false
})
},
{header: LANG['title'], width: 150, sortable: true, dataIndex: 'title',
editor: new Ext.form.TextField({}),
summaryType: 'count',
summaryRenderer: function(v, params, data){
return ((v === 0 || v > 1) ? LANG['Tasks']+ ': '+ v: '1 '+LANG['Task']);
}
},
{header: LANG['lenght'], width: 60, sortable: true, dataIndex: 'lenght', renderer: formatKM, align: 'center',
summaryType: 'sum',
summaryRenderer: Ext.util.Format.cifres2,
editor: new Ext.form.NumberField({
allowNegative: false,
decimalPrecision: 2,
//decimalSeparator: ',',
maxValue: 1000
//allowBlank: false
})
},
{header: LANG['time'], width: 30, sortable: true, dataIndex: 'time', align: 'center',
renderer: formatTimeStr,
summaryType: 'sum22',
editor: new Ext.form.NumberField({
//format: 'H:i',
allowNegative: false,
decimalPrecision: 2,
decimalSeparator: ':'
})
},
{header: LANG['vsr'], width: 50, sortable: true, dataIndex: 'vsr', renderer: formatKM, align: 'center',
summaryType: 'average',
summaryRenderer: Ext.util.Format.cifres2,
editor: new Ext.form.NumberField({
allowNegative: false,
decimalPrecision: 2,
maxValue: 100
})
},
{header: LANG['vmax'], width: 50, sortable: true, dataIndex: 'vmax', renderer: formatKM, align: 'center',
summaryType: 'max',
summaryRenderer: Ext.util.Format.cifres2,
editor: new Ext.form.NumberField({
allowNegative: false,
decimalPrecision: 2,
maxValue: 100
})
}, ............
I implemented this with easy way. My method is enable key events of number field and check if pressed key is ',' than add a '.' to value. That is the implementation;
Ext.create('Ext.form.Panel', {
title: 'On The Wall',
width: 300,
bodyPadding: 10,
renderTo: Ext.getBody(),
items: [{
xtype: 'numberfield',
anchor: '100%',
name: 'bottles',
fieldLabel: 'Bottles of Beer',
enableKeyEvents : true,
value: 99,
maxValue: 99,
minValue: 0,
listeners : {
keypress : function(view, e){
if(e.getKey() == 44)
view.setRawValue(view.getRawValue() +'.');
}
}
}]
});
I used this in my project but I don't know it's fit your request.
First, checking the value where it's number and has "." then put proper value based on this condition. If it's not has "." in the value then calling the replace function.
value.match(/^[0-9]+\.?[0-9]*$/) ? Number(value) : Number((value.replace(',', '.')));
The solution is found here: http://habrahabr.ru/post/137966/ (russian lang)
Since there is seen a solution for version> 4.0, and I have worth less had to change the code, replacing the standard JS functions.Vykladyvayu it here, maybe someone will need:
To begin at the header of the page to connect сursor.js
// Author: Diego Perini <dperini#nwbox.com>
var sb = new Array(
'leftbox', 'rightbox', 'scrollLeft', 'scrollRight',
'caretPos', 'maxLength', 'textLength', 'availLength',
'beforeCaret', 'afterCaret', 'selectedText'
)
var leftbox = {};
var rightbox = {};
var scrollLeft = {};
var scrollRight = {};
var caretPos = {};
var maxLength = {};
var textLength = {};
var availLength = {};
var beforeCaret = {};
var afterCaret = {};
var selectedText = {};
/*
for (var i in sb) {
var v = '"var '+sb[i]+' = {}"';
eval(v);
}
*/
var os = 0
var oe = 0
function update(o) {
var t = o.value, s = getSelectionStart(o), e = getSelectionEnd(o)
if (s == os && e == oe) return
caretPos.firstChild.nodeValue = s
maxLength.firstChild.nodeValue = o.getAttribute('maxLength')
textLength.firstChild.nodeValue = t.length
availLength.firstChild.nodeValue = o.getAttribute('maxLength') - t.length
afterCaret.firstChild.nodeValue = t.substring(s).replace(/ /g, '\xa0') || '\xa0'
beforeCaret.firstChild.nodeValue = t.substring(0, s).replace(/ /g, '\xa0') || '\xa0'
selectedText.firstChild.nodeValue = t.substring(s, e).replace(/ /g, '\xa0') || '\xa0'
rightbox.value = scrollRight.firstChild.nodeValue = t.substring(s).replace(/ /g, '\xa0') || '\xa0'
leftbox.value = scrollLeft.firstChild.nodeValue = t.substring(0, s).replace(/ /g, '\xa0') || '\xa0'
os = s
oe = e
return true
}
function setup() {
for (var i in sb) eval(sb[i] + ' = document.getElementById(sb[i])')
update(document.getElementById('textbox'))
}
function getSelectionStart(o) {
if (o.createTextRange) {
var r = document.selection.createRange().duplicate()
r.moveEnd('character', o.value.length)
if (r.text == '') return o.value.length
return o.value.lastIndexOf(r.text)
} else return o.selectionStart
}
function getSelectionEnd(o) {
if (o.createTextRange) {
var r = document.selection.createRange().duplicate()
r.moveStart('character', -o.value.length)
return r.text.length
} else return o.selectionEnd
}
and connect the plugin:
Ext.ns('Ext.ux.form');
/**
* #class Ext.ux.form.NumberInputFilter
* #extends Ext.form.NumberField
* Plugin (ptype = 'numberinputfilter')
* #param allowedDecimalSeparators: ',.:-' and other
* #ptype numberinputfilter
*/
Ext.ux.form.NumberInputFilter = Ext.extend(Ext.form.NumberField, {
initComponent: function(){
Ext.ux.form.NumberInputFilter.superclass.initComponent.call(this);
},
init : function(field) {
if (!(field && field.isXType('numberfield'))) {
return;
}
Ext.apply(field, {
allowedDecimalSeparators : this.allowedDecimalSeparators,
checkValue : function(newChar) {
var raw = this.getRawValue();
var el = Ext.get(this.id).dom;
// functions taken from here http://javascript.nwbox.com/cursor_position/
// and connected to a separate file cursor.js
var start = getSelectionStart(el);
var end = getSelectionEnd(el);
if (start != end) {
// delete the selected text from the expected values
raw = raw.substring(0, start) + raw.substring(end);
}
if (Ext.isEmpty(raw)) {
return (newChar == this.decimalSeparator || (this.minValue < 0) && newChar == '-') || newChar.search('/^\d$/');
}
if (raw.length == this.maxLength) {
return false;
}
if (newChar == this.decimalSeparator && (!this.allowDecimals || raw.indexOf(this.decimalSeparator) != -1)) {
return false;
}
// form the intended meaning
raw = raw.substring(0, start) + newChar + raw.substring(start);
raw = raw.split(new RegExp(this.decimalSeparator.replace("/([-.*+?^${}()|[\]\/\\])/g", "\\$1")));
return (!raw[0] || this.intRe.search(raw[0])) && (!raw[1] || this.decRe.search(raw[1]));
},
filterKeys : function(e){
if (e.ctrlKey && !e.altKey) {
return;
}
var key = e.getKey(),
charCode = String.fromCharCode(e.getCharCode());
if(Ext.isGecko && (e.isNavKeyPress() || key === e.BACKSPACE || (key === e.DELETE && e.button === -1))){
return;
}
if(!Ext.isGecko && e.isSpecialKey() && !charCode){
return;
}
// begin hack
if (charCode != this.decimalSeparator && this.allowedDecimalSeparators.indexOf(charCode) != -1) {
// if the input character is not a decimal point,
                    // But it is one of the alternatives,
                    // Replace it with a decimal point
charCode = this.decimalSeparator;
if (Ext.isIE) {
// in the IE code of the pressed key can be substituted directly
e.browserEvent.keyCode = charCode.charCodeAt(0);
} else if (Ext.isGecko) {
// for gecko-engine slowing Event
e.stopEvent();
// create a new event with the modified code of the pressed key
var newEvent = document.createEvent('KeyEvents');
// Mandatory event must be cancelable
                        // As it can be reversed, if the decimal
                        // Delimiter is entered in the field
newEvent.initKeyEvent(
e.browserEvent.type,
e.browserEvent.bubbles,
true, //cancellable
e.browserEvent.view,
e.browserEvent.ctrlKey,
e.browserEvent.altKey,
e.browserEvent.shiftKey,
e.browserEvent.metaKey,
0, // keyCode
charCode.charCodeAt(0) // charCode
);
e.getTarget().dispatchEvent(newEvent);
// event generated, nothing doing.
return;
} else if (Ext.isWebKit) {
// stopped event
e.stopEvent();
// into webkit initKeyboardEvent dont work, use TextEvent
if (this.checkValue(charCode)) {
var newEvent = document.createEvent('TextEvent');
newEvent.initTextEvent(
'textInput',
e.browserEvent.bubbles,
true,
e.browserEvent.view,
charCode
);
e.getTarget().dispatchEvent(newEvent);
}
return;
}
}
if (!this.checkValue(charCode)) {
e.stopEvent();
}
// end hack
},
updateDecimalPrecision : function(prec, force) {
if (prec == this.decimalPrecision && force !== true) {
return;
}
if (!Ext.isNumber(prec) || prec < 1) {
this.allowDecimals = false;
} else {
this.decimalPrecision = prec;
}
var intRe = '^';
if (this.minValue < 0) {
intRe += '-?';
}
intRe += '\\d' + (Ext.isNumber(this.integerPrecision) ? '{1,' + this.integerPrecision + '}' : '+') + '$';
this.intRe = new RegExp(intRe);
if (this.allowDecimals) {
this.decRe = new RegExp('^\\d{1,' + this.decimalPrecision + '}$');
} else {
delete this.decRe;
}
},
fixPrecision : function(value) {
// support decimalSeparators
if (Ext.isString(value)) {
value = value.replace(new RegExp('[' + (this.allowedDecimalSeparators + this.decimalSeparator).replace("/([-.*+?^${}()|[\]\/\\])/g", "\\$1") + ']'), '.');
}
// end hack
var me = this,
nan = isNaN(value),
precision = me.decimalPrecision;
if (nan || !value) {
return nan ? '' : value;
} else if (!me.allowDecimals || precision <= 0) {
precision = 0;
}
console.info(parseFloat(parseFloat(value).toFixed(precision)));
return parseFloat(parseFloat(value).toFixed(precision));
}
});
field.updateDecimalPrecision(field.decimalPrecision, true);
}
});
Ext.preg('numberinputfilter', Ext.ux.form.NumberInputFilter);
Use any separator, what we like in a digital field, just listing them:
.....
{header: LANG['lenght'], width: 60, sortable: true, dataIndex: 'lenght', renderer: formatKM, align: 'center',
summaryType: 'sum',
summaryRenderer: Ext.util.Format.cifres2,
editor: new Ext.form.NumberField({
allowNegative: false,
decimalPrecision: 2,
plugins: new Ext.ux.form.NumberInputFilter({
allowedDecimalSeparators : ',.-'
}),
maxValue: 1000,
allowBlank: false
})
},
.....

How to put confimation box in extjs

In my UI, I have 3 buttons which are 'add','save' and 'cancel' and an Editor Grid Panel. Now if the user add or edit a record and try to save but accidentally clicked the cancel button, automatically all the records that supposed to be change and saved will remain the same.
My problem is that how can I put a confirmation box at the cancel button that it will only show if there are changes made in the records?
This is what I've tried so far:
var grid = new Ext.grid.EditorGridPanel({
id: 'maingrid',
store: store,
cm: cm,
width: 785.5,
anchor: '100%',
height: 700,
frame: true,
loadMask: true,
waitMsg: 'Loading...',
clicksToEdit: 2,
tbar: [
'->',
{
text: 'Add',
iconCls: 'add',
id: 'b_add',
disabled: true,
handler : function(){
var Put = grid.getStore().recordType;
var p = new Put({
action_take: 'add',
is_active: '',
allowBlank: false
});
Ext.getCmp('b_save').enable();
Ext.getCmp('b_cancel').enable();
grid.stopEditing();
store.insert(0, p);
grid.startEditing(0, 1);
}
},'-',{
text: 'Save',
iconCls: 'save',
id: 'b_save',
disabled: true,
handler : function(){
var objectStore = Ext.getCmp("maingrid").getStore();
var objectModified = objectStore.getModifiedRecords();
// console.log(objectModified);
var customer_id = Ext.getCmp("maingrid").getStore().baseParams['customer_id'];
var objectData = new Array();
var dont_include;
if(objectModified.length > 0)
{
for(var i = 0; i < objectModified.length; i++)
{
dont_include = false;
//all fields are null, then prompt that it should be filled-in (for edit)
if(objectModified[i].data.id
&&
(
(objectModified[i].data.firstname == undefined || objectModified[i].data.firstname == null|| objectModified[i].data.firstname == '')
||
(objectModified[i].data.lastname == undefined || objectModified[i].data.lastname == null|| objectModified[i].data.lastname == '')
||
(objectModified[i].data.email_address == undefined || objectModified[i].data.email_address == null|| objectModified[i].data.email_address == '')
)
)
{
Ext.Msg.show({
title: 'Warning',
msg: "Input value required.",
icon: Ext.Msg.WARNING,
buttons: Ext.Msg.OK
});
return;
}
else//no id, means new record
{
//all fields are not filled-in, just do nothing
if((objectModified[i].data.firstname == undefined || objectModified[i].data.firstname == null|| objectModified[i].data.firstname == '')&&
(objectModified[i].data.lastname == undefined || objectModified[i].data.lastname == null|| objectModified[i].data.lastname == '')&&
(objectModified[i].data.email_address == undefined || objectModified[i].data.email_address == null|| objectModified[i].data.email_address == ''))
{
//boolean flag to determine whether to include this in submission
dont_include = true;
}
//either one field is not filled-in prompt to fill in all fields
else if((objectModified[i].data.firstname == undefined || objectModified[i].data.firstname == null|| objectModified[i].data.firstname == '')||
(objectModified[i].data.lastname == undefined || objectModified[i].data.lastname == null|| objectModified[i].data.lastname == '')||
(objectModified[i].data.email_address == undefined || objectModified[i].data.email_address == null|| objectModified[i].data.email_address == ''))
{
Ext.Msg.show({
title: 'Warning',
msg: "Input value required.",
icon: Ext.Msg.WARNING,
buttons: Ext.Msg.OK
});
return;
}
}
//the data for submission
if(!dont_include)
{
objectData.push({
firstname: objectModified[i].data.firstname,
lastname: objectModified[i].data.lastname,
email_address: objectModified[i].data.email_address,
id: objectModified[i].data.id,
customer_id: customer_id
});
}
}
// console.log(objectData)
// return;
//check if data to submit is not empty
if(objectData.length < 1)//empty
{
Ext.Msg.show({
title: 'Warning',
msg: "No record to save",
icon: Ext.Msg.WARNING,
buttons: Ext.Msg.OK
});
Ext.getCmp('maingrid').getStore().reload();
return;
}
// return;
Ext.Msg.wait('Saving Records...');
Ext.Ajax.request({
timeout:900000,
params: {objdata: Ext.encode(objectData)},
url: '/index.php/saveContent',
success: function(resp){
var response = Ext.decode(resp.responseText);
Ext.Msg.hide();
if(response.success == true){
Ext.Msg.show({
title: "Information",
msg: response.msg,
buttons: Ext.Msg.OK,
icon: Ext.Msg.INFO,
fn: function(btn){
Ext.getCmp('maingrid').getStore().reload();
Ext.getCmp('b_save').disable();
Ext.getCmp('b_cancel').disable();
}
});
}else{
Ext.Msg.show({
title: "Warning",
msg: response.msg,
buttons: Ext.Msg.OK,
icon: Ext.Msg.WARNING
});
}
},
failure: function(resp){
Ext.Msg.hide();
Ext.Msg.show({
title: "Warning1",
msg: response.msg,
buttons: Ext.Msg.OK,
icon: Ext.Msg.WARNING
});
}
});
}
else{
Ext.Msg.show({
title: 'Warning',
msg: "No changes made.",
icon: Ext.Msg.WARNING,
buttons: Ext.Msg.OK
});
}
}
},'-',
{
text: 'Cancel',
iconCls: 'cancel',
id: 'b_cancel',
disabled: true,
handler : function(){
Ext.getCmp('maingrid').getStore().reload();
Ext.getCmp('b_save').disable();
Ext.getCmp('b_cancel').disable();
}
}
],
bbar: pager
});
You can use Ext.data.Store getModifiedRecords() and getRemovedRecords() methods to detect if grid store contains changes. For displaying confirmation dialog you can use Ext.MessageBox.confirm() method and then reload store only if user click on "yes" button.
var store = Ext.getCmp('maingrid').getStore();
var modified = store.getModifiedRecords();
var removed = store.getRemovedRecords();
if (modified.length || removed.length) {
Ext.MessageBox.confirm('Cancel', 'Do you want cancel changes?', function(btnId) {
if (btnId == 'yes') {
store.reload();
Ext.getCmp('b_save').disable();
Ext.getCmp('b_cancel').disable();
}
});
}

Resources