PDA

View Full Version : vbox stacked containers not rendering images



benjamineberle
11 Sep 2012, 7:26 PM
The following code gives me rendering problems in Chrome and to a lesser extend in Firefox. That's what happens:
1. On first opening the container has a zero-height (always in Chrome, only occasionally in Firefox), i.e. the images are not rendered.
2. After either re-sizing the browser or clicking a different form such as another tabpanel and then clicking back, the container is rendered correctly.

I have tried the following without success:
1. Set height of container manually
2. doLayout() on form and container
3. vbox layout class updateChildBoxes()
4. forceComponentLayout() on images

This post might belong into the bugs section, but obviously there is a routine to render the images on first try which is called when the browser re-renders after a view change. I haven't looked that deep into the source code, yet, since I'm not a pro and would have to look for some time.

Thanks for any help!



Ext.define('MyApp.view.MyViewport', {
extend: 'Ext.container.Viewport',
alias: 'widget.MyViewport',

layout: {
type: 'fit'
},

initComponent: function() {
var me = this;

Ext.applyIf(me, {
items: [
{
xtype: 'form',
layout: {
type: 'vbox'
},
items: [
{
xtype: 'container',
items: [
{
xtype: 'image',
src: 'images/....png'
},
{
xtype: 'image',
src: 'images/....png'
}
]
}
]
}
]
});

me.callParent(arguments);
}

});

scottmartin
11 Sep 2012, 9:25 PM
This works fine. There is no layout for the images, so it paints next to each other



Ext.define('MyApp.view.MyViewport', {
extend: 'Ext.container.Viewport',

layout: {
type: 'fit'
},

initComponent: function() {
var me = this;

Ext.applyIf(me, {
items: [
{
xtype: 'panel',
layout: {
align: 'stretch',
type: 'vbox'
},
title: 'My Panel',
items: [
{
xtype: 'container',
flex: 1,
items: [
{
xtype: 'image',
src: 'flag.jpg'
},
{
xtype: 'image',
src: 'flag.jpg'
}
]
}
]
}
]
});

me.callParent(arguments);
}

});


Ext.create('MyApp.view.MyViewport', {});


Scott

fschaeffer
11 Sep 2012, 9:28 PM
I also had this problem when using images. The solution was to provide the height of the images as config option. Or maybe try using align: stretch...

The problem is, that on the first run the images are not in the browser cache and thus will be downloaded. As you're not providing any height information, the CSS/DOM doesn't know about the needed height and so the resulting height is 0. After the images are loaded, the browser (at least Chrome) knows about the dimensions of the images and is able to calculate the needed height of the container.

HTH
Florian

benjamineberle
11 Sep 2012, 10:38 PM
Thanks Scott and Florian for the fast answers.

Adding an align: 'stretch' to the vbox layout does help in above example. However the code I gave is simplified from what I've been actually doing. The rather complicated layout I've been using did not work properly by tinkering with the align-property. The only thing that helped so far has been to set the width AND height of the image, which is just a bit inconvenient since I have to get them somehow.

Isn't there a method to force the browser to redo the layout just as it does on resizing the viewport?

Here the actual full example:


Ext.define('MyApp.view.MyViewport', {
extend: 'Ext.container.Viewport',

layout: {
type: 'fit'
},

initComponent: function() {
var me = this;

Ext.applyIf(me, {
items: [
{
xtype: 'form',
layout: {
type: 'vbox'
},
items: [
{
xtype: 'container',
layout: {
align: 'stretch',
type: 'hbox'
},
items: [
{
xtype: 'container',
layout: {
pack: 'center',
type: 'vbox'
},
items: [
{
xtype: 'image',
height: 242,
width: 351,
src: 'images/wholepuncher01.png'
}
]
},
{
xtype: 'container',
layout: {
pack: 'center',
type: 'vbox'
},
items: [
{
xtype: 'image',
height: 71,
width: 150,
src: 'images/imgPlaceholder.png'
}
]
}
]
}
]
}
]
});
me.callParent(arguments);
}
});

The reason for the deep nesting is that it allows me to center the images next to each other and use placeholders that just look better.

fschaeffer
11 Sep 2012, 11:08 PM
Just as I said, there is no way for Ext to "know" the dimensions before you load them, so only AFTER the items are rendered a resize would work.

I'm still not sure, what you're trying to achieve. If you want to simply display item pictures side by side why don't you just use a dataView and some custom CSS as in the examples from Ext?

If you want to go the hard way you could add a listener to your form and call a function which resizes the form AFTER all elements are loaded. But that would lead to a visual "flicker"...

benjamineberle
12 Sep 2012, 7:22 AM
Thanks again. I found that the width x height properties of the Extjs image element remain 0 respectively undefined on first load, however width x height of the underlying image are accessible via image.getEl().dom.height after the images are cached.
It seems to me I should try to set width x height after the image is loaded (tested successfully), however the afterrender event of the image and of the main form fires too early for the images to be cached.
Is there a load-event or callback I could use? Otherwise I have to get the image size from a base64 encoded string or a url and I don't know yet how that would be possible (maybe it's easier to do... ?).

Here's an old thread, I hope the answer to this problem has become easier...
http://www.sencha.com/forum/showthread.php?32955-Load-Image-with-Callback

The dataView does not work very well here because I'm creating very flexible reports that can be a mix of pretty much every component and the vbox & hbox layouts are a perfect match.

Thanks!

fschaeffer
12 Sep 2012, 10:05 PM
How about providing the image dimensions server side using store load?

On application launch load a store which contains all necessary image informations, put a callback on that and then load you viewport. Now on render have a lookup in the store and use the provided dimensions for creating the vbox-items...

benjamineberle
13 Sep 2012, 12:03 AM
That still wouldn't work because my images either come from a local source such as file upload or a phone camera, or from the server. That means I would have to read the size from a base64 string which is usually left to the browser I guess.

However I found another solution that seems to work fine:

Before creating the component and the underlying image I add a dom listener to the configs like this:


image.src = '...';
image.listeners = {
load: {
element: 'el',
fn: function() {
var img = Ext.getCmp(this.id);
img.setSize(this.dom.width, this.dom.height);
}
}
};

This always fires after the browser cached the image no matter where it comes from (I hope!).

Thanks for your efforts!