Suppose we have defined a component (e.g. FieldSet) that we'd like to reuse in the single app (e.g. display/use it in 2 different modal windows.) This FieldSet has a reference, which we use to access it. The goal is to have these 2 windows contain independent fieldsets, so we can control and collect the inputs from each one separately.
Here's the sample fiddle that demonstrates the problem. As soon as any function triggers any lookupReference(...) call, Sencha issues the warning for "Duplicate reference" for the fieldset. It correctly creates two distinct fieldset components (by assigning different ids) on each window, but fails to properly assign/locate the references. As a result, any actions on one of these windows' fieldsets would be performed on the "unknown" one (probably on the first created one), messing up the UI behavior.
I see how it is a problem for Sencha to understand which component to use when operating on the reference, but there should be a way to reuse the same component multiple times without confusing the instances. Any help is greatly appreciated.
According to the docs on ViewController:
A view controller is a controller that can be attached to a specific view instance so it can manage the view and its child components. Each instance of the view will have a new view controller, so the instances are isolated.
This means that your use of singleton on your ViewController isn't correct, as it must be tied to a single view instance.
To fix this, I'd recommend making some modifications to your Fiddle, mainly removing the singleton: true from your VC class, accessing the views through lookup, and getting their VC's through getController to access your func method.
Ext.application({
name: 'Fiddle',
launch: function () {
/**
* #thread https://stackoverflow.com/questions/67462770
*/
Ext.define('fsContainerHandler', {
extend: 'Ext.app.ViewController',
alias: 'controller.fsContainerHandler',
// TOOK OUT singleton: true
func: function () {
var x = this.lookupReference('fsRef');
alert(x);
}
});
Ext.define('fsContainer', {
extend: 'Ext.container.Container',
xtype: 'xFSContainer',
controller: 'fsContainerHandler',
items: [{
xtype: 'fieldset',
title: 'myFieldset',
reference: 'fsRef'
}]
});
Ext.define('mainContainerHandler', {
extend: 'Ext.app.ViewController',
alias: 'controller.mainContainerHandler',
singleton: true,
onButton1Click: function () {
var win = this.getView().window1;
win.show();
// CHANGED LOGIC
win.lookup('theContainer').getController().func();
},
onButton2Click: function () {
var win = this.getView().window2;
win.show();
// CHANGED LOGIC
win.lookup('theContainer').getController().func();
}
});
Ext.define('mainContainer', {
extend: 'Ext.container.Container',
width: 400,
controller: 'mainContainerHandler',
window1: null,
window2: null,
initComponent: function () {
this.window1 = Ext.create('window1');
this.window2 = Ext.create('window2');
this.callParent(arguments);
},
items: [{
xtype: 'button',
text: 'Window 1',
reference: 'btn1',
handler: mainContainerHandler.onButton1Click,
scope: mainContainerHandler
}, {
xtype: 'button',
text: 'Window 2',
reference: 'btn2',
handler: mainContainerHandler.onButton2Click,
scope: mainContainerHandler
}]
});
Ext.define('window1', {
extend: 'Ext.window.Window',
title: 'Window1',
modal: true,
width: 100,
height: 100,
closeAction: 'hide',
// ADDED referenceHolder
referenceHolder: true,
items: [{
xtype: 'xFSContainer',
// ADDED reference
reference: 'theContainer'
}]
});
Ext.define('window2', {
extend: 'Ext.window.Window',
title: 'Window2',
modal: true,
width: 100,
height: 100,
closeAction: 'hide',
// ADDED referenceHolder
referenceHolder: true,
items: [{
xtype: 'xFSContainer',
// ADDED reference
reference: 'theContainer'
}]
});
Ext.create('mainContainer', {
renderTo: document.body
});
}
});
Related
I'm trying to look up a reference to one of my components in the ViewController's beforeInit, so I can set some variables on myComponent before it's created, but lookupReference does not find my component in the beforeInit call... it's found in the ViewController's init method, but I'm assuming because the view hasn't actually been rendered, the reference hasn't been created.
So I figured I'd try initComponent, but once again, the lookupReference doesn't work in there... I know I could create an itemId and look for this in initComponent, but that's last resort for me... I'd rather use the reference. See this Fiddle example and code:
Ext.application({
name: 'Fiddle',
launch: function() {
Ext.define('MyViewController', {
extend: 'Ext.app.ViewController',
alias: 'controller.myview',
beforeInit: function() {
console.log('beforeInit: I want this to exist before myComponent is rendered', this.lookupReference('myComponent'));
},
init: function() {
console.log('init', this.lookupReference('myComponent'));
}
});
Ext.define('MyView', {
extend: 'Ext.panel.Panel',
controller: 'myview',
title: 'My View',
height: 400,
width: 400,
items: [{
xtype: 'textfield',
reference: 'myComponent',
fieldLabel: 'My Component'
}],
initComponent: function() {
// I want this to happen before myComponent is rendered
console.log('initComponent: I want this to exist before myComponent is rendered', this.lookupReference('myComponent'));
this.callParent();
console.log('initComponent callParent', this.lookupReference('myComponent'));
}
});
Ext.create('MyView', {
renderTo: Ext.getBody()
});
}
});
Does anyone have any guidance on how to accomplish something like this?
I have a Window class like this:
Ext.define('EMS.tf.alerts.alerts.view.AlertWindow', {
extend: 'Ext.window.Window',
alias: 'widget.ems-alerts-window',
height: 220,
width: 600,
alertTpl: undefined,
autoScroll: true,
selectedRecord: undefined,
title: undefined,
atext: undefined,
// #private
initComponent: function() {
var me = this;
Ext.apply(me, {
tpl: me.alertTpl,
listeners: {
show: function() {
Ext.create('Ext.Container', {
renderTo: 'alertContainer',
itemId: 'buttonContainer',
items : [{
xtype: 'button',
cls: 'ackbtn',
text : 'Acknowledge',
name: 'ackButton',
itemId: 'renderbutton'
},{
xtype: 'button',
cls: 'attchmntbtn',
text : 'Attachment',
name: 'attButton',
itemId: 'renderattachmntbutton'
}]
});
}
},
title: me.title
});
me.callParent();
}
});
I want to get reference to button "Attachment" using itemId "renderattachmntbutton". How to do this?
I tried windowobject.down('#renderattachmntbutton') but still it didn't work. I can get reference to the items placed before init function but not like this. Any idea on what needs to be done to get reference to this button?
That button is not an item (a child) of the window but of the button container. If you want to find it with down then you need to grab a reference to the container and call down on that.
Instead of
windowobject.down('#renderattachmntbutton') // WRONG
call
buttoncontainer.down('#renderattachmntbutton') // Correct
Try this
Ext.ComponentQuery.query('[itemId=renderattachmntbutton]')[0]
The itemId can be used with the getComponent() call on parent items, like container and panels. If you change your itemId on your container to just an id property. You can then get to your child items like so:
Ext.getCmp('buttonContainer').getComponent('renderattachmntbutton');
This is just one possible way, there are others!
You could try
windowobject.down('[itemId=renderattachmntbutton]') ;
Here is an example of the code I'm using, I'm creating my own component from a panel and loading with the require property
Ext.define("VMPWorld.view.Navbar.MainNavbar", {
xtype: 'MainNavbar',
extend: 'Ext.Panel',
requires: [
'VMPWorld.view.Navbar.NavbarContainer'
],
config: {
layout: {
type: 'vbox',
align: 'stretch'
},
ui: 'none',
items: [{
xtype: 'NavbarContainer',
height: 700
}]
},
initialize: function() {
this.callParent();
console.log("Called");
}
});
I've never seen such template function. If you want to call it automatically, consider initComponent or handler of some event such as render. Anyway, you should provide some additional information on how you expect the function to be called.
I'm struggling with getting references and not using Ext.getCmp(..). I understand why it is best not to use Ext.getCmp in production apps, primarily because of potential confusion around duplicated DOM id's. I've create a basic sample below that I've put some comments in that I'm hoping, if I can find answers to will help me better understand how to get references.
I'm also looking for some really good explanations, tutorials, etc on this topic. I gather that learning how to do ComponentQuery's would be best but I'm not even sure if that is the case. So without further words, here the code. Please take a look at button event in pop up window for what I'm hoping to figure out.
Ext.define('MyApp.view.MyViewport', {
extend: 'Ext.container.Viewport',
layout: {
type: 'border'
},
initComponent: function () {
var me = this;
Ext.applyIf(me, {
items: [{
xtype: 'panel',
flex: 2,
region: 'center',
title: 'My Panel',
dockedItems: [{
xtype: 'toolbar',
dock: 'top',
items: [{
xtype: 'button',
text: 'MyButton',
listeners: {
click: {
fn: me.onButtonClick,
scope: me
}
}
}]
}],
items: [{
xtype: 'component',
html: '<b>my component</b>'
}]
}]
});
me.callParent(arguments);
},
onButtonClick: function (button, e, eOpts) {
Ext.define('MyApp.view.MyWindow', {
extend: 'Ext.window.Window',
height: 250,
width: 400,
title: 'My Window',
initComponent: function () {
var me = this;
Ext.applyIf(me, {
items: [{
xtype: 'button',
text: 'Want to get link to my component in window that opened this',
listeners: {
click: {
fn: me.onButtonClick,
scope: me
}
}
}]
});
me.callParent(arguments);
},
onButtonClick: function (button, e, eOpts) {
// I would like to set the html property of the
// component in the window below
// I would like to do this efficintly
// user itemId?
// use componentquery?
// use up/down/etc.
//
// Need help with componentquery, extjs help is not helpful for me
// I need more basics I think.
this.up('panel').up('panel').down('component').html = '<i>set from button</i>';
console.log(this.up('panel').up('panel').down('component'));
}
});
var win = Ext.create('MyApp1.view.MyWindow', {});
win.show();
}
});
Windows are floating so you cant use up to get back to your viewport. Likewise, you can't use down in your viewport to find any components in windows. Your outer onButtonClick method is called in the scope of the viewport. If you save off a reference to this at that point, you can use it with down to grab your component.
onButtonClick: function() {
var viewport = this;
Ext.define('YourWindow', {
// setup everything
onButtonClick: function() {
// this may not return what you want since everything
// else inside the viewport is technically also a component
// You'd be better off adding an itemId to the component
// you wish to grab and using that in the call to down.
console.log(viewport.down('component'));
}
});
// show window
}
On a side note, I'm not sure that you want to be defining your window class on button click. Unless you can guaranty that the button will only ever be clicked once, you should define your class elsewhere and just create the window in the click handler. That complicates getting a reference to the viewport, but you could easily set it as a property on the window when you create it, or just add the onButtonClick method in the window's configuration object.
I am new to extjs in general, specially to 4 version:
I have created a class:
Ext.define('MyPanel', {
extend:'Ext.panel.Panel',
views: ["MyPanel"],
config: {
width: 200,
height: 300,
title: "HELLO"
},
constructor:function(config) {
this.initConfig(config);
return this;
},
alias: 'widget.MyPanel'
});
Next, I want to call this class in form of XTYPE in a tabPanel items:[]:
I did like this:
items: [{
title: 'Kontakt',
id: 'kontaktTab',
closable:true,
closeAction: 'hide',
layout: 'fit',
items:[{
xtype: "MyPanel"
}]
No luck yet, all I get is :
Cannot create an instance of unrecognized alias: widget.MyPanel"
You must think, what a noob....
;-)
Someone please help!!!
When you are defining your view (MyPanel), why have you set views property?
Ext.define('MyPanel', {
extend:'Ext.panel.Panel',
alias: 'widget.MyPanel'
views: ["MyPanel"], <------ Why do you need this???
config: {
width: 200,
height: 300,
title: "HELLO"
},
constructor:function(config) {
this.initConfig(config);
return this;
}
});
And when you are making use of the new view, you need to specify it in requires. Here is an example:
Ext.define('MyApp.view.Viewport',{
extend: 'Ext.container.Viewport',
layout: 'border',
requires: [
'MyPanel' <--- This will ensure that this view file needs to be loaded
],
.
.
.
items: [{
title: 'Kontakt',
id: 'kontaktTab',
closable:true,
closeAction: 'hide',
layout: 'fit',
items:[{
xtype: "MyPanel"
}]
Hrm, have you tried lowercasing your alias. I thought aliases were always stored and fetched lowercase, but not sure about it
Ah, hehe, I completely overlooked something:
with an 'alias' you are creating a new class reference in the ExtJS class list. So by adding the alias like you did above, you can instantiate it by calling
var newMyPanel = Ext.create('widget.MyPanel');
However, if you are adding an instance with a xtype specifier you have to omit the widget part and just do:
var myContainer = Ext.create('Ext.panel.Panel',{
items: [{
xtype: 'MyPanel'
}]
});
With the above code, Ext will look for class with the alias 'widget.MyPanel'.
Apart from this, I think your constructor is a bit funky looking. The constructor should not return itself (like you would do in a Perl constructor for instance)
This is enough:
constructor: function() {
this.callParent(arguments);
// Your own code here
}
Cheers and let me know if it helps
Rob
all u have to do is declare it like that :
var panel1 = Ext.create('Ext.app.myPanel',{title : 'panel 1',height:350});//title and hight are optionals if u have already defined them
and then use it like this:
...
items : [panel1 ]
...
and you may need to require it :
Ext.require([
, 'Ext.app.myPanel'
]);
and put the mypanel.js an app folder
hope this helps