Is it possible to use Ext.Resizable on a 'width: 100%' element? - extjs

When I apply Ext.Resizable with parameters wrap: true, handles: 's' to an Ext.form.TextArea with width: 100%, the text area loses its width. More specifically, the width is reset to sth. like default width in pixels. Is it possible to cleanly make Ext.Resizable just not touch width at all and operate on element's height only? I checked that not touching width would work fine in principle by replacing (in FireBug) explicit widths on the text area and wrapping div back with 'width: 100%'.
I'm trying to achieve effect similar to SO's question/answer text area, which can be resized for height.

Isn't anchoring an option, with layout:'fit' on a panel and anchor:'100%' on the textarea the width should stay at 100%.
The drawback is that you are required to wrap everything in a panel and possibly also use ext components (like Ext.form.Textarea)
ref: http://www.sencha.com/deploy/dev/examples/form/anchoring.html

Try this:
Ext.onReady(function(){
var el = Ext.getBody().createChild({
tag: 'div',
style: 'width: 600px;'
});
var area = new Ext.form.TextArea({
renderTo: el,
style: 'width: 100%'
});
new Ext.Resizable(area.el, {
wrap: true,
handles: 's',
height: area.getHeight(),
});
});

Here is my hackish subclass I eventually used.
ExtOverrideUtils = {
setSizeIgnoreWidth: function (width, height, animate)
{
this.setHeight ((typeof width == 'object' ? width.height : height), animate);
},
intercept: function (func, overrides)
{
return function ()
{
var value = func.apply (this, arguments);
if (value) {
for (var name in overrides)
value[name] = overrides[name];
}
return value;
};
}
}
NoWidthResizable = Ext.extend (Ext.Resizable,
{
constructor: function (el, config)
{
el = Ext.get (el);
el.setSize = ExtOverrideUtils.setSizeIgnoreWidth;
el.wrap = ExtOverrideUtils.intercept (el.wrap, { setSize: ExtOverrideUtils.setSizeIgnoreWidth });
NoWidthResizable.superclass.constructor.call (this, el, config);
}
});
Usage from a custom subclass of Ext.form.TextArea (though I guess it can be used just as normal Ext.Resizable, for arbitrary element):
this._resizer = new NoWidthResizable (this.el,
{ wrap: true,
handles: 's',
minHeight: 30,
pinned: true,
dynamic: true });

Related

Prevent Rendering (hide and/or disable) of a Component at construction time

Background: Our app is always packed as a whole but through the users access some serverside actions may be restricted. We know which actions are allowed the time the app starts. We now want to hide all the views (panels, buttons, etc) from the user to which he lacks the access to.
For that we have written a plugin which can be applied to any Component. But here comes the problems:
Here is what we try to run against the plugin host:
if (cmp['setVisible']) cmp.setVisible(false); else cmp.hidden = true;
if (cmp['disable']) cmp.disable(); else cmp.disabled = true;
cmp.on('beforerender', function() { return false; })
First we thought the earlier we do this the better. So we tried to run it at construction time of the plugin. But that was not possible because the listeners (of the host) seems to be not ready yet (the component tries to fire the hide event). So we moved it into the init method of the plugin which does not throw a error but just worked partly. Only the beforerender event got really applied but it only aborted the rendering of the child. so we ended up with a broken looking component (the borders are there and the content not). If we commented the event registration out the host stayed untouched. We also tested the use of only the hidden:true and disabled:true with no luck.
So how can we prevent rendering of component in the correct way?
Edit:
The component should be flagged as disabled and hidden because we cannot prevent the creation of the component. The snipped I got from my colleague was wrong so the call of setVisible(false) worked, we guess disable() also. But the component get stilled rendered and we seem not really able to veto this without ending up with a half rendered component.
Answer by #AlexTokarev
I tried what #AlexTokarev suggested. For that I added the following lines into the Plugin-Constructor
cmp.hidden = true;
cmp.autoShow = false; // I know this may do nothing for non floating but I added it anyway
cmp.autoRender = true;
Based on debugging I know that the settings get applied really early (at the Ext.AbstractComponent.constructor), but I still ending up with a hidden and rendered component.
Comment by #sbgoran
In one Testcase we use a column-layout in which all containers extend from the same class. As soon as I add our plugin (with the beforerender event returning false configuration) to one of this extending containers (the plugin is directly added to class definition (as ptype)) all containers within this columns look broken (only borders are rendered and in the content a small grey box in the upper left corner.). So the aborted rendering affect all child items of the column when only one child item get the rendering canceled.
**Sample Code **
First I want to note that we are looking for a way to do this in general cause as far as we know the rendering in ExtJS is one thing. I can ask to setup a demo but I think this will not be that easy because we are using the Ext.app.portal.Panel for the failing example. but the plugin should work for any sort of Component. First I will add some demo code:
We have a view which is placed into a Viwport with border layout
Ext.define('MVC.view.Application',{
extend:'Ext.tab.Panel',
alias:'widget.appview',
region: 'center',
activeTab: 1
});
Within the Controller we fill this
var portal = this.portalRef = Ext.widget('portalpanel', {
title: 'Employee',
portalCols: 2
});
portal.addPortlet(0,['employee','employee2','employee3']);
portal.addPortlet(1,['employee4','employee5']);
app.appviewmain.add(portal);
Here is the portal panel
Ext.define('MVC.app.portal.PortalPanel', {
extend: 'Ext.panel.Panel',
alias: 'widget.portalpanel',
requires: [
'Ext.layout.container.Column',
'Ext.app.portal.PortalDropZone',
'Ext.app.portal.PortalColumn'
],
portalCols: 2,
portalColCfg: {
defaults: {
closable: false,
draggable: false,
collapsible: false,
header: false,
bodyStyle: {
background: '#fff',
padding: '10px'
}
},
items: []
},
addPortlet: function(idx, portlets) {
if (idx > this.portalCols || idx < 0)
return;
var portalCol = this.items.getAt(idx);
function insertPortlet(portlet) {
if (Ext.isString(portlet)) {
portlet = { xtype: portlet };
}
portalCol.add(portlet);
};
if (Ext.isArray(portlets)) {
var len = portlets.length,
i = 0;
for(;i<len;i++) {
insertPortlet(portlets[i]);
}
} else {
insertPortlet(portlets);
}
},
initPortal: function() {
var cfg = this.portalColCfg,
i = 0,
cols = [];
for (;i<this.portalCols;i++) {
cols.push(Ext.clone(cfg));
}
this.items = cols;
},
cls: 'x-portal',
bodyCls: 'x-portal-body',
defaultType: 'portalcolumn',
autoScroll: true,
manageHeight: false,
initComponent : function() {
var me = this;
// init only if nothing is defined
if (!me.items)
me.initPortal();
// Implement a Container beforeLayout call from the layout to this Container
me.layout = {
type : 'column'
};
me.callParent();
me.addEvents({
validatedrop: true,
beforedragover: true,
dragover: true,
beforedrop: true,
drop: true
});
},
// Set columnWidth, and set first and last column classes to allow exact CSS targeting.
beforeLayout: function() {
var items = this.layout.getLayoutItems(),
len = items.length,
firstAndLast = ['x-portal-column-first', 'x-portal-column-last'],
i, item, last;
for (i = 0; i < len; i++) {
item = items[i];
item.columnWidth = 1 / len;
last = (i == len-1);
if (!i) { // if (first)
if (last) {
item.addCls(firstAndLast);
} else {
item.addCls('x-portal-column-first');
item.removeCls('x-portal-column-last');
}
} else if (last) {
item.addCls('x-portal-column-last');
item.removeCls('x-portal-column-first');
} else {
item.removeCls(firstAndLast);
}
}
return this.callParent(arguments);
},
// private
initEvents : function(){
this.callParent();
this.dd = Ext.create('Ext.app.portal.PortalDropZone', this, this.dropConfig);
},
// private
beforeDestroy : function() {
if (this.dd) {
this.dd.unreg();
}
this.callParent();
}
});
And here is the Portlet
Ext.define('Ext.app.portal.Portlet', {
extend: 'Ext.panel.Panel',
alias: 'widget.portlet',
layout: 'fit',
anchor: '100%',
frame: true,
closable: true,
collapsible: true,
animCollapse: true,
draggable: {
moveOnDrag: false
},
cls: 'x-portlet',
initComponent : function() {
this.callParent();
},
// Override Panel's default doClose to provide a custom fade out effect
// when a portlet is removed from the portal
doClose: function() {
if (!this.closing) {
this.closing = true;
this.el.animate({
opacity: 0,
callback: function(){
var closeAction = this.closeAction;
this.closing = false;
this.fireEvent('close', this);
this[closeAction]();
if (closeAction == 'hide') {
this.el.setOpacity(1);
}
},
scope: this
});
}
}
});
Here is a sample view
Ext.define('MVC.view.employee.Employee',{
extend:'Ext.app.portal.Portlet',
alias:'widget.employee',
plugins: [{ptype: 'directbound', accessRoute: 'Employee.Read'}],
items: [
/*A form with some fields*/
]
});
Here's the plugin
Ext.define('MVC.direct.plugins.DirectBound',{
extend: 'Ext.AbstractPlugin',
alternateClassName: ['MVC.direct.DirectBound'],
alias: 'plugin.directbound',
/**
* #cfg {int} blockMode Indicates the way in which the Component gets blocked
* options
* 0 hide and disable
* 1 disable
*/
blockMode: 1,
constructor: function(config) {
var me = this,
cmp = config['cmp'],
route;
me.parseRoute(route);
// check for access
if (!me.checkAccess()) {
if (me.blockMode === 0) {
cmp.hidden = true;
cmp.autoShow = false;
cmp.autoRender = true;
}
me.diabled = true;
}
me.callParent(arguments);
}
/* some more methods */
});
Here's the column Layout
Ext.define('MVC.app.portal.PortalColumn', {
extend: 'Ext.container.Container',
alias: 'widget.portalcolumn',
requires: [
'Ext.layout.container.Anchor',
'MVC.app.portal.Portlet'
],
layout: 'anchor',
defaultType: 'portlet',
cls: 'x-portal-column'
// This is a class so that it could be easily extended
// if necessary to provide additional behavior.
});
Have you tried to set autoRender: true in your optional components? Here's the doc: http://docs.sencha.com/extjs/4.2.2/#!/api/Ext.AbstractComponent-cfg-autoRender
You can try with hide and show functions and also try with "added" event listener, which will be called after adding the component to container.
Try something like this for your plugin:
Ext.define('MVC.direct.plugins.DirectBound',{
extend: 'Ext.AbstractPlugin',
alternateClassName: ['MVC.direct.DirectBound'],
alias: 'plugin.directbound',
/**
* #cfg {int} blockMode Indicates the way in which the Component gets blocked
* options
* 0 hide and disable
* 1 disable
*/
blockMode: 1,
constructor: function(config) {
var me = this,
cmp = config['cmp'],
route;
me.parseRoute(route);
// Try to define beforerender callback on component and return false if
// component should not be visible
cmp.on('beforerender', function() {
if (!me.checkAccess()) {
if (me.blockMode === 0) {
return false;
}
// Not sure what this do for you but it wont disable component
// if you want your component disabled here try cmp.disable()
me.diabled = true;
}
});
// Maybe this code is not needed anymore
// check for access
if (!me.checkAccess()) {
if (me.blockMode === 0) {
cmp.hidden = true;
cmp.autoShow = false;
cmp.autoRender = true;
}
me.diabled = true;
}
// This should stay for sure
me.callParent(arguments);
}
/* some more methods */
});

change the background color of panels in extjs

In my project, I am trying to change the background color of all the panels inside a container. The code which I am trying is as follows:
container --> panel (don't change) --> panel (Change)
//Generated dynamically using for loop.
listeners: {
'render': function(panel) {
panel.body.on('click', function() {
//here change the background color of all the panels inside the container>panel.
});
}
}
What should I write to change the background color of the only panels which are present inside the parent panels of a main container?
I tried:
Ext.each('panel',function(){this.body.setStyle('background','white')}),
But the above approach is giving me the following error:
Uncaught TypeError: Cannot call method 'setStyle' of undefined
EDIT:
Here, I am looking for a method of extjs which quite do the same work as jQuery's children().
$('#containerID').children('panel').children('panel').css(change background color);
Based on your requirements you will always have a sum of 9 components you are looking at -1 the you start from. The shortest way is to use the each() method of the MixedCollection (at runtime all items are within a MixedCollection)
'render': function(panel) {
panel.body.on('click', function() {
panel.items.each(function(p){ p.body.setStyle('background','white'); }, this)
},this);
}
This may not be the variant with the best performance but knowing your requirement from the last question I can say that this is the easiest. And in addition it will be easy to maintain. And read the article about delegates that I posted in the comments of the last question!
I hope there is now typo, cause it is untested
Update
Well, you are looking for the ownerCt property here (at least that is the easiest way). But there are some mightier navigation methods up() / down() both can be feeded with a ComponentQuery string. Leave the up() arguments empty will return the immediate owner/activater (basically the same as ownerCt).
Following a working example:
var childItems = [], items = [];
for (i = 0; i < 9; ++i) {
childItems.push({
xtype: 'container',
width: 50,
height: 50,
html: i + '',
style: {borderColor:'#000000', borderStyle:'solid', borderWidth:'1px'},
listeners: {
'afterrender': function(panel) {
panel.el.on('click', function(e,t) {
panel.el.on('click', function(e,t) {
panel.el.setStyle('background','red');
panel.ownerCt.items.each(function(p){ if(panel.el.id != p.id) p.el.setStyle('background','white'); })
});
}
}
});
}
for (i = 0; i < 9; ++i) {
items.push({
xtype: 'container',
layout: {
type: 'table',
columns: 3
},
style: {borderColor:'#000000', borderStyle:'solid', borderWidth:'1px'},
items: childItems
});
}
Ext.create('Ext.container.Container', {
layout: {
type: 'table',
// The total column count must be specified here
columns: 3
},
renderTo: Ext.getBody(),
style: {borderColor:'#000000', borderStyle:'solid', borderWidth:'1px', margin: '30px'},
items: items
});
Update 2
To reset all try this (untested)
'afterrender': function(panel) {
panel.el.on('click', function(e,t) {
panel.el.setStyle('background','red');
panel.ownerCt.ownerCt.items.each(function(op){
op.items.each(function(p){
if(panel.el.id != p.id)
p.el.setStyle('background','white');
})
}, this)
});
}
JSFiddle

ExtJS window animatetarget

I'm using animateTarget to animate the showing and hiding of a window. However i can't seem to set any animation options for this like duration and easing.
Only setting the duration and easing would be sufficient for me in this specific use case.
I'm using ExtJS 4.1
thnx!
It is possible to configure this animation but not with just additional configuration properties applied to a Ext.window.Window at creation. The animation is done with the animate method of the Ext.Element and is of type Ext.fx.Anim (see this link for config details)
You will need to extend from Ext.window.Window and override the afterShow() and onHide() private eventhandler. Within these you can modify the appropriate configs. Here's a example where I extended the duration from default 250ms to 500ms. And here's a working JSFiddle life-demo
Ext.define('Ext.ux.Window',{
extend: 'Ext.window.Window',
alias: 'widget.animatedwindow',
initComponent: function() {
this.callParent(arguments);
},
afterShow: function(animateTarget, cb, scope) {
var me = this,
fromBox,
toBox,
ghostPanel;
// Default to configured animate target if none passed
animateTarget = me.getAnimateTarget(animateTarget);
// Need to be able to ghost the Component
if (!me.ghost) {
animateTarget = null;
}
// If we're animating, kick of an animation of the ghost from the target to the *Element* current box
if (animateTarget) {
toBox = me.el.getBox();
fromBox = animateTarget.getBox();
me.el.addCls(me.offsetsCls);
ghostPanel = me.ghost();
ghostPanel.el.stopAnimation();
// Shunting it offscreen immediately, *before* the Animation class grabs it ensure no flicker.
ghostPanel.el.setX(-10000);
me.ghostBox = toBox;
ghostPanel.el.animate({
from: fromBox,
to: toBox,
duration: 500,
listeners: {
afteranimate: function() {
delete ghostPanel.componentLayout.lastComponentSize;
me.unghost();
delete me.ghostBox;
me.el.removeCls(me.offsetsCls);
me.onShowComplete(cb, scope);
}
}
});
}
else {
me.onShowComplete(cb, scope);
}
},
onHide: function(animateTarget, cb, scope) {
var me = this,
ghostPanel,
toBox,
activeEl = Ext.Element.getActiveElement();
// If hiding a Component which is focused, or contains focus: blur the focused el.
if (activeEl === me.el || me.el.contains(activeEl)) {
activeEl.blur();
}
// Default to configured animate target if none passed
animateTarget = me.getAnimateTarget(animateTarget);
// Need to be able to ghost the Component
if (!me.ghost) {
animateTarget = null;
}
// If we're animating, kick off an animation of the ghost down to the target
if (animateTarget) {
ghostPanel = me.ghost();
ghostPanel.el.stopAnimation();
toBox = animateTarget.getBox();
ghostPanel.el.animate({
to: toBox,
duration: 500,
listeners: {
afteranimate: function() {
delete ghostPanel.componentLayout.lastComponentSize;
ghostPanel.el.hide();
me.afterHide(cb, scope);
}
}
});
}
me.el.hide();
if (!animateTarget) {
me.afterHide(cb, scope);
}
}
});

Extjs panel extend

I have an extjs panel. I need to induce a drag and resizing property for this panel.
This is the code for creating the panel:
var childPanel = new Ext.Panel({
draggable: true,
layout: 'fit',
................
});
I have achieved drag and resizing properties using the code:
Ext.override(Ext.Panel, {
// private
initEvents: function () {
if (this.draggable) {
this.initDraggable();
}
this.resizer = new Ext.Resizable(this.el, {
animate: true,
duration: '.6',
easing: 'backIn',
handles: 'all',
pinned: false,
transparent: true
});
this.resizer.on("resize", this.onResizer, this);
},
onResizer: function (oResizable, iWidth, iHeight, e) {
this.setHeight(iHeight);
this.setWidth(iWidth);
}
});
As you can see I am overriding the property. Therefore all the panels that I create have these properties. I don't want it like that.
I know that Ext.extend is the method to use but each time I am getting some errors. What I need is an extended panel with the above code.
Can anybody help me to achieve this?
Have you tired extending? what were the errors?
Your should remmeber to call the superclass constructor from the extended methods.
MYDRAGRESIZEPANEL = Ext.extend(Ext.Panel,{
// private
initEvents: function () {
**MYDRAGRESIZEPANEL.superclass.constructor.call(this);**
if (this.draggable) {
this.initDraggable();
}
this.resizer = new Ext.Resizable(this.el, {
animate: true,
duration: '.6',
easing: 'backIn',
handles: 'all',
pinned: false,
transparent: true
});
this.resizer.on("resize", this.onResizer, this);
},
onResizer: function (oResizable, iWidth, iHeight, e) {
this.setHeight(iHeight);
this.setWidth(iWidth);
}
});

How to query current expanded item in accordion layout

OK, so here is the way I can find current collapsed in accordion layout:
Ext.getCmp("myaccordion").query("{collapsed}")
How in the same manner I can find expanded one? I can't see expanded property. Moreover, this code:
Ext.getCmp("myaccordion").query("not:{collapsed}")
crushes my browser down.
UPD: here is my decision, based on example in ExtJS docs:
Ext.ComponentQuery.pseudos.expanded = function(items) {
var res = [];
for (var i = 0, l = items.length; i < l; i++) {
if (!items[i].collapsed) {
res.push(items[i]);
}
}
return res;
};
And then I just query this way Ext.getCmp("myaccordion").query(">*:expanded")
But can we make it shorter, using :not somehow?
You don't have to use a function for this component query.
If you have other panels somewhere in your framework that are not collapsed query('[collapsed=false]') would also include those so that was probably the problem you were having.
But you can limit the query to only direct children by calling child instead:
Ext.getCmp("myaccordion").child("[collapsed=false]");
Or you can limit it to direct children in the selector string itself if you give the accordion an id config, as it looks like you did: (id: 'myaccordion'). You can use:
Ext.ComponentQuery.query('#myaccordion > panel[collapsed=false]')
If you have the accordion configured for only one expanded panel at a time (the default config) that will give you an array with one item - the expanded panel. If you have more than one expanded panel they will each be contained in the array.
You could do:
Ext.onReady(function(){
var p1 = Ext.create('Ext.panel.Panel', {
renderTo: document.body,
width: 100,
height: 100,
title: 'Foo',
collapsed: true
});
var p2 = Ext.create('Ext.panel.Panel', {
renderTo: document.body,
width: 100,
height: 100,
title: 'Foo',
collapsed: false
});
console.log(Ext.ComponentQuery.query('[collapsed=false]'));
});

Resources