[ 11 posts ] Go to page 1, 2 Next

Tor Edvardsson

  • Username: tored950
  • Joined: Wed May 19, 2010 11:56 pm
  • Posts: 4
  • GitHub: tored950
  • Gists: tored950
  • Offline
  • Profile

Child widgets together with DOM nodes

Post Posted: Thu May 20, 2010 12:15 am
+0-
Hi!

How do you solve the problem of creating composite widgets with child widgets with interleaving DOM elements?

Assume we have a composite widget (uses widget-parent as extension) and two widgets Foo and Bar both with extensions widget-child. I add them to the composite widgets, but I want to use some structural elements between these two widgets.

I can of course create a child widget of the structural elements and solve the problem, but it feels unjustified to have a widget with no logical function.

with renderUI as follows

Code:
renderUI: funciton () {
  var foo = new Y.My.FooWidget();
  this.add(foo);
  var node = Y.Node.create("<span/>")
  this.get("contentBox").append(node)
  var bar = new Y.My.BarWidget();
  this.add(bar);
}


The problem here is that this.add() does not alter the DOM until later and therefore my structrual div ends up before the both child widgets.

Another solution is to have a CONTENT_TEMPLATE with all structural elements predefined. However I have not yet found a simple way to insert child widgets into such structure without resorting to CSS selectors to find placeholders in that CONTENT_TEMPLATE. It feels a bit cumbersome.

Am I missing something obvious?

Caridy Patino

YUI Contributor

  • Username: caridy
  • Joined: Mon Dec 08, 2008 5:40 pm
  • Posts: 491
  • Location: Miami, FL
  • Twitter: caridy
  • GitHub: caridy
  • Gists: caridy
  • IRC: caridy
  • YUI Developer
  • Offline
  • Profile

Re: Child widgets together with DOM nodes

Post Posted: Thu May 20, 2010 5:43 am
+0-
Hello Tor,

Child-Parent relation is not tied through the DOM structure, but to the object definition. You can even have a child widget outside of the content of the parent widget.

This relation is only to delegate process, and open a communication pipeline, but it's not subject to any DOM manipulation between the objects.

A good practice is to create parent and child elements at the same time, connecting them through ".add", probably during the initialization of the parent. Then you just need to "show" the parent, and it will carry on the render process for the child elements as well.

About the boundingBox area for the child elements, just keep in mind that you can't specify it through the "render" method because you don't control that method, the parent widget does it. What you can do is to specify a "boundingBox" attribure during the creation of the widget, and it will use that node as the "boundingBox".

In your example:
Code:
renderUI: funciton () {
  ....
  var node = Y.Node.create("<span/>")
  this.get("contentBox").append(node)
  var bar = new Y.My.BarWidget({
      boundingBox: node
  });
  this.add(bar);
}


In this case, you're defining that this child should be rendered within the "span" instead of the default position which is always the parent widget "contentBox".

Best Regards,
Caridy

Tor Edvardsson

  • Username: tored950
  • Joined: Wed May 19, 2010 11:56 pm
  • Posts: 4
  • GitHub: tored950
  • Gists: tored950
  • Offline
  • Profile

Re: Child widgets together with DOM nodes

Post Posted: Wed Jun 09, 2010 6:51 am
+0-
Hi Caridy,

Thanks for your answer. It was almost what I wanted. It pushed me in right direction.

Had some time to play around with the Widget framework and now I understand what is the problem. Basically it is the add() method that does it wrong then it's calling the childs render method.

Lets have this example

Code:
 
CONTENT_TEMPLATE: "<div>" +
                     "<div name='foo'></div>" +
                     "<span> ==== </span>" +
                     "<div name='bar'></div>"
                   "</div>",


As we can se I have two placeholder divs for my two widgets and in between I have span. That span is just for structure, decoration etc

After I create and add my widgets, I still want the structure above, but with the child widgets inserted. So I use the idea you told me.

Code:
renderUI: funciton () {
  var fooNode = this.get("contentBox").one("div[name=foo]");
  var foo = new Y.My.FooWidget({boundingBox: fooNode});
  this.add(foo);

  var barNode = this.get("contentBox").one("div[name=bar]");
  var bar = new Y.My.BarWidget({boundingBox: barNode});
  this.add(bar);
}


This does not work. add() just adds evertyhing to the end and not placed around my structure.

However if I do like this, it does work


Code:
renderUI: funciton () {
  var fooNode = this.get("contentBox").one("div[name=foo]");
  var foo = new Y.My.FooWidget({boundingBox: fooNode});
  foo.render();
  this.add(foo);

  var barNode = this.get("contentBox").one("div[name=bar]");
  var bar = new Y.My.BarWidget({boundingBox: barNode});
  bar.render();
  this.add(bar);
}


So when add() calls render() internally it does not keep the position of the element in the DOM tree but moves it. By calling render() prior to to add() the element stays where it should.

Is this behavior intended?

Except for the extra method call to render() I think this is a great solution because then you can define the structure of the widget in your CONTENT_TEMPLATE and the placeholder is passed over as a boundingBox to the child widget which means that you can define parent specific classes for your child and alter between the use of divs and spans depending on the use case for the parent. And when reading the code you get a good overview how this widget is structured. Compare to multiple Y.Node.create() inside renderUI() - much harder to grasp what the end result is.

Of course the you have then rule that a child widget should never have its own boundingBox specific stuff.

One warning thou. I experienced a lot of problem with this solution because I closed my CONTENT_TEMPLATE tags in short form instead of a separate closing tag. It took a while to figure out what went wrong.

By writing like this, you get a different result.

Code:
 
CONTENT_TEMPLATE: "<div>" +
                     "<div name='foo'/>" +
                     "<span> ==== </span>" +
                     "<div name='bar'/>"
                   "</div>",


Sincerely Tor

Caridy Patino

YUI Contributor

  • Username: caridy
  • Joined: Mon Dec 08, 2008 5:40 pm
  • Posts: 491
  • Location: Miami, FL
  • Twitter: caridy
  • GitHub: caridy
  • Gists: caridy
  • IRC: caridy
  • YUI Developer
  • Offline
  • Profile

Re: Child widgets together with DOM nodes

Post Posted: Wed Jun 09, 2010 7:15 am
+0-
Hey Tor,

About the "render" and "add" order, lets include Satyen in this discussion, to see if this is a bug, or the intended behavior.

About the CONTENT_TEMPLATE definition, <div /> it's an invalid definition, and the result can be unpredictable due the browser normalization process.

Best Regards,
Caridy

Satyen Desai

YUI Developer

  • Username: sdesai
  • Joined: Tue Dec 09, 2008 4:17 pm
  • Posts: 302
  • GitHub: sdesai
  • Gists: sdesai
  • YUI Developer
  • Offline
  • Profile
Tags:

Re: Child widgets together with DOM nodes

Post Posted: Wed Jun 09, 2010 9:59 am
+0-
Hi,

If the problem we're trying to solve is "Render my children to a location other than the default [ which is the parent contentBox ]", how about over-riding the method(s) which handle this aspect of parent-child:

Code:
    _renderChildren: function () {

        var renderTo = this._childrenContainer || this.get("contentBox");

        this._childrenContainer = renderTo;

        this.each(function (child) {
            child.render(renderTo);
        });
    },


And

Code:
    _uiAddChild: function (child, parentNode) {

        child.render(parentNode);
        ...


Note: It sounds like the assumption that parentNode is always used to render the child to is the thing breaking "add()" for you. add() ends up calling _uiAddChild, which probably moves the bounding box from where it's at to the parentNode (which again, is widget-parent's contentBox by default).

Looking at it in isolation, we should probably add a check around it to make sure the child is not already a descendent of the widget-parent boundingBox before defaulting to parentNode. However, looking at it from a higher level, it seems like in general there's a feature here where the widget-parent could support a rendering template which identifies where certain "types" of children are rendered to.

Just thinking through this template feature a little more: For your use case, if I were to add another Foo child (currrently parent-child is designed for a 0..n child setup), what would happen? Should it go into the "foo" div? Or is your parent designed to have exactly 1 Foo and 1 Bar?

Regards,
Satyen

Satyen Desai

YUI Developer

  • Username: sdesai
  • Joined: Tue Dec 09, 2008 4:17 pm
  • Posts: 302
  • GitHub: sdesai
  • Gists: sdesai
  • YUI Developer
  • Offline
  • Profile

Re: Child widgets together with DOM nodes

Post Posted: Wed Jun 09, 2010 10:19 am
+0-
Actually, one step up from _uiAddChild, this may be a better place to customize where children are rendered, because it's the code deciding where the child should go after it's been added:

Code:
    _afterAddChild: function (event) {
        var child = event.child;

        if (child.get("parent") == this) {
            this._uiAddChild(child, this._childrenContainer);
        }
    },

Tor Edvardsson

  • Username: tored950
  • Joined: Wed May 19, 2010 11:56 pm
  • Posts: 4
  • GitHub: tored950
  • Gists: tored950
  • Offline
  • Profile

Re: Child widgets together with DOM nodes

Post Posted: Wed Jun 09, 2010 10:57 am
+0-
Hi Satyen!


sdesai wrote:
Note: It sounds like the assumption that parentNode is always used to render the child to is the thing breaking "add()" for you. add() ends up calling _uiAddChild, which probably moves the bounding box from where it's at to the parentNode (which again, is widget-parent's contentBox by default).


Yes, that is what I have figured out.

I'm wondering about this comment In _uiAddChild() that says

Code:
// Insert or Append to last child.


After that it tries to insert the the added childs boundingbox compared to its siblings. Is that valid in this use case?

sdesai wrote:
Actually, one step up from _uiAddChild, this may be a better place to customize where children are rendered, because it's the code deciding where the child should go after it's been added:


Yes, I tried to set the _childrenContainer property prior to using add(), becuse that is what is sent as an argument to ._uiAddChild() (used as parentNode), but that did not help either.

sdesai wrote:
Looking at it in isolation, we should probably add a check around it to make sure the child is not already a descendent of the widget-parent boundingBox before defaulting to parentNode. However, looking at it from a higher level, it seems like in general there's a feature here where the widget-parent could support a rendering template which identifies where certain "types" of children are rendered to.


Yes, exactly that is what I'm looking for. Using a template for this would be great. That is basically what I'm trying to achieve with my CONTENT_TEMPLATE and using placeholders for each widget. Thats much easier to maintain and adds the possibility to move around widgets inside that template, instead of changing the order of each add()-call.

You get a good overview on the end result, easy to read and understand. The renderUI() tends to grow and can get messy and I rather separate logic from design. By doing like this I can achieve that, but still use javascript.

sdesai wrote:
Just thinking through this template feature a little more: For your use case, if I were to add another Foo child (currrently parent-child is designed for a 0..n child setup), what would happen? Should it go into the "foo" div? Or is your parent designed to have exactly 1 Foo and 1 Bar?


At the moment, only designed to have exactly 1 Foo and 1 Bar. If more Foos and Bars is needed, we have to add more placeholders to the CONTENT_TEMPLATE -> foo-1, bar-1, foo-2, bar-2 etc.

Code:
CONTENT_TEMPLATE: "<div>" +
                     "<div name='foo-1'></div>" +
                     "<span> ==== </span>" +
                     "<div name='bar-1'></div>" +
                     "<div> +
                         "<div name='bar-2'></div>" +
                     "</div> +
                     "<span name='foo-2'></span>" +
                   "</div>",



Thank you, Satyen for spending time on this.

Regards, Tor

Satyen Desai

YUI Developer

  • Username: sdesai
  • Joined: Tue Dec 09, 2008 4:17 pm
  • Posts: 302
  • GitHub: sdesai
  • Gists: sdesai
  • YUI Developer
  • Offline
  • Profile
Tags:

Re: Child widgets together with DOM nodes

Post Posted: Wed Jun 09, 2010 4:22 pm
+0-
Quote:
After that it tries to insert the the added childs boundingbox compared to its siblings. Is that valid in this use case?


No, you'd probably over-ride that method completely. WidgetParent is designed to handle a list of chidren (unlike your fixed children use case)


Quote:
Yes, I tried to set the _childrenContainer property prior to using add(), becuse that is what is sent as an argument to ._uiAddChild() (used as parentNode), but that did not help either.


Right, that just changes the default from the parent contentBox to some other box [ but the children are still added/inserted into that single box ].

Quote:
Yes, exactly that is what I'm looking for. Using a template for this would be great.


If you want to file an enhancement request for it, we can look at it for a future release if other similar use cases come up.

Regards,
Satyen

Caridy Patino

YUI Contributor

  • Username: caridy
  • Joined: Mon Dec 08, 2008 5:40 pm
  • Posts: 491
  • Location: Miami, FL
  • Twitter: caridy
  • GitHub: caridy
  • Gists: caridy
  • IRC: caridy
  • YUI Developer
  • Offline
  • Profile

Re: Child widgets together with DOM nodes

Post Posted: Wed Jun 09, 2010 7:41 pm
+0-
Hey Satyen,

Following with the initial example from Tor, I don't see why this doesn't work:

Code:
renderUI: funciton () {
  var foo = new Y.My.FooWidget({boundingBox: '#myid'});
  this.add(foo);
}


And why do I need to call render() manually like this:

Code:
renderUI: funciton () {
  var foo = new Y.My.FooWidget({boundingBox: '#myid'});
  foo.render();
  this.add(foo);
}


I have a couple of questions:

1- Does a child has to be a sub-dom structure of its parent?
2- Why the parent tries to override the boundingBox even when the child specifically defines the selector?

Here is one of my use-cases:

- I want to create a widget that supports multiple templates. Every template defines placeholders for some child widgets using specific selectors.

I think this is a valid request.

Best Regards,
Caridy

Satyen Desai

YUI Developer

  • Username: sdesai
  • Joined: Tue Dec 09, 2008 4:17 pm
  • Posts: 302
  • GitHub: sdesai
  • Gists: sdesai
  • YUI Developer
  • Offline
  • Profile

Re: Child widgets together with DOM nodes

Post Posted: Thu Jun 10, 2010 9:35 am
+0-
I would assume that:

Code:
renderUI: funciton () {
  var foo = new Y.My.FooWidget({boundingBox: '#myid'});
  this.add(foo);
}


In the first case, add() is invoking foo.render() with the default parentNode (the parent's contentBox), so that's where the child widgets end up being inserted.

Code:
renderUI: funciton () {
  var foo = new Y.My.FooWidget({boundingBox: '#myid'});
  foo.render();
  this.add(foo);
}


In the second case, you're invoking foo.render() providing a custom parentNode (although it's not shown in the snippet, it's the foo div into which you want the foo widget to be rendered), and then calling add(). Since render() is a one-time operation, when add calls foo.render() it's a no-op, and the widget stays where you originally rendered it.

Quote:
1- Does a child has to be a sub-dom structure of its parent?


That is the implicit assumption. However, the Parent implementation can set the _childrenContainer property to over-ride the default container into which the children are rendered, and it doesn't necessarily need to reside under Parent.

Quote:
2- Why the parent tries to override the boundingBox even when the child specifically defines the selector?


I don't think it does. Are you maybe referring to node the child is rendered into (which is explained above - the argument passed to render)?

Quote:
- I want to create a widget that supports multiple templates. Every template defines placeholders for some child widgets using specific selectors.

I think this is a valid request.


I agree it's a valid request (it's the enhancement request I suggested above).
  [ 11 posts ] Go to page 1, 2 Next
Display posts from previous:  Sort by  
You cannot post new topics in this forum
You cannot reply to topics in this forum
You cannot edit your posts in this forum
You cannot delete your posts in this forum