tags:

views:

95

answers:

4

Is it possible to replace known html element with my widget component? (Emphasis on the word 'replace', I don't want to put the widget in that element. :)

<body>
  <img />
  <div />
  <a id="tmpEl" />
  ...
  <img />
</body>

would become

<body>
  <img />
  <div />
  <div class="gwt-panel">...</div>
  ...
  <img />
</body>

I tried something like this...

tmpEl.getParentElement().replaceChild(myPanel.getElement(), tmpEl);

...but the resulting DOM elements were 'deaf', i.e. they did not receive click events. (To make this work I would probably have to call RootPanel.get().adopt(widget), but that method is not accessible.)

For a second I thought HTMLPanel.addAndReplaceElement could be the answer, but that only works when your 'placeholder' element is (direct) child of HTMLPanel widget. Which is obviously not my case. :(

Note please that I only know id of that element, I'm not creating it. Simply put: I need exactly what the question says.

As for 'DOM manipulation at higher level': I will happily manipulate the DOM at highest possible level if it lets me place widget instead of that placeholder element.

A: 

I would suggest you try something like this...

<body>
  <a id="tmpEl" />
</body>

then in somewhere create a widget (e.g. textBox widget)

TextBox tb = new TextBox();

After you have created the widget you can get it's DOM object by using tb.getElement() (I'm not sure that's the correct method name, but it's definitely something like getElement)

After that you could use

Element parent = Document.getElementById('tmpEl').getParent();
parent.removeChild(Document.getElementById('tmpEl'));
parent.appendChild(tb.getElement());

One more thing that add() does is call Widget.onAttach() on the widget that is being added to the panel. onAttach does some work to register the widget to receive events.

You could try calling tb.onAttach(); It might also be necessary to call RootPanel.detachOnWindowClose(tb); (as mentioned in a previous post)

markovuksanovic
That's basically the solution I mentioned in the original question, and it does not work. Only that instead of replaceChild you used removeChild+appendChild. Also with appendChild you put element at the very end of its parent, which is not necessarily where the placeholder could have been.
Jaroslav Záruba
I must say that I have a feeling that you're doing something wrong here - it doesn't look like gwt style, at least not to me.
markovuksanovic
Have you attached the widget to a parent widget? This way the widget won't get automatically attached.
markovuksanovic
As for 'not GWT-style': GWT let's me remove elements by their id, GWT let's me append into elements by their id. Therefore I honestly can't think of reason why I am not allowed to put my widget in place of the element that I'm about to remove. I fail to see the difference.
Jaroslav Záruba
As for attaching the widget: How can I attach the widget when its parent is the RootPanel? (I'm newbie to GWT, excuse me if that's something obvious.)
Jaroslav Záruba
You are absolutely allowed to do that - but from my point of view it is not a nice thing to do - gwt compiler would handle that much better. But let's not go into that discussion. BTW, I edited the answer.
markovuksanovic
Well, it doesn't have a parent if you didn't attach it manually in this case. If you used sth like RootPanel.get.add(tb), tb would have its parent implicitly set.
markovuksanovic
I still don't see the difference from what I have written in the original question. What have you edited please? That does not work. (And the reason is IMO that I would have to call adopt, as mentioned in the question.) As for compiler doing it better - I think you don't understand, I want the original element go away, and my widget to be exactly at where the element was - meaning 'exactly in the DOM structure'. How does that compiler do? (Even if it was not better I would use that.)
Jaroslav Záruba
RootPanel.get().add(tb) is of no use here. :(
Jaroslav Záruba
Check the edited answer for the explanation. I know you have already figured out but I just wanted to figure out exactly what was going on under the hood.
markovuksanovic
A: 

I agree with markovuksanovic - you should consider DOM manipulation on a "higher level". For example, the functionality you need is provided via the InsertPanel interface, which FlowPanel implements:

FlowPanel mainPanel = new FlowPanel();
Hyperlink link = new Hyperlink("Something cool");
mainPanel.add(link);
mainPanel.add(new Label("Bla bla"));

// Now we want to replace the first element of the FlowPanel with a TextBox
// We can do that by its index...
mainPanel.remove(0);

// ...or Widget instance
mainPanel.remove(link);

// Now we add a TextBox at the beginning
mainPanel.add(new TextBox(), 0);

As you can see, this code is much more readable (at least to me), you don't manipulate the DOM directly (referencing via ids and such). Of course, there are places where direct DOM manipulation is beneficial (optimizing, most notably), but for the most part, I'd avoid juggling Elements, ids and such :) (at least in GWT)

Igor Klimer
I don't need to create link, thank you. I need to replace existing one. Please read the question.
Jaroslav Záruba
I was just presenting a use case - the way you want to manipulate the DOM is common in "normal" JS frameworks, but GWT tries to create a layer between the underlying DOM tree and what you want to add to it. So, following markovuksanovic's comment, I showed how what you described in your question, could be done the "GWT way". Of course, manipulating the DOM directly is often a necessity, when working with existing applications or trying to integrate a GWT widget/app with an existing website.
Igor Klimer
And that's exactly the case here: I'm writing widget for otherwise GWT-unaware website. I have <a id="widgetPlaceholder" href="http://domain.com/?someParams">some text</a> in place where owner of the website wants my widget to be created. When rendering the widget I need URL to read further content from ("href" attribute comes into play), and should something go wrong the anchor element serves as an alternate way of serving the content - good old clickable link. Placing the widget in the anchor element is a no-no for several obvious reasons.
Jaroslav Záruba
Thanks for the clarification - you should have put that info in the question - I (and probably markovuksanovic) thought that you just weren't familiar with GWT and tried to use the framework in the "wrong" way :)
Igor Klimer
+1  A: 

It seems that calling widget.onAttach() after inserting widget into DOM does the trick.

class MyWidget extends Composite
{
  ...

  public void attach()
  {
    /* Widget.onAttach() is protected
     */
    onAttach();

    /* mandatory for all widgets without parent widget
     */
    RootPanel.detachOnWindowClose(this);
  }
}

tmpEl.getParentElement().replaceChild(myWidget.getElement(), tmpEl);
myWidget.attach();

Credit goes to André at Google Web Toolkit group.

I still wonder though why there is no RootPanel.addAndReplaceElement(Widget, Element), similar to HTMLPanel.addAndReplaceElement(Widget, Element).

Jaroslav Záruba
Should work, Anchor.wrap() does basically the same thing.
Chris Lercher
Works. :) And yes, it is the same thing (as mentioned in the group-post), cheers!
Jaroslav Záruba
+1  A: 

The solution is probably not so much different from what Igor suggested. I'd write something like this:

RootPanel rootPanel = RootPanel.get();
Element anchorElement = DOM.getElementById("tmpEl");
Anchor anchor = Anchor.wrap(anchorElement);

rootPanel.remove(anchor);
rootPanel.insert(new HTML("<div class='gwt-panel'>...</div>", 0); 
Chris Lercher
I believe it is fundamentally different, because this would probably work. Wrapping existing element with widget was where André M. from GWT-group took inspiration with his solution.
Jaroslav Záruba