Extjs: How to approach the concept: Add, remove, add instance
Asked Answered
R

2

6

Concept: Consider having two panels A and B, and a window C like in the following example. The buttons on the window switches between the two panels.

var A = new Ext.Panel({
    title: 'A'
});

var B = new Ext.Panel({
    title: 'B'
});

var C = new Ext.Window({
    layout: 'fit',
    width: 300,
    height: 300,
    items: A,
    buttons: [
    {
        text: 'Switch to A',
        handler: function() {
            C.removeAll(false);
            C.add(A);
            C.doLayout();
        }
    }, {
        text: 'Switch to B',
        handler: function() {
            C.removeAll(false);
            C.add(B);
            C.doLayout();
        }
    }]
});
C.show();

The concept is very simple: Add a component, remove it and add the same instance again.

Problem: The switch from A to B works, but going back to A doesn't (B stays and A is not shown again).

Question: Thinking OOP, I would expect the above concept to work. Since this is not the case and its a very basic manouvre, how should I think / contemplate / design when I'm trying to do this?

I understand that there might be varying cases when considering a FormPanel vs. other layouts/components - but there must be a general and correct way to do this :)

Resinous answered 31/3, 2011 at 13:24 Comment(0)
G
3

Perhaps a card layout is exactly what you need:

var C = new Ext.Window({
    layout: 'card',
    width: 300,
    height: 300,
    activeItem: 0,
    items: [A, B],
    buttons: [{
        text: 'Switch to A',
        handler: function() {
            C.getLayout().setActiveItem(0);
        }
    }, {
        text: 'Switch to B',
        handler: function() {
            C.getLayout().setActiveItem(1);
        }
    }]
});
C.show();

I assume the problem with your code is that you're reusing the same instance again. Ext internally sets a rendered-flag on a component once it has been written to the DOM tree. As the rendered flag is still true after you removed the component from C it won't be redrawn when you add the component again.

A simple modification will make your code work: add A.rendered = false; and B.rendered = false respectively before you call C.doLayout() in your button handlers.

But still the card-layout approach would be best-practice.

Griseofulvin answered 31/3, 2011 at 13:38 Comment(8)
@Stefan: This visually might solve the panel issue, but I am thinking in more general terms. What about having a set of form controls and in one situation showing one sub set of them and in another situation showing another sub set - while all the time maintaining the initial instances of the components. Does it makes any sense? Instead of instantiating a new control when I need it, I want to maintain a finite set of controls. Conceptually kind of the same way as a TabPanel but without the tabs above it.Resinous
But why does this interfere with the card layout? If you don't like the numeric assignment, you also could use ids or itemIds to address the activeItem or the item to be activated.Griseofulvin
It does not interfere. The card layout simply isn't what I am looking for. Ex. I cannot show two items at the same time. My example in the OP was just the most simple I could come up with. I want to be able to have a set of components and show one or more of them at the same time, and when the user does something, I want to be able to show/hide/change the order of appearance of the components in my set. The X.rendered = false - doesn't that interfere with the state of the component in an inconvenient way, which again requires me to maintain other attributes?Resinous
The X.rendered = false will surely leave you components in a dirty state resulting in perhaps very tricky problems. Not something you should use in your code. It was just to demonstrate what the problem really is.Griseofulvin
OK - now I can see your problem. But I don't think the components are designed for this requirement as they couple the logic component to its rendered DOM tree. Another option would be to hide all components and only switch the ones to visible that are currently required.Griseofulvin
Yeah, I thought about that. But then I am not sure how the browser will handle any changes in the order of appearance :SResinous
You'd have to call doLayout() after every visibility-change to have Ext recalculate the layout and the appropriate component sizing but in general this will work. Not sure how the browsers will handle a large amount of components though.Griseofulvin
Thanks for all your comments and considerations. I will try to think the structure of my components in another fashion, where I will avoid to reuse components.Resinous
U
0

I've found a simple solution, but it's more of a hack. After removing the component (your A or B panel), you have to add it to another container which must to be rendered. Here is an example in which the panels get moved to a hidden container:

var A = new Ext.Panel({
    title: 'A'
});

var B = new Ext.Panel({
    title: 'B'
});

var C = new Ext.Window({
    layout: 'fit',
    width: 300,
    height: 300,
    items: A,
    buttons: [
    {
        text: 'Switch to A',
        handler: function() {
            C.remove(B, false);
            T.add(B);
            C.add(A);
            C.doLayout();
        }
    }, {
        text: 'Switch to B',
        handler: function() {
            C.remove(A, false);
            T.add(A);
            C.add(B);
            C.doLayout();
        }
    }]
});

var T = new Ext.Container({
    renderTo: "temporaryContainer",
    renderHidden: true
});

C.show();

And somewhere in the page's body you need to have this:

<div id="temporaryContainer" class="x-hidden"></div>

Tested with ExtJs 4.0.2a.

Undergraduate answered 31/7, 2011 at 12:54 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.