I've specified showCondition custom property for some items inside view config. How can I query all such components?
I've tried Ext.ComponentQuery.query(). The problem is, that query() returns to me correct number of elements, but there are not 'real' components, that is, when I try to do elements[0].hide(), is makes no effect.
I noticed, that when I get the same element using ref in controller class, hide() works perfectly.
After that I've console.log-ed results of both ways of getting the element and noticed strange things. First, returned element have different html id attributes (textfield-1115 and textfield-1089). Second, the element which is returned by query() method already has hidden=true property (that's why hide() has no effect). Both elements are textfield components.
Below are related code parts. The important is in onAfterRenderForm().
In view:
Ext.define('MyApp.view.Test', {
extend: 'Ext.container.Container',
alias: 'widget.test',
layout: 'fit',
initComponent: function() {
Ext.apply(this, {
items: [
{
title: 'form',
itemId: 'myForm',
xtype: 'form',
items: [
{
xtype: 'textfield',
fieldLabel: 'code',
showCondition: 'is-root',
allowBlank: false,
vtype: 'alphanum'
}
]
}
]
});
this.callParent(arguments);
}
});
In controller:
Ext.define('MyApp.controller.Test', {
extend: 'Ext.app.Controller',
requires: [
'MyApp.view.Test'
],
refs: [
{
ref: 'codeField', selector: '[showCondition]'
}
],
init: function() {
this.control(
{
'#myForm': {
afterrender: this.onAfterRenderForm
}
);
},
onAfterRenderForm: function(oForm) {
var elements = oForm.query('[showCondition]');
console.log(elements[0]);
console.log(this.getCodeField());
if(elements[0].id == this.getCodeField().id)
alert('Elements are not the same!!!');
}
});
This:
refs: [{
ref: 'codeField', selector: '[showCondition']
}]
is subtly different from oForm.query('[showCondition]')[0].
For the ref you a grabbing the first component found with a defined showCondition value. In oForm.query, you are grabbing the first component found that is a child of oForm which has a defined showCondition value.
That means that if you have other fields in any view within your app that have showCondition defined on them, the call to the generated getter for the ref could return any one of those fields. It depends on what order Ext decides to put them in.
It sounds to me like a couple things are happening
You have other fields in your app that have showCondition defined but are not on the form your controller is looking at.
Your view is being rendered in the hidden state. Is is being added as an item in a card layout or something like that?
Related
Is it possible that two or more views in extjs share the same viewModel?
For example, if I have two views as presented in the code below, both views do not have the same view model, but instances of that viewModel. Maybe that was intention, but how to achieve to have global field that would be accessible across several views with the same declaration for viewModel.
So, I would like to bind the field name, so the change in one view automatically causes changes in all corresponding views.
Ext.define('MainModel', {
extend: 'Ext.app.ViewModel',
alias: 'viewmodel.main',
data: {
name : '',
}
});
Ext.define('View1', {
extend: 'Ext.Container',
xtype: 'view1',
viewModel: {
type: 'main'
},
items: [{
xtype: 'textfield',
fieldLabel: 'Name:',
bind: {
value: '{name}',
}
}
]
});
Ext.define('View2', {
extend: 'Ext.Container',
xtype: 'view2',
viewModel: {
type: 'main'
},
items: [{
xtype: 'textfield',
fieldLabel: 'Name:',
bind: {
value: '{name}',
}
}
]
});
I don't think it is possible to do this they way you are asking. When you bind to a data element the system will first check the current components view model if it is does not find the value it will go up the parent and look and will continue this until it finds a match or there are no more parents. So if you want to share data in a view model you should store the data in a parent viewModel.
Here is a fiddle
The fields have two way binding so as you type in one it will update the view model and then the other field will be updated. On the fiddle edit each field and the other field will change. You can set either the property in the viewModel or either one of the two fields.
This fiddle shows setting the value of the field and the data in the parent viewModel via a button.
how do I point this menu item click to launch a method in a controller.
The item click is being hit successfully but the error message states No method named "onDownloadTopdayRecapContextButton" on ExtApplication4.view.main.MainController. That is the problem, you can see the view's controller is portalRealtime-portalRealtime.
So somehow its pointing to the wrong controller. Can someone show me what I am doing wrong?
menu code
var contextMenuTopday = Ext.create('Ext.menu.Menu', {
items: [{
text: 'Download Topday Recap',
iconCls: 'downloadIcon',
listeners: {
click: 'onDownloadTopdayRecapContextButton'
}
grid menu is held in
Ext.define('ExtApplication4.view.portalRealtime.PortalRealtime', {
extend: 'Ext.panel.Panel',
xtype: 'app-portalRealtime',
itemId: 'portalRealtimeItemID',
requires: [
'ExtApplication4.view.portalRealtime.PortalRealtimeController',
'Ext.form.action.StandardSubmit'
],
controller: 'portalRealtime-portalRealtime',
title: 'Main Portal',
layout: {
type: 'vbox'
},
items: [
//i deleted some grid code here
collapsible: true,
collapseDirection: 'left',
listeners: {
itemcontextmenu: function (view, rec, node, index, e) {
e.stopEvent();
contextMenuTopday.showAt(e.getXY());
return false;
}
{
You are creating the context menu outside of your view, so it does not inherit your controller.
Before using the below code please scroll to the bottom of the answer for a better solution, but this hopefully shows what is the cause of your issue.
If this doesn't solve your issue, please comment and provide a more complete code example, and I will update my answer
In these cases you can pass a controller manually, but you need to pass as a parent, as you get all kinds of problems if you re-use the same controller on multiple components (when you destroy one for example, it destroys the controller, leaving the other without)
So you could create from within your view like so:
Ext.define('ExtApplication4.view.portalRealtime.PortalRealtime', {
initComponent:function(){
this.callParent(arguments);
this.contextMenuTopday = Ext.create('Ext.menu.Menu', {
controller:{
parent: this.getController()
},
items: [{
text: 'Download Topday Recap',
iconCls: 'downloadIcon',
listeners: {
click: 'onDownloadTopdayRecapContextButton'
}
}]
});
}
Then rather than use a variable to access the context menu you can access the contextMenuTopday property, as you are within a child item you may need to traverse to your actual view, the simplest way of doing this is via the up method available on components, you would need to make sure you include an xtype to do this:
Ext.define('ExtApplication4.view.portalRealtime.PortalRealtime', {
xtype:'portalrealtime'
Then from within the context menu you can do:
itemcontextmenu: function (view, rec, node, index, e) {
this.up('portalrealtime').contextMenuTopday.showAt(e.getXY());
}
A better way
Best illustrated looking at this fiddle: https://fiddle.sencha.com/#view/editor&fiddle/1qpn
Define your menu as its own class:
Ext.define('Example.ContextMenu', {
xtype:'testmenu',
extend:'Ext.menu.Menu',
items: [{
text: 'Download Topday Recap',
iconCls: 'downloadIcon',
listeners: {
click: 'onDownloadTopdayRecapContextButton'
}
}]
});
Use a method on your controller for the itemcontextmenu event (This is good anyway as it provides a better separation of concerns):
itemcontextmenu: 'showContextMenu'
Then add a a few new methods to your portalRealtime-portalRealtime controller:
getContextMenu:function(){
if(!this.contextMenu){
this.contextMenu = this.getView().add({xtype:'testmenu'});
}
return this.contextMenu;
},
showContextMenu:function (view, rec, node, index, e) {
// we can't use showAt now we have added this to our view, as it would be positioned relatively.
this.getContextMenu().show().setPagePosition(e.getXY());
}
What we are doing here is adding the context menu to the view, so it inherits the controller (and a viewmodel if provided).
The best way to call methods on your controller for listeners/button handlers etc is to just specify the method name as a string i.e.:
listeners:{
itemcontextmenu: 'showContextMenu'
}
This will automatically look up the responsible controller and use the correct method.
If you need to call from within a component you will find that this.getController() fails unless you call on the actual component the controller is attached to - i.e. you are calling from a child component. In these cases you can use this.lookupController() to find the inherited/responsible controller and then call any methods from here e.g. this.lookupController().myMethod()
In my app I have the viewport with one item, a main view, which it is a simple class extending from Ext.container.Container.
I have a main controller too, and I'm trying to get the view instance, so dynamically I can push the corresponding items if the user is logged or not.
I've tried using views: ['MainView'], refs[ { selector: 'thextype' }], etc with no luck.
I was using the reference (ref) in sencha touch to do this kind of things, can you help me with Extjs v4.2 ?
Just for clarification, I'm not trying to get the DOM element, I'm trying to get the view instance with the associated methods.
Thanks in advance,
Define xtype of your view:
xtype: 'mainview'
and then in your controller:
requires: ['...'] // your view class
// ...
refs: [{
ref: 'mainView',
selector: 'mainview' // your xtype
}]
and then you can get the instance of your view in the controller with this.getMainView()
I've tried that without good results.
What I'm trying to do is something like. Based on your response should work
Ext.define('MyApp.view.MainView', {
extend: 'Ext.container.Container',
alias: 'widget.mainContainer',
cls: ['mainContainer'],
items: [
{
xtype: 'panel',
items: [
{
html: "my view"
}
]
}
]});
Ext.define('MyApp.controller.MainController', {
extend: 'MyApp.controller.BaseController',
refs: [
{
ref: 'mainContainer',
selector: 'mainContainer'
}
],
init: function() {
this.getApplication().on({
openDashboard: this.onOpenDashboard
});
},
onOpenDashboard: function() {
var mainContainerView = this.getMainContainer();
mainContainerView.showSomething(); //mainContainerView should be an instance of the view.
}});
Where, openDashboard event is fired if after a login success.
After some time debugging, it seems the problem was the context where it was being called the function.
I've added in the init method a line like:
var openCrmDashboardFn = Ext.Function.bind(this.onOpenCrmDashboard, this);
and it worked.
Thank you!
In my MVC application I have a controller defined like this:
Ext.define('NE.controller.MyController', {
extend: 'Ext.app.Controller',
stores : [...'StoreAgents'..],
views: [ ...'MyView'...], // * alias w_view
init: function() {
this.control({
'w_view': {
render: function() { this.getStore('StoreAgents').load(); }
}
});
}
});
And in the view MyView I have a combobox defined like this:
{
xtype: 'combobox',
name : 'id_agent',
forceSelection: true,
fieldLabel: 'Agent',
store: 'StoreAgents',
queryMode: 'local',
displayField: 'name',
valueField: 'id'
}
I would expect combobox list to be updated every time the view is rendered, am I wrong?
Currently the combobox remains with no options, even if I see (through firebug) that the application fires the request which correctly returns all agents data.
Furthermore, I noticed that whenever I browse through another view, managed by another controller, which in turn declares another StoreAgent and calls its load() method.. well, if I come back, now I see the combobox populated.
What I am missing?
Thank you
Edit:
I noticed that the store is {buffered: true}. If I switch it to false, then the store fires the 'datachange' event; otherwise it does not. So the question now is: why if buffering is enabled the load() does not fire 'datachange'?
This may be due to a filtered out situation. If the combobox is being filled with a value before the store loads, a filter will be put on the store that filters out all the values before they exist, and then when new records are added they are not displayed either. Try this.
init: function() {
this.control({
'w_view': {
render: function() { this.getStore('StoreAgents').load();
this.getStore('StoreAgents').clearFilter();}
}
});
}
I'm trying to create a specialized class which should contain, among other things, a link and a image.
I have something like:
Ext.define('Macros.app.ribbonAction', {
extend: 'Ext.Component',
//extend: 'Ext.panel.Panel',
alias: 'widget.ribbonAction',
initComponent: function () {
Ext.apply(this, {
items:[
{}
]
}
);
this.callParent(arguments);
}
});
What's the best way to add a link (bound to a javascript function) to the items collection?
The closest I can find is a button, but I'd really prefer a good old-fashioned link.
(I'm using ExtJs 4)
How about standard box?
{
xtype: 'box',
id: 'myLinkId',
autoEl: 'Link'
}
And add this to the container.
The only problem is that you need to assign event handler for 'click' event and you can do this only after element has been rendered.