I need to do something like web shop where ideas is to have to views on products. One is grid and second is card/tile.
I try to achive this with combination of sencha grid and view with template based on same view model.
If you want just "present" data view is working fine, but if you need bind data into controls there are some issues.
Example pictures:
Grid view
Card/Tile view
Issue 1: If I change quantity in Grid view with + on row level all buttons and numeric field for that row will disappear on Card view
Issue 2: +/- button are not working on card view, they cannot fetch row quantity to increase or decrease value
Any ideas ?
Here is the code of Card View:
cls: "b2bbp-shop-widget-carditemview",
reference: "b2bbp-shop-widget-carditemview",
bind: {
store: "{items}",
record: "{record}"
},
tpl: new Ext.XTemplate(
"<tpl for=\".\">",
"<div class=\"admin-b2bbp-cardview-card\">",
"<div class=\"admin-b2bbp-cardview-content\">",
"<link rel=\"stylesheet\" href=\"https://cdnjs.cloudflare.com/ajax/libs/font-awesome/4.7.0/css/font-awesome.min.css\">",
"<img class=\"admin-b2bbp-cardview-image\" src=\"https://dictionary.cambridge.org/images/thumb/lightb_noun_002_21219.jpg?version=5.0.74\" height=190 width=180 />",
"<br/>",
"<div class=\"question-button\"></div>",
"<p class=\"admin-b2bbp-cardview-descripion\"> {description} </p>",
"<div class=\"details\"></div>",
"<span class=\"admin-b2bbp-cardview-left shop-order-summary\">Quantity</span>",
"<span class=\"admin-b2bbp-cardview-right shop-order-summary\">{msrp}</span>",
"<br/>",
"<div class=\"admin-b2bbp-cardview-pricesum\">",
"<span class=\"admin-b2bbp-cardview-left shop-order-summary-value\">Total</span>",
"<span class=\"admin-b2bbp-cardview-right shop-order-summary-value\">{itemTotal}</span>",
"</div><br/>",
"<div class=\"buttons\"></div>",
"</div>",
"</div>",
"</tpl>", {
complied: true
}
),
itemSelector: "div.admin-b2bbp-cardview-card",
listeners: {
refresh: function (a, b) {
var renderSelectorQuestionMark = Ext.query("div.question-button");
for (var i in renderSelectorQuestionMark) {
var rec = b[i];
Ext.create("Ext.form.field.Display", {
bind: {
value: rec.data.name
},
fieldCls: "shop-order-summary",
renderTo: renderSelectorQuestionMark[i]
});
}
var renderSelectorButtons = Ext.query("div.buttons");
for (var j in renderSelectorButtons) {
var rec = b[j];
console.log("Render Buttons records", rec)
Ext.create("Ext.container.Container", {
layout: {
type: "column",
},
height: 50,
padding: "20 0 0 0",
items: [
{
xtype: "container",
padding: "0 10 0 0",
items: [
{
xtype: "button",
glyph: "xf068#FontAwesome",
width: 30,
height: 30,
record: rec,
handler: function(button) {
console.log(button.record)
//button.record.set('quantity',button.record.data.quantity-1);
},
}]
},
{
xtype: "numberfield",
hideTrigger: true,
fieldCls: "store-grid-text",
minValue: 1,
value: rec.data.quantity,
step: 1,
width: 100,
/* bind: {
value: rec.data.quantity,
record: rec
},*/
setRecord: function(record) { // Required for binding to work
this.record = record;
},
getRecord: function() { // Required for binding to work
return this.record;
}
},
{
xtype: "container",
padding: "0 35 20 10",
items: [
{
xtype: "button",
glyph: "xf067#FontAwesome",
width: 30,
height: 30,
record: rec,
handler: function(button) {
console.log(button)
console.log(button.record)
//button.record.set('quantity',button.record.data.quantity+1);
},
setRecord: function(record) { // Required for binding to work
this.record = record;
},
getRecord: function() { // Required for binding to work
return this.record;
}
}]
},
{
xtype: "button",
//text: 'Dodaj',
cls: "shop-button",
iconCls: "x-fa fa-shopping-basket",
maxWidth: 60,
height: 30,
record: rec,
handler: function(button) {
console.log(button.record);
},
}
],
renderTo: renderSelectorButtons[j]
});
}
var renderSelectorDetails = Ext.query("div.details");
for (var k in renderSelectorDetails) {
Ext.create("Ext.button.Button", {
text: "INFO",
iconCls: "x-fa fa-info-circle",
cls: "shop-info-button",
renderTo: renderSelectorDetails[k]
})
}
},
},
Related
In ExtJS, on a menu toolbar button, I am trying to remove the current panel in my center window, then recreate it with the new selection. I do not understand the proper way to do this. So far when I click the menu item, it removes whatever is currently there successfully, then it will add the new panel successfully. The problem is the 2nd time I hit the button I get the following error:
REGISTERING DUPLICATE COMPONENT ID 'mainportalID'.
I realize its telling me I already used this ID, but then what would be the correct way to remove the current panel, and replace with a new one?
Here is my view controller:
selectMenuButton: function (button, e) {
console.log('select menu button section was hit')
console.log(button);
console.log(e);
var optionString = button.text;
var myDetailsPanel = Ext.getCmp('navview');
console.log(myDetailsPanel);
var count = myDetailsPanel.items.items.length;
if (count > 0) {
myDetailsPanel.items.each(function (item, index, len) {
myDetailsPanel.remove(item, false);
});
}
myDetailsPanel.add({
xtype: optionString
});
}
var myStore = Ext.create('ExtApplication1.store.PositionsStore');
var gridSummary = Ext.create('Ext.grid.Panel', {
store: myStore,
width: 600,
title: 'my first grid',
columns: [
{
text: 'AcctNum',
dataIndex: 'AcctNum',
width: 100
},
{
text: 'AcctShortCode',
dataIndex: 'AcctShortCode',
flex: 1
},
{
text: 'Exchange',
dataIndex: 'Exchange',
width: 200
}
]
});
This is my view
Ext.define('ExtApplication1.view.main.MainPortal', {
extend: 'Ext.panel.Panel',
xtype: 'mainportal',
alias: 'widget.mainportal',
id: 'mainportalID',
html: 'user... this is the main portal window',
autoScroll: true,
bodyPadding: 10,
items: [
gridSummary
]
});
adjusted panel
Ext.define('ExtApplication1.view.main.MainPortal', {
extend: 'Ext.panel.Panel',
xtype: 'mainportal',
alias: 'widget.mainportalAlias',
reference: 'gridtextfield',
//id: 'mainportalID',
html: 'user... this is the main portal window',
autoScroll: true,
bodyPadding: 10,
items: [
gridSummary
]
});
adjusted view controller
onComboboxSelect: function (combo, record, eOpts) {
console.log('new listener was hit');
//return selected Item
var selectedValue = record.get('ClientName');
var selectedCID = record.get('ClientID');
//find the grid that was created
var me = this;
console.log(me);
var xxx = this.lookupReference('gridtextfield');
debugger;
//debugger;
var mainPortalView = Ext.getCmp('mainportalID');
var targetGrid = mainPortalView.down('grid');
//find the store associated with that grid
var targetStore = targetGrid.getStore();
//load store
targetStore.load({
params: {
user: 'stephen',
pw: 'forero',
cid: selectedCID
}
//callback: function (records) {
// Ext.each(records, function (record) {
// console.log(record);
// });
// console.log(targetStore);
//}
});
},
added listeners to MainPortal.js
var myStore = Ext.create('ExtApplication1.store.PositionsStore');
var gridSummary = Ext.create('Ext.grid.Panel', {
store: myStore,
width: 600,
title: 'my first grid',
columns: [
{
text: 'AcctNum',
dataIndex: 'AcctNum',
width: 100
},
{
text: 'AcctShortCode',
dataIndex: 'AcctShortCode',
flex: 1
},
{
text: 'Exchange',
dataIndex: 'Exchange',
width: 200
}
],
listeners: {
destroy: function () {
debugger;
}
}
});
Ext.define('ExtApplication1.view.main.MainPortal', {
extend: 'Ext.panel.Panel',
xtype: 'mainportal',
alias: 'widget.mainportalAlias',
//id: 'mainportalID',
itemId: 'mainportalID',
html: 'user... this is the main portal window',
autoScroll: true,
bodyPadding: 10,
items: [
gridSummary
],
listeners: {
destroy: function () {
debugger;
}
}
});
Can anyone tell me how I can keep the position of my scroll bar when my datastore reloads? Below is the code I have for the window/grid and refresh code. Everytime refreshActions executes the scroll bar scrolls to the top of the grid:
preserveScrollonRefresh does not work in this scenario
View
Ext.define('Tool.view.ActionView',{
extend: 'Ext.window.Window',
xtype: 'toolactions',
requires:[
'Tool.view.ActionController'
],
controller: 'actions',
viewModel: {},
layout: { type: 'border' },
closeAction: 'hide',
title: 'Actions',
store: 'Task',
width: 1500,
height: 800,
items: [{
id: 'ChangeLog',
xtype: 'grid',
selType: 'rowmodel',
split: true,
title: 'Log',
region: 'south',
width: 600,
height: 300,
bind: {store: '{tasks}'},
columns: {
defaults: {
width: 175
},
items: [
{ text: 'Script Command', dataIndex: 'command_script', flex: 1},
{ text: 'Output', dataIndex: 'command_output', width: 250, flex: 1 },
{ text: 'Status', dataIndex: 'state', width: 250, flex: 1 }]
},
bbar: ['->',{
xtype: 'button',
text: 'Refresh',
listeners: {
click: 'refreshActions'
}
}
]
}]
Refresh Code
refreshActions: function() {
var me = this;
this.currentRecord.tasks().load(function(records, operation, success) {
if(!operation.wasSuccessful()) {
var message = "Failed to load data from server.";
if(operation.hasException())
message = message + " " + operation.getError();
var app = Tool.getApplication();
app.toast(message,'error');
}
else {
me.configureButtons();
me.configureAutoRefresh();
}
});
}
Detect for Auto Refresh
configureAutoRefresh: function() {
var autoRefresh = false;
var maxId = this.getMaximumId(this.currentRecord.tasks());
var maxRecord = this.currentRecord.tasks().getById(maxId);
if(maxRecord.data.state=='1' || maxRecord.data.state=='0') {
autoRefresh = true;
}
if(autoRefresh == true) {
if(this.autoRefreshTask == null) {
var me = this;
var task =
{
run: function() {
me.refreshActions();
return true;
},
interval: 2000 // 2 seconds
};
this.autoRefreshTask = Ext.TaskManager.start(task);
}
}
else if(this.autoRefreshTask != null) {
Ext.TaskManager.stop(this.autoRefreshTask);
this.autoRefreshTask = null;
}
}
As I see it you have 2 options that not necessarily exclude each other:
If a grid row is selected:
Before the store reload get the selected record using getSelection() method and after the action is complete use the ensureVisible( record, [options] ) method to scroll that record into view.
If no row is selected:
Before the store reload get the current scroll position (I assume in your case is X) using the getScrollX() method and after the action is complete use setScrollX(x, [animate]) to get back to the previous scroll position.
i am very new to EXT.js; i need to submit the form when ENTER is pressed below is my code but i dont know what to call in the listener of the password field here is my code:
ie:what is the function to call in the listener
<script type="text/javascript">
Ext.onReady(function() {
Ext.tip.QuickTipManager.init();
Ext.create("Ext.container.Viewport", {
layout: "border",
rtl: <spring:theme code='theme.rtl' text='false' />
});
Ext.create("Ext.window.Window", {
title: "<spring:message code='title.login' text='Login' />",
height: 310,
width: 450,
closable: false,
layout: "border",
items: [{
xtype: "panel",
border: false,
bodyCls: "login-header",
height: 160,
region: "north"
}, {
id: "<%=loginFormId%>",
url: "<spring:url value='/secure/auth'/>",
xtype: "form",
layout: "form",
region: "center",
bodyPadding: 10,
border: false,
buttons: [{
handler: function() {
var form = this.up("form").getForm();
if (form.isValid()) {
Ext.getCmp("<%=submitBtnId%>").disable();
form.standardSubmit = true;
form.method = "POST";
form.submit();
}
},
id: "<%=submitBtnId%>",
text: "<spring:message code='button.submit' text='Submit' />"
}, {
handler: function() {
var form = this.up("form").getForm();
form.reset();
},
id: "<%=clearBtnId%>",
text: "<spring:message code='button.clear' text='Clear' />"
}],
defaultType: "textfield",
defaults: {
msgTarget: "side",
labelWidth: 100
},
items: [{
fieldLabel: "<spring:message code='input.username' text='Username' />",
name: "selfcare_username"
}, {
fieldLabel: "<spring:message code='input.password' text='Password' />",
name: "selfcare_password",
enableKeyEvents:true,
inputType: "password",
listeners: {
scope: this,
specialkey: function(f, e) {
if (e.getKey() === e.ENTER) {
}
}
}
}]
}]
}).show();
<c:if test="${not empty param.error}">
var errorMsg = "<c:out value="${SPRING_SECURITY_LAST_EXCEPTION.message}" />";
if (errorMsg !== "") {
Ext.MessageBox.show({
title: "<spring:message code='title.error' text='Error' />",
msg: errorMsg,
closable: false,
buttons: Ext.Msg.OK
});
}
</c:if>
});
</script>
These days it is better to use the defaultButton property on the form to designate the default button on the form. This is the button who's handler will handle your ENTER key.:
http://docs.sencha.com/extjs/6.0/6.0.2-classic/#!/api/Ext.panel.Panel-cfg-defaultButton
You should attach key event of the components listener, here is the sample which is working if the field not empty and pressed key ENTER or TAB inside the field.
suppliers is a JsonStore where I am loading store by params which means you can call whatever you wrote in the app.
{
xtype: 'textfield',
id: 'supplier-id',
flex: 1,
tabIndex: 1,
fieldLabel: 'SUPPLIER NO',
fieldStyle: 'text-align: right; font-size: 12pt',
margins: '0 5 0 0',
enablekeyEvents: true,
listeners: {
specialkey: function (field, e) {
if (field.getValue() != 'null') {
if (e.getKey() === e.ENTER || e.TAB) {
suppliers.load({
params: {'supplier': field.getValue(), 'type': 'supplier'},
callback: function () {
Ext.getCmp('supplier-name').setValue(suppliers.data.items[0].data['MATCH_NAME']);
}
});
}
}
},
focus: function (e) {
e.setValue('');
Ext.getCmp('supplier-name').setValue("");
suppliers.loadData([], false);
}
}
}
For Sencha:
listeners: {
specialkey: function(field, e){
if (e.getKey() == e.ENTER) {
//submitLogin();
}
}
},
Add listener with afterrender
listeners: {
afterRender: function(thisForm, options){
this.keyNav = Ext.create('Ext.util.KeyNav', this.el, {
enter: fnLogin,//give here to login function
scope: this
});
}
}
I have a extjs store which produces a grid. In the first column of the grid I have a rownumberer xtype to generate the serial number.
In the grid I can add(from other grid) or delete records through some action.While adding it copies the row number from the other grid. I want the rownumberer is to be sorted after every addition or removal.Please help.
items: [
{
xtype: 'grid',
id: 'allSurveyGrid',
store: alludfStore,
border: false,
width: 600,
height: 250,
layout: 'fit',
columns: [{
xtype: 'rownumberer'
}, {
header: 'Date',
sortable: false,
dataIndex: 'createdDt',
flex: 0.3
}, {
header: 'Question Name',
sortable: false,
dataIndex: 'questiontext',
flex: 1
}
],
selModel: {
mode: 'MULTI'
},
}, {
xtype: 'tbspacer',
height: 10
}, {
xtype: 'container',
header: false,
style: {
left: '465px'
},
layout: {
type: 'hbox'
},
items: [{
xtype: 'tbspacer',
width: 250
}, {
xtype: 'button',
text: '▼',
id: 'selectQn',
//flex: 1,
height: 40,
width: 40,
handler: function () {
//Elison : Delegate this to controller once the function is ready.
var allSurveyGrid = Ext.getCmp("allSurveyGrid");
var selModels = allSurveyGrid.getView().getSelectionModel().getSelection();
var selSurveyGrid = Ext.getCmp("addSurveyGrid");
var selectedSurveyStore = selSurveyGrid.getStore();
var countOfQuestion = 0;
if (selModels.length == 0) {
Ext.Msg.alert(' ' + 'ERROR', "Select atleast one question to add.");
return;
}
for (var c = 0; c < selectedSurveyStore.data.length; c++) {
item = selectedSurveyStore.data.items[c].data;
if (item.qName !== null && item.qName !== "") {
countOfQuestion = countOfQuestion + 1;
} else {
selectedSurveyStore.remove(selectedSurveyStore.getAt(c));
}
}
var finalCount = countOfQuestion + (selModels.length);
if (finalCount >= 16) {
Ext.Msg.alert(' ' + 'ERROR', "More than 15 UDF is not allowed");
return;
} else {
// selectedSurveyStore.add(selModels);
/* for(var x=0;x<selModels.length;x++){
selModels.getAt[0].set('questionId',selectedSurveyStore.data.length+1);
}*/
selectedSurveyStore.loadData(selModels, true);
selSurveyGrid.getView().refresh();
}
}
}, {
xtype: 'tbspacer',
width: 10
}, {
xtype: 'button',
text: '▲',
id: 'unSelectQn',
height: 40,
width: 40,
style: {
borderRadius: 0
},
//flex: 1,
handler: function () {
var selSurveyGrid = Ext.getCmp("addSurveyGrid");
var selModels = selSurveyGrid.getView().getSelectionModel().getSelection();
var selectedSurveyStore = selSurveyGrid.getStore();
selectedSurveyStore.remove(selModels);
},
}, {
xtype: 'tbspacer',
width: 250
}
]
}, {
xtype: 'tbspacer',
height: 10
}, {
xtype: 'grid',
id: 'addSurveyGrid',
store: addudfStore,
border: false,
width: 600,
height: 250,
layout: 'fit',
columns: [{
xtype: 'rownumberer'
}, {
header: 'Date',
sortable: false,
dataIndex: 'createdDt',
flex: 0.3
}, {
header: 'Question Name',
sortable: false,
dataIndex: 'questiontext',
flex: 1
}
],
selModel: {
mode: 'MULTI'
},
viewConfig: {
plugins: {
ptype: 'gridviewdragdrop'
}
}
}
]
Try calling
grid.getView().refresh();
right after you add or remove an item from the grid.
https://www.sencha.com/forum/showthread.php?240240-Refresh-RowNumberer-after-store.insert()
FYI:
/**重写rownumber*/
Ext.override(Ext.grid.RowNumberer, {
renderer: function(value, metaData, record, rowIdx, colIdx, store) {
var rowspan = this.rowspan;
if (rowspan){
metaData.tdAttr = 'rowspan="' + rowspan + '"';
}
metaData.tdCls = Ext.baseCSSPrefix + 'grid-cell-special';
//return store.indexOfTotal(record) + 1;
return store.indexOf(record) + 1;
}
});
and,refresh the grid view after insert or delete,like this:
store.insert(0,newRecord);
headerFormGrid.getView().refresh();
Edit: I deleted the code that didn't matter for the question being asked
I am trying to mimic the example given in the documentation here: http://docs.sencha.com/ext-js/4-1/#!/example/dd/dragdropzones.html.
When I click and drag inside any of the divs created by the dataview, I start selecting text instead of dragging the dataview item.
I have put console.log() statements here and there to verify that the code is firing. All of the elements that are returned by the getDragData function hold information.
I have tried adding both "draggable: true" and "enableDrag: true". Neither one allowed me to drag the div. However, "draggable: true" did make it such that when I clicked and dragged I was no longer selecting text.
I believe this is the only code that pertains to the issue:
SearchDataView.js
Ext.require('Client.store.SearchStore');
Ext.define('Client.view.SearchDataView',
{
extend: 'Ext.view.View',
alias: 'widget.SearchDataView',
config:
{
store: Ext.create('Client.store.SearchStore'),
tpl: '<tpl for=".">' +
'<div class="search-wrapper">' +
'<div class="search-icon">' +
'<img src="../../Images/icons/Person50x50.jpg" />' +
'</div>' +
'<div class="search-text">' +
'<span class="title">{FirstName} {LastName}</span>' +
'<span class="address">address, city, state zip</span>' +
'<span class="info">DOB: 7/3/1970</span>' +
'</div>' +
'</div>' +
'</tpl>',
itemSelector: 'div.search-wrapper',
emptyText: 'Nobody in database',
deferEmptyText: false,
singleSelect: true,
listeners:
{
render: initializeSearchDragZone
}
}
}
);
function initializeSearchDragZone(v) {
v.dragZone = Ext.create('Ext.dd.DragZone', v.getEl(), {
getDragData: function (e) {
var sourceEl = e.getTarget(v.itemSelector, 10), d;
if (sourceEl) {
d = sourceEl.cloneNode(true);
d.id = Ext.id();
return v.dragData = {
sourceEl: sourceEl,
repairXY: Ext.fly(sourceEl).getXY(),
ddel: d,
searchData: v.getRecord(sourceEl).data,
sourceStore: v.store
}
}
},
getRepairXY: function () {
return this.dragData.repairXY;
}
});
}
Viewport.js
Ext.require('Client.view.SearchPanel');
Ext.require('Client.view.DesktopPanel');
Ext.define('Client.view.Viewport',
{
extend: 'Ext.container.Viewport',
initComponent: function () {
Ext.apply(this,
{
layout: 'border',
items:
[
{
region: 'north',
margins: 5,
height: 30,
xtype: 'container'
},
{
region: 'west',
margins: '0 5 0 5',
flex: .25,
collapsible: true,
titleCollapse: true,
xtype: 'SearchPanel'
},
{
region: 'center',
xtype: 'DesktopPanel'
},
{
region: 'east',
margins: '0 5 0 5',
width: 200,
collapsible: true,
titleCollapse: true,
collapsed: true
},
{
region: 'south',
margins: '0 5 5 5',
flex: .3,
split: true
}
]
}
);
this.callParent(arguments);
}
}
);
SearchPanel
Ext.require('Client.view.SearchForm');
Ext.require('Client.view.SearchDataView');
Ext.require('Client.view.AddTrashForm');
Ext.define('Client.view.SearchPanel',
{
extend: 'Ext.panel.Panel',
alias: 'widget.SearchPanel',
config:
{
items:
[
{
xtype: 'SearchForm'
},
{
xtype: 'SearchDataView'
},
{
xtype: 'AddTrashForm'
}
]
},
cls: 'searchpanel'
}
);
I added layout config to SearchPanel, and drag started working:
layout:
{
type: 'vbox',
align: 'stretch'
}
It changes some of my layout, so I will have to adjust css now, but drag is working.