ExtJS 7.4 creating a right-click contextmenu using Modern - extjs

I need to create a contextmenu in ExtJS 7.4 on right click but there's only childtap which only triggers on left-click event.
Is there a way to trigger that event or another for right-click?

I added two solutions. The first is a component solution (here Grid) and the second a global one.
COMPONENT
The solution would be the childcontextmenu listener.
Have a look at the following example (Modern toolkit 6+7)
const menu = new Ext.menu.Menu({
items: [{
text: 'Menu Item 1'
}, {
text: 'Menu Item 2'
}]
});
Ext.define('MyApp.MyGrid', {
extend: 'Ext.grid.Grid',
store: Ext.create('Ext.data.Store', {
fields: ['name', 'email', 'phone'],
data: [{
name: 'Lisa',
email: 'lisa#simpsons.com',
phone: '555-111-1224'
}]
}),
columns: [{
text: 'Name',
dataIndex: 'name',
width: 200
}],
listeners: {
childcontextmenu: function (grid, eObj) {
grid.deselectAll();
grid.setSelection(eObj.record);
menu.showAt(eObj.event.getX(), eObj.event.getY());
eObj.event.stopEvent()
}
}
})
GLOBAL
I can show you how to fire global events, but the problem is the target. In the following example I am using document but you can also use any view.el.on and the target will be always the view. More answers might be found here
Ext.getDoc().on(
'contextmenu',
function(e){
Ext.GlobalEvents.fireEvent('contextmenu', e);
},
Ext.GlobalEvents
);
Ext.GlobalEvents.on('contextmenu', function(eObj) {
console.log('Who dares to RightClick me?');
});

Related

Extjs6.2 Modern toolkit- Extend a textbox

I am still learning EXTJs and one of the thing I was trying to to was extend a component. Below is my example:
Ext.define('MyApp.view.CustomTextField',{
extend: 'Ext.field.Text',
xtype: 'customtextfield',
config:
{
fieldID: null,
langID: null
},
initialize: function() {
alert("init"); //1. called before I navigate to view
Call a controller method here
this.callParent(arguments);
},
initComponent: function () {
alert("initComp"); //2. not called at all
Call a controller method here
this.callParent(arguments);
}
I want to call a controller method to validate if user has permission to see this field and accordingly do next actions. I want this validation to happen when I navigate to the view
I used this custom field in my View as:
xtype: 'fieldset',
margin: 10,
bind: '{workOrders}',
title: 'Dispatch Information',
items: [
{
id: 'Tag',
xtype: 'customtextfield',
name: 'Tag',
label: 'Tag',
bind: '{Tag}',
labelAlign: 'top'
},
But the initComponent is never fired.
The initialize is fired to soon ,even before my stores are loaded. How do I properly extend this control?
In ExtJS 6 modern there is no initComponent event for textfield . initComponent event have
in classic for textfield.
For calling events in controller you need to create controller and define to you view.
In this FIDDLE, I have created a demo using form, ViewController, textfield and ViewModel. I hope this will help/guide you to achieve your requirements.
For more details please refer ExtJS Docs.
CODE SNIPPET
Ext.application({
name: 'Fiddle',
launch: function () {
//Define the cutsometext from extending {Ext.field.Text}
Ext.define('CustomText', {
extend: 'Ext.field.Text',
xtype: 'customtext',
labelAlign: 'top',
listeners: {
initialize: 'onInitializeCutsomText'
}
});
Ext.define('FormModel', {
extend: 'Ext.app.ViewModel',
alias: 'viewmodel.formmodel',
data: {
user: {
firstName: 'Narendra',
lastName: 'Jadhav',
email: 'narendrajadhav105#gmail.com'
},
permissionCng: {
firstName: false,
lastName: false,
email: true,
isAdmin: false
}
}
});
//Define the FormController from extending {Ext.app.ViewController}
Ext.define('FormController', {
extend: 'Ext.app.ViewController',
alias: 'controller.formctn',
onInitializeCutsomText: function (textfield) {
var permissionCng = this.getViewModel().get('permissionCng');
// Here is just basic example for disabled textfield on initialize event.
//In your case you can put your requirement.
textfield.setDisabled(permissionCng[textfield.getName()]);
}
});
//Creating form
Ext.create('Ext.form.Panel', {
fullscreen: true,
viewModel: {
type: 'formmodel'
},
controller: 'formctn',
items: [{
xtype: 'fieldset',
title: 'Personal Info',
defaults: {
xtype: 'customtext' //Here I am using {customtext}
},
items: [{
label: 'First Name',
name: 'firstName',
bind: {
value: '{user.firstName}',
//You can also use like property
//hidden:'{permissionCng.firstName}'
}
}, {
label: 'Last Name',
name: 'lastName',
bind: {
value: '{user.lastName}',
//You can also use like property
//hidden:'{permissionCng.firstName}'
}
}, {
label: 'Email Id',
name: 'email',
bind: {
value: '{user.email}',
//You can also use like property
//hidden:'{permissionCng.firstName}'
}
}, {
label: 'Admin Name',
name: 'isAdmin',
bind: {
value: '{user.isAdmin}',
//You can also use like property
hidden: '{!permissionCng.isAdmin}'
}
}]
}]
});
}
});

EXTJS 6 MVVM basics confusion

I'm new to EXTJS 6 and MVVM and I'm not sure if I'm understanding things properly. Please help me with this basic example and whether this is the correct way to do things in the MVVM architecture
I started by creating the sample app via sencha cmd. I see that it created in /view/main/MainModel.js a "variable"? named loremIpsum. I see in the main view there are some bindings to loremIpsum.
I guess my question is, if I wanted to create a 2nd view, like a popup window from the main view, how could I access loremIpsum from Main's viewModel?
I'm getting confused as to whether I should be "sharing" Main's viewModel, or whether I should be moving loremIpsum to model/Base.js which I guess would be a shared model, and then I could have multiple viewModels looking at that view?
What is MVVM?
Model-View-ViewModel (MVVM) is another architectural pattern for writing software that is largely based on the MVC pattern. The key difference between MVC and MVVM is that MVVM features an abstraction of a View (the ViewModel) which manages the changes between a Model’s data and the View‘s representation of that data (i.e. data bindings) — something which typically is cumbersome to manage in traditional MVC applications.
The MVVM pattern attempts to leverage the architectural benefits of MVC (separation of functional responsibilities) yet also provides the additional advantages of data binding. The result is that the Model and framework perform as much work as possible, minimizing (and in some cases eliminating) application logic that directly manipulates the View.
Elements of the MVVM pattern include:
The Model describes a common format for the data being used in the application, just as in the classic MVC pattern.
The View represents the data to the user, just as in the classic MVC pattern.
The ViewModel is an abstraction of the view that mediates changes between the View and an associated Model. In the MVC pattern, this would have been the responsibility of a specialized Controller, but in MVVM, the ViewModel directly manages the data bindings and formulas used by the View in question.
MVVM: An Example
In this FIDDLE, I have created a demo usng grid and window. I hope this will help you to understand concept of MVVM.
CODE SNIPPET
//Define model
Ext.define('NJDHV10.model.UserModel', {
extend: 'Ext.data.Model',
//Define fields in store
fields: ['fullname', 'email', 'phone'],
});
//Define Store
Ext.define('NJDHV10.store.UserStore', {
extend: 'Ext.data.Store',
model: 'NJDHV10.model.UserModel',
alias: 'store.userstore',
data: [{
fullname: 'Chunk P',
email: 'alias#njdhv10.com',
phone: 9827623311
}, {
fullname: 'Champ M',
email: 'super#njdhv10.com',
phone: 9827623312
}, {
fullname: 'David W',
email: 'david#njdhv10.com',
phone: 9827623313
}, {
fullname: 'Marin d',
email: 'marin#njdhv10.com',
phone: 9827623314
}]
});
//Define ViewModel for user list
Ext.define('NJDHV10.view.UserListModel', {
extend: 'Ext.app.ViewModel',
alias: 'viewmodel.userlistvm',
stores: {
userstore: {
type: 'userstore'
}
}
});
//Define Controller
Ext.define('NJDHV10.view.UserController', {
extend: 'Ext.app.ViewController',
alias: 'controller.user',
/**
* This function will fire on grid item click
* #param { Ext.selection.RowModel} selModel
* #param {Ext.data.Model} rec
*/
onGridItemClick: function (selModel, rec) {
var form = Ext.ComponentQuery.query('userform')[0];
if (!form) {
form = Ext.create('NJDHV10.view.UserForm');
}
if (form.isHidden()) {
form.show();
}
form.getViewModel().set('userData', rec)
}
});
//Define ViewModel for user form data
Ext.define('NJDHV10.view.UserFormModel', {
extend: 'Ext.app.ViewModel',
alias: 'viewmodel.userformvm',
data: {
userData: null
}
});
//User form for entry
Ext.define('NJDHV10.view.UserForm', {
extend: 'Ext.window.Window',
closable: true,
width: 320,
//Define xtype
// xtype: 'userform',
alias: 'widget.userform',
model: true,
autoDestroy: true,
floating: true, // make this panel an absolutely-positioned floating component
//provide viewmodel to form
viewModel: {
type: 'userformvm'
},
title: 'User Form',
layout: {
align: 'stretch',
type: 'vbox'
},
defaults: {
xtype: 'textfield',
margin: 10,
labelAlign: 'top'
},
items: [{
fieldLabel: 'Full Name',
bind: {
value: '{userData.fullname}' //bind data using viewmodel in form
},
name: 'fullname'
}, {
fieldLabel: 'Email',
bind: {
value: '{userData.email}' //bind data using viewmodel in form
},
name: 'email',
vType: 'email'
}, {
fieldLabel: 'Phone Number',
bind: {
value: '{userData.phone}' //bind data using viewmodel in form
},
name: 'phone'
}]
});
//Define user grid
Ext.define('NJDHV10.view.UserGrid', {
extend: 'Ext.grid.Panel',
xtype: 'usergrid',
title: 'User List (Click to any row and see details in window)',
controller: 'user',
//provide view model to gridQA8sjZHC
viewModel: {
type: 'userlistvm'
},
//Bind store to grid
bind: {
store: '{userstore}'
},
//Add listeners into item click
listeners: {
itemclick: 'onGridItemClick'
},
columns: [{
xtype: 'rownumberer'
}, {
text: 'Name',
flex: 1,
dataIndex: 'fullname'
}, {
text: 'Email',
dataIndex: 'email',
flex: 1
}, {
text: 'Phone Number',
flex: 1,
dataIndex: 'phone'
}]
});
Ext.application({
name: 'NJDHV10',
launch: function () {
//Create grid to view
Ext.create('NJDHV10.view.UserGrid', {
layout: 'fit',
renderTo: Ext.getBody()
});
}
});

Ext JS grid panel events

I am developing my first EXT js mvc application. I have a problem adding events listeners to my grid panel from controller class.
My question is why i am getting below error while adding event handlers through 'on' method using below code
Error:
Uncaught TypeError:
gridPanel.on is not a function
at constructor.init
Relevant Code:
var gridPanel = Ext.ComponentQuery.query('userlist');
gridPanel.on('itemdblclick', this.editUser, gridPanel);
I am able to add handlers through a different method though but i am trying to figure out what is wrong with adding through 'on' method on grid panel reference. Any help is appreciated.
Thanks
Here is my complete controller class
Ext.define('AM.controller.Users', {
extend: 'Ext.app.Controller',
views: ['user.List'],
init: function() {
var gridPanel = Ext.ComponentQuery.query('userlist');
gridPanel.on('itemdblclick', this.editUser, gridPanel);
},
editUser: function() {
console.log('User edit has begun..');
}
}, function() {
});
And my grid panel class:
Ext.define('AM.view.user.List', {
extend: 'Ext.grid.Panel',
alias: 'widget.userlist',
title: 'All Users',
id: 'usergrid',
initComponent: function() {
this.store = {
fields: ['name', 'email'],
data : [
{name: 'Ed', email: 'ed#sencha.com'},
{name: 'Tommy', email: 'tommy#sencha.com'}
]
};
this.columns = [
{header: 'Name', dataIndex: 'name', flex: 1},
{header: 'Email', dataIndex: 'email', flex: 1}
],
this.callParent(arguments);
}
});
Ext.ComponentQuery.query('userlist') returns an array of found components - so you need to select the first item in the array to actually get your component:
Ext.ComponentQuery.query('userlist')[0]
The Ext.first('userlist') shorthand makes this simpler. (As #sceborati mentioned in the comments)
When you use component query you are querying by its xtype*
You have also specified an id in your component, which is OK if you will only have one instance on a page, (as soon as you have more than one - you should not use ids)
You can find components much quicker using Ext.getCmp('componentid') so Ext.getCmp('userlist') would also work for you.
All that being said, to make better use of ExtJs MVC capabilities, you could switch to using a ViewController, and then you don't need to do any lookups for your components at all:
Ext.define('AM.controller.Users', {
extend: 'Ext.app.ViewController',
alias: 'controller.userlist',
editUser: function () {
console.log('User edit has begun..');
}
});
Ext.define('AM.view.user.List', {
extend: 'Ext.grid.Panel',
xtype: 'userlist',
title: 'All Users',
id: 'usergrid',
controller: 'userlist',
store: {
fields: ['name', 'email'],
data: [{
name: 'Ed',
email: 'ed#sencha.com'
}, {
name: 'Tommy',
email: 'tommy#sencha.com'
}]
},
columns: [{
header: 'Name',
dataIndex: 'name',
flex: 1
}, {
header: 'Email',
dataIndex: 'email',
flex: 1
}],
listeners:{
itemdblclick:'editUser'
}
});
Notice the alias config added to controller, and the corresponding controller config on the view.
This means you can specify listeners just by providing the method name on the controller.
listeners:{
itemdblclick:'editUser'
}
You may also notice I have moved your column and store configuration out of the initComponent method - your use of initComponent would work, but this method is much cleaner.
I have created a fiddle to demonstrate this updated code:
https://fiddle.sencha.com/#view/editor&fiddle/1qvu
You also seem to be passing an extra function as a third argument when defining your controller - you should remove this.
* you have used alias:'widget.userlist' which is the same as xtype:'userlist'
The query method on Ext.ComponentQuery returns an array of components. So you would need to provide an index, like:
var gridPanel = Ext.ComponentQuery.query('userlist')[0];

Seems like ViewModel persists after its View is destroyed in extjs

I'm sure there's something I'm missing here and I just can't see what it is.
I have demo project I'm building in extjs 6. In it I have a grid of inventory items.
Ext.define("InventoryDemo.view.inventory.list.Inventory",{
extend: "Ext.container.Container",
xtype: 'inventory',
requires: [
"InventoryDemo.view.inventory.list.InventoryController",
"InventoryDemo.view.inventory.list.InventoryModel"
],
controller: "inventory-inventory",
viewModel: {
type: "inventory-inventory"
},
closable: true,
listeners:{
refreshList: 'onRefreshList'
},
layout:{
type: 'hbox',
align: 'stretch'
},
items:[
{
xtype: 'grid',
flex: 1,
tbar:[
{xtype: 'button', text: 'New Item', handler: 'newInventoryItem'}
],
bind:{
store: '{inventory}'
},
listeners:{
itemclick: 'showDetails'
},
columns:[
{ text: 'Name', dataIndex: 'name', flex: 1 },
{ text: 'Price', dataIndex: 'price' },
{ text: 'Active', dataIndex: 'active' },
]
}
]
});
When you click on a row, a new detail panel is created and the selected record is linked to its viewmodel and it's added to the container view that holds the grid. I also want to use the same detail panel when creating a new inventory record so I extracted the shared logic for creating and editing so it can be reused in the controller.
Here's the list's controller:
Ext.define('InventoryDemo.view.inventory.list.InventoryController', {
extend: 'Ext.app.ViewController',
alias: 'controller.inventory-inventory',
config:{
// holds the newly created detail panel
detailsPanel: null
},
showDetails: function (grid, record, item, index, e, eOpts){
this.createDetailsPanel();
this.addTitleToDetailsPanel(record.get('name'));
// This creates the link in the new detail panel's viewmodel for the
// selected record. We specifically do NOT do this in the
// `newInventoryItem`.
details.getViewModel().linkTo('inventoryitem', record);
this.addDetailsPanelToView();
},
newInventoryItem: function (button, e){
this.createDetailsPanel();
this.addTitleToDetailsPanel('New Item');
// I thought that because the previous panel was destroyed during the
// `createDetailsPanel` method any previously linked record would not
// be linked to the new detail panel created and that not linking here
// would give me an empty detail panel.
this.addDetailsPanelToView();
},
createDetailsPanel: function (){
if(this.getDetailsPanel() !== null){
// I'm destroying any previous view here which, as I understand,
// would also destroy the the associated ViewController and ViewModel
// which would also kill any links to the viewmodel
this.getDetailsPanel().destroy();
}
details = Ext.create('InventoryDemo.view.inventory.details.Inventory',{
session: true,
listeners:{
refreshList: 'onRefreshList'
}
});
this.setDetailsPanel(details);
},
addDetailsPanelToView: function (){
this.getView().add(this.getDetailsPanel());
},
addTitleToDetailsPanel: function (title){
this.getDetailsPanel().setTitle("<h3>" + title + "</h3>");
},
onRefreshList: function (){
this.getViewModel().get('inventory').load();
}
});
The details panel being created looks like this:
Ext.define("InventoryDemo.view.inventory.details.Inventory",{
extend: "Ext.form.Panel",
requires: [
"InventoryDemo.view.inventory.details.InventoryController",
"InventoryDemo.view.inventory.details.InventoryModel"
],
controller: "inventory-details-inventory",
viewModel: {
type: "inventory-details-inventory"
},
flex: 1,
closable: true,
bodyPadding: 10,
reference: 'inventorydetails',
defaults:{
layout: 'anchor',
anchor: '50%'
},
dockedItems:[
{
xtype: 'toolbar',
dock: 'bottom',
ui: 'footer',
items:[
{xtype: 'button', text: 'Update', handler: 'updateRecord'},
{xtype: 'button', text: 'Delete', handler: 'deleteRecord'}
]
}
],
items:[
{
xtype: 'hiddenfield',
name: '_method',
value: 'PUT'
},
{
xtype: 'fieldset',
title: 'IDs',
collapsible: true,
defaults:{
xtype: 'textfield'
},
items:[
{
name: 'id',
fieldLabel: 'ID',
readOnly: true,
bind: '{inventoryitem.id}'
},
{
name: 'brand_id',
fieldLabel: 'Brand ID',
readOnly: true,
bind: '{inventoryitem.brand_id}'
}
]
},
{
xtype: 'fieldset',
title: 'Details',
defaults:{
xtype: 'textfield'
},
items:[
{
name: 'name',
fieldLabel: 'Name',
bind: '{inventoryitem.name}'
},
{
name: 'price',
fieldLabel: 'Price',
bind: '{inventoryitem.price}'
}
]
}
]
});
The problem that I'm running into is if I click on a row to view it's details (which works) and then click on the New Item button, the record that was loaded on the previous detail panel is still loaded on the new detail panel.
If I click the New Item button first I get the blank form I'm going for and if I select different item rows each of the records from the selected row loads into the detail panel correctly (it's not a situation where the record from the first row is "stuck" in the detail panel), but as soon as I select a row, the New Item button will only give me forms with the previously loaded record.
Is there something that would make a link to a viewmodel persist between the destruction and creation of two separate views/viewmodels/viewcontrollers (or is there a flaw in my controller logic that I'm just not seeing)?
if the only thing you need in the detail's viewModel is this singular property you are trying to link to, consider not using a stand-alone viewModel for the detail at all.
When your detail panel resides in the items of your inventory view, it actually has natural access to the view's viewModel (detailPanel.lookupViewModel() will return the single closest viewModel in the component tree hierarchy). Bindings should work too.
However, if you need a separate viewModel for the detail panel, you can create the detail view with an ad-hoc viewModel config that gets merged into the detail's viewModel upon instantiation.
The view:
Ext.define('APP.view.TheView',{
extend: "Ext.container.Container",
alias: 'widget.theView',
controller: 'theView',
viewModel: { type: 'theView' },
config: {
detailConfig: { xtype: 'theDetail', reference: 'detail' }
},
items: ...
};
The view's viewController:
Ext.define('APP.controller.TheView',{
extend: "Ext.app.ViewController",
alias: 'controller.theView',
onOpenDetail: function(){
var detail = this.lookupReference('detail');
if(!detail){
var view = this.getView(),
detailConfig = view.getDetailConfig(),
theProperty = this.getViewModel().get('theProperty');
detailConfig = Ext.apply({
viewModel: {
// This actually gets correctly merged with whatever there is
// in the viewModel configuration of the detail
data: { theProperty: theProperty }
}
}, detailConfig));
detail = view.add(detailConfig);
}
// Do something with the detail instance
}
};
And the detail:
Ext.define('APP.view.TheDetail',{
extend: "Ext.panel.Panel",
alias: 'widget.theDetail',
controller: 'theDetail',
viewModel: { type: 'theDetail' },
items: ...
};
Hope this helps a little! :-)

Popup with Grid as well as submit button using Extjs

In our application we are using Extjs. Now I need a pop up with a grid and a cancel and submit button. So that I can select some records from the grid and save the record to DB.
I tried Ext.Window for popup.
I think items attribute in the Ext.Window can hold only one type of object ( means the object of Ext.grid.GridPanel or form). But I need both controls.
How can I implement both controls with in a popup window?
Please give your valueable information about this.
Thanks in advance.
Given you're code submitted in the comments (btw, you can edit your question to include in the question).
You can either add multiple objects to the items array or, in this case, I would add a buttons bar to the bottom (bbar)
Here is code demonstrating this, Additionally here is working fiddle:
var myData = [
['ddd', '1111'],
['eee', '2222']
];
var store = new Ext.data.ArrayStore({
fields: [{
name: 'FLD',
type: 'string'
}, {
name: 'VAL',
type: 'string'
}]
});
store.loadData(myData);
var grid = new Ext.grid.GridPanel({
store: store,
loadMask: true,
//renderTo:Ext.getBody(),
columns: [{
header: 'FLD',
dataIndex: 'FLD'
}, {
header: 'VAL',
dataIndex: 'VAL'
}],
viewConfig: {
forceFit: true
}
});
var window = Ext.create('Ext.window.Window', {
title: 'My Title',
height: 400,
width: 600,
items: [
grid
],
bbar: [{
text: 'Save',
handler: function(btn) {
alert('Save!');
}
}, {
text: 'Cancel',
handler: function(btn) {
alert('Cancel!');
}
}]
});
window.show();

Resources