I have a grid panel like this
Ext.define('sCon.view.SalesGrid',{
extend: 'Ext.grid.GridPanel',
title: 'Sales Data',
//other params
initComponent: function(){
this.callParent(arguments);
}
});
On a click event, I want to change the title of this panel. My code inside the controller looks like this.
Ext.define('sCon.controller.SalesGridController', {
extend: 'Ext.app.Controller',
views: ['SalesGrid'],
// other handlers
changeTitle: function(newTitle){
this.getView('SalesGrid').setTitle('title_changed');
}
Currently it says that it does not have a function as setTitle(). But the docs say that grid panel has setTitle() function. I also tried changing the title using the 'title' variable like
changeTitle: function(newTitle){
this.getView('SalesGrid').title = 'title_changed';
Neither works.. Please help.
UPD: Here is some refs docs from Sencha for ExtJS 4.1.
Use refs property of your controller to get references to any Components.
In your example:
Ext.define('sCon.view.SalesGrid',{
extend: 'Ext.grid.GridPanel',
title: 'Sales Data',
alias: 'widget.salesgrid',
//other params
initComponent: function(){
this.callParent(arguments);
}
});
In Controller add:
refs: [
{ ref: 'salesGrid', selector: 'salesgrid', },
]
Then you can access your SalesGrid view from anywhere in your controller like this.getSalesGrid(); method.
In Your case:
changeTitle: function(newTitle){
this.getSalesGrid().setTitle('title_changed');
}
Note
In the decribed case webbandit answer is the best for accessing a view instance, this one
stays for clarification on the use of autogenerated getters.
The selector Method you use just gives you the class and not an instance of it!
You should do it this way
this.getView('SalesGrid').create().setTitle('title_changed');
See API
Related
I have this button class defined as follows :
Ext.define('Mine.view.buttons', {
extend: 'Ext.container.Container',
alias: 'widget.MyClass',
requires: [
'Mine.view.Main',
'Mine.view.newDashboard'
],
xtype: 'myclass',
items: [{
xtype: 'button',
html: '<img src=\"../../iDeviceMap.png\" width="76" height="76"/>',
text: '',
width: 76,
height: 76,
handler: function() {
this.redirectTo('main', true);
}]
});
Which i embed in a Panel view along with other components (composing the main view) defined in another class.
My problem is that this.redirectTo('main', true); doesn't work. How to do the redirection with the right "this" that of the main view where the button is defined (say Mine.view.Main) ?
Q2 : how to call the parent view (Mine.view.Main) from the handler of the button?
Thanks.
The button handler should be a function in the controller. The method in the controller with then call the redirectTo method.
Or you could get the panel's controller in the button's handler and call it from there.... best to do it in the controller so if you write a different view for a phone or tablet each view can share the controller.
A fiddle showing switching panels
Same fiddle so you can see the hash change
EDIT: #ltes - I wrote a fiddle that did what you wanted it to do. You ask a question about two methods. It would be much faster for you to simple review the documentation on each of these methods.
Ext.create method
Ext.Container down method
You don't understand the tool kit you are using. Ext.Create will create a new object this.getView().down and up() look in the hierarchy to find a component. If you want to replace a panel then use the remove() and add() method on the panel. YOu don't have to execute Ext.create when you add you can simply pass a config:
panel.add({
xtype: 'panel',
title: 'This is my new panel',
html: 'new panel data'
}
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()
What I want to achieve is very simple, I want to have a main menu all across my application, with the same functionality for all the views.
I would like to create a view that contains exclusively the menu portion plus its own viewcontroller. What would be the [best] way to achieve this?
I am using ExtJS 5 implementing the MVVM paradigm.
Thanks!
This is a pretty broad question about how to architect the app that is pretty difficult to answer without knowing more about other parts of the app.
Generally, anything application global (that is not the application container/viewport) is probably easiest to implement with MVC. Menu controller (MVC controller) would listen to menu events and then it would drill down the component hierarchy to call components' API methods to execute the actions.
I could be more specific if I knew the app.
I would create a main view, in which you define the fixed parts of the application, as well as a container with layout 'fit' to hold the changing "views". Instead of layout 'fit', this could also be a tab panel or something. Nothing prevents you from add behaviour to the fixed part of this main view using a view controller for it of course.
Pretty straightforward in fact. Then, you'll change the current app view by putting it into the main view's central container. You'll need some kind of decision logic and configuration data to define the available views in your application. This would probably best to wrap that in a single place dedicated to this very task only, that is an app controller (rather than the view controller).
Here's an example fiddle, and bellow is the reasoning explaining the different parts of the code:
So, you'd start with a view like that:
Ext.define('My.view.Main', {
extend: 'Ext.container.Container',
xtype: 'main', // you can do that in ext5 (like in touch)
// shortcut for that:
//alias: 'widget.main',
controller: 'main',
layout: 'border',
items: [{
xtype: 'panel',
region: 'west',
layout: {type: 'vbox', align: 'stretch', padding: 5},
defaults: {
margin: 5
},
items: [{
xtype: 'button',
text: "Say Hello",
handler: 'sayHello'
}]
},{
// target for app's current view (that may change)
xtype: 'container',
region: 'center',
layout: 'fit'
}]
});
Ext.define('My.view.MainController', {
extend: 'Ext.app.ViewController',
alias: 'controller.main',
sayHello: function() {
Ext.Msg.alert("Controller says:", "Hello :-)");
}
});
Then, you set this main view as the "viewport" of your application. I also add a method to change the center view. I think the Application instance is a good place for that, but you could move this method to another dedicated app controller...
Ext.application({
name : 'My', // app namespace
// in real life, Main view class would lie in another file,
// so you need to require it
views: ['Main'],
// from ext 5.1, this is the config to auto create main view
mainView: 'My.view.Main',
// we also register a ref for easy retrieval of the main view
// (the value 'main' is the xtype of the main view -- this is
// a component query)
refs: {
main: 'main'
},
setCenterRegion: function(cmp) {
// getter generated by refs config
// you could another layout in the main view, and another component query of course
var center = this.getMain().down('[region=center]');
// replace the current center component with the one provided
center.removeAll();
center.add(cmp);
}
});
So, now, you can change the view with code like this:
My.getApplication().setCenterRegion(myView);
You could wire it through the ViewController of the main view, and using it as handlers in your view. For example, in your ViewController:
changeView: function() {
// retrieve the next view, this is application specific obviously
var myView = ...
// My.getApplication() gives you the instance created by
// Ext.getApplication in namespace 'My'
My.getApplication().setCenterRegion(myView);
}
And, in your main view, use an item like this:
{
xtype: 'button',
text: "Change view (by view controller)",
handler: 'changeView'
}
That could be fine for simple applications, nevertheless that seems like mixing of concern. Deciding about application level view swapping seems more like an application controller's business. So, I would rather recommend to put the changeView method in an app controller, and exposes it to components with a component query, like this:
Ext.define('My.controller.Main', {
extend: 'Ext.app.Controller',
config: {
control: {
// components will have to match this component query
// to be animated with the change view behaviour
'#changeView': {
click: 'changeView'
}
}
},
changeView: function() {
My.getApplication().setCenterRegion(/*
...
*/);
}
});
And you would hook the behaviour to components in any view like this:
{
xtype: 'button',
text: "Change view (by app controller)",
// will be matched by the controller component query
itemId: 'changeView'
}
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!
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.