gwtp Accessing uibinder input, div, span using DOM - dom

I wanted to access the elements in uibinders using DOM or document interface in presenters, is it possible.
wanted to get the element and attach handler to it.
I have input, select, div, span element in uibinders. Is it possible to achieve.
I tried to get using DOM and Document it returns null.
<g:HTMLPanel styleName="{style.content1}" ui:field="containerPanel1">
<div class="{style.rightAlign}" id="container">
<g:FormPanel styleName="{style.formLayout}" ui:field="formPanel1">
<g:HTMLPanel width="100%" height="100%">
<div id="inner" class="{style.horiCon}">
<div class="{style.label}">
<ui:msg key="name" description="Name of the user">Name</ui:msg>
</div>
<input type="text" id="firstName" name="name"
placeholder="FirstName" class="{style.textboxFirstName}" />
<input type="text" id="lastName" placeholder="LastName"
name="name" class="{style.textboxLastName}" />
<span id="nameError" class="{style.errorMsgHide}">Error</span>
</div>
</g:HTMLPanel>
</g:FormPanel>
</div>
</g:HTMLPanel>
Presenter
#Override
protected void onBind() {
super.onBind();
Window.alert("" + DOM.getElementById("firstName"));
// returns null;
}
#Override
protected void onReveal() {
super.onReveal();
Window.alert("" + DOM.getElementById("firstName"));
// returns <input class="GG1RCY0BHB" id="firstName" name="name" placeholder="FirstName" type="text">
}
Onbind() returns null and onreveal() returns
after that when I do
TextBox.wrap(DOM.getElementById("firstName"));
I am getting
java.lang.AssertionError: A widget that has an existing parent widget may not be added to the detach list
at com.google.gwt.user.client.ui.RootPanel.detachOnWindowClose(RootPanel.java:136)
at com.google.gwt.user.client.ui.TextBox.wrap(TextBox.java:69)
What is the issue? Can I use Rootpanel in presenter to get the reference?
Please help me.
Thanks In Advance,
Jose.

To access DOM elements the elements need to get attached to the DOM first, otherwise it will return null. You can use the DOM.getElementByID("id") in overridden onLoad() method in your ui-binder java class .
#Override
void onLoad()
{
DOM.getElementByID("id");
}
You Mentioned the input, select, div, span element in uibinders. Why not use a ui:field and access the field directly to add handlers etc? Just a thought.
select is GWT ListBox there is FlowPanel is a div likewise.

Related

Extract part of Wicket form in a reusable panel

1 year ago, i ported an existing application from older wicket to version 8, without too much thinking how it works.
Now, i want to understand a bit better to simplify the code i found...
For this, in a big form page, i have code like the sample below.
The "situationdiv?"'s have the same structure, i want to make it a reusable panel.
I wanted to pass the model "CLBX.?" to the panel constructor, but i can't find the good way to do it.
Is there another better solution ?
Please help me find how to do this...
<form wicket:id="form">
<input name="check1" id="check1" type="checkbox" wicket:id="IGEN1ST"
value="1" oninput="visibiliteTxtArea('1')"/>
<div id="body1div"><textarea id="body1" wicket:id="T01K1ST01" name="body1"
maxlength="1000" control_length="1"
style="display: none" rows="10"></textarea></div>
....
<div id="situationdiv1">
<h2><wicket:message key="F8.Mot2">Durée de la situation</wicket:message></h2>
<wicket:message key="F8.Per">Période du</wicket:message>
<input type="text" class="text date" name="startDate0" id="DPERBEG0"
wicket:id="CLBX.0.DPERBEG" value=""/>
<wicket:message key="F8.Tot2">au</wicket:message>
<input type="text" class="text date" name="endDate0"
wicket:id="CLBX.0.DPEREND" id="DPEREND0"
value=""/>
......
</div>
<div id="situationdiv2">
<h2><wicket:message key="F8.Mot2">Durée de la situation</wicket:message></h2>
<wicket:message key="F8.Per">Période du</wicket:message>
<input type="text" class="text date" name="startDate1" id="DPERBEG1"
wicket:id="CLBX.1.DPERBEG" value=""/>
<wicket:message key="F8.Tot2">au</wicket:message>
<input type="text" class="text date" name="endDate1"
wicket:id="CLBX.1.DPEREND" id="DPEREND1"
value=""/>
......
</div>
....
</form>
public class MyWebPage extends WebPage {
//Data is filled/organized from DB.
//Structure: Map<String, Object>
//mostly <String, String> or <String, Integer>
//sometimes anything else like <"CLBX", ArrayList<Map<String, Object)>> (for situations)
// and in these, only <String, String> or <String, Integer>
public MyWebPage(Map<String, Object> data) {
this.init(data, readonly);
}
private void init(Map<String, Object> data, boolean readonly) {
Form<String> form = new Form<String>("form") {
#Override
protected void onSubmit() {
super.onSubmit();
log.debug("form submit");
}
}
add(form);
form.setDefaultModel(new CompoundPropertyModel(data));
}
}
You could use FormComponentPanel to reuse the common form components.
FormComponentPanel is a FormComponent that could have its own markup, like a Panel. As such, it could have FormComponent children, like TextField, DropdownChoice, etc.

Why a seperate markup identifier for a container and for the fragment?

Can I add multiple fragments to a container? Apparently not, but why do I need an seperate markup identifier for the container, and one for the fragment?
In HTML,
<div wicket:id="container"></div>
<div wicket:id="container2"></div>
<wicket:fragment wicket:id="myfragment">
<h3 wicket:id="dexter"></h3>
<h3 wicket:id="deedee"></h3>
</wicket:fragment>
<wicket:fragment wicket:id="myotherfragment">
<h3 wicket:id="foo"></h3>
<h3 wicket:id="bar"></h3>
</wicket:fragment>
The web page,
public class MyPage extends WebPage {
private Component container;
public MyPage() {
container=new MyFragment("container",this);
add(container);
container=new MyOtherFragment("container2",this);
add(container);
One of the fragents is (the other is alike),
public MyFragment(String id,WebPage page) {
super(id,"myfragment",page);
Label label=new Label("dexter", "Omelette du fromage");
add(label);
Label label2=new Label("deedee","That's all you can say!");
add(label2);
}
Fragments are meant for easy reuse of small components, to fill a dynamic form or similar.
When using fragment you should use a ListView to populate them. That way you can easily build dynamic markup. You need to reference the fragment you want to use and tell wicket where to place it in the markup.
Instead of the two container divs, use a list
<div wicket:id="fragmentList">
<div wicket:id="fragment"></div>
</div>
There is no point in having two fragments with the same markup in then (two H3 tags). Instead you can have a header fragment and a label fragment or whatever you need.
<wicket:fragment wicket:id="headerFragment">
<h3 wicket:id="dexter"></h3>
<h3 wicket:id="deedee"></h3>
</wicket:fragment>
<wicket:fragment wicket:id="labelFragment">
<label wicket:id="foo"></label>
<label wicket:id="bar"></label>
</wicket:fragment>
<wicket:fragment wicket:id="inputFragment">
<label wicket:id="foo"></label>
<input wicket:id="bar" type="text">
</wicket:fragment>
In your WebPage you can add the fragments needed to a list of fragment and add them to a ListView
public HomePage(final PageParameters parameters)
{
List<Fragment> fragments = Arrays.asList(new HeaderFragment("fragment", this),
new LabelFragment("fragment", this),
new InputFragment("fragment", this));
add(new ListView<Fragment>("fragmentList", fragments) {
#Override
protected void populateItem(ListItem<Fragment> item)
{
final Fragment frag = _item.getModelObject();
_item.add((Fragment) frag);
}
});
}
There's no such thing as a container here. There's the fragment, which has its own id and stands separate from the main markup tree, and there is a place where you can attach your fragment to. This place will have its own id like any other Wicket component.
So the first id in the Fragment constructor tells Wicket where to attach the fragment and the second tells it which fragment to attach there.

Dynamically adding form elements with jQuery and Zend_Form

I have a form in which people shall be able to add the same portion of elements with a plus-button, so that something like this is produced:
<div id="person-1" class="person">
<input type="text" name="name-1" id="name-1" />
<input type="text" name="age-1" id="age-1" />
</div>
<!-- as of here, it's JS created -->
<div id="person-2" class="person">
<input type="text" name="name-2" id="name-2" />
<input type="text" name="age-2" id="age-2" />
</div>
<div id="person-3" class="person">
<input type="text" name="name-3" id="name-3" />
<input type="text" name="age-3" id="age-3" />
</div>
I already managed to write jquery-code that allows me to add the same elements once again with a new id (name-1, age-1, name-2, age-2, name-3, age-3, …).
Of course, Zend_Form does not know about name-2 and name-3, so it just drops them when the form contains an error and is displayed again. Neither can I access the value of name-2 with $form->getValue('name-2'). I have to go over raw $this->getRequest()->getPost().
Is there a better method I can use to combine Zend_Form and javascript-based added form elements (of same type like an hardcoded element).
Caveat: In the real problem, it’s select and not input. Found out this could make a difference (with ->setIsArray(true)), but using select would blow up the example code.
What you could do is create a subform container inside your main form and add an X amount of subforms to that container.
For example:
class My_Form extends Zend_Form
{
private $_numPersons = 1;
public function setNumPersons($numPersons)
{
$this->_numPersons = (int) $numPersons;
}
public function init()
{
$container = new Zend_Form_SubForm();
$this->addSubForm($container, 'persons');
for($index = 0; $index < $this->_numPersons; $index++) {
$personForm = new My_PersonForm();
$container->addSubForm($personForm, $index+1);
}
}
}
When rendered, the input fields will have names like persons[1][name]. Note the $index+1, Zend_Form does not allow a form to be named '0'.
Ofcourse, you should only use this method if the amount of person subforms is limited.
Another strategy would be to override the isValid method and use a single My_PersonForm form to validate all the person data.
Sidenote; the above code will only work when you define the numPersons as part of the options set, when creating the form instance. E.g.;
$form = new My_Form(array('numPersons' => 10));

GWT: How do I get a reference to a button from the RootPanel?

I'm using GWT 2.4. In my onModuleLoad method, given a string id, how do I get a reference to an existing button on the page from the RootPanel object? I'm trying this
public void onModuleLoad() {
...
final Button submitButton = (Button) RootPanel.get("submit");
but getting the compile error, "Cannot cast from RootPanel to Button".
Edit:
I thought using an iterator would heal the pain, but no dice. Here is the default HTML loaded (notice the button with id="submit") ...
<div>
<form name="f">
File name: <input type="text" size="25" id="filename" name="filename"
value="" /> <input type="button" id="submit" name="submit"
value="Submit" /> <input type="hidden" name="curId" id="curId"
value="" />
</form>
</div>
<div id="content"></div>
but this code in onModuleLoad causes a NullPointerException (because the submitButton id can't be found) ...
public void onModuleLoad() {
final Button submitButton = (Button) getWidgetById("submit");
submitButton.addStyleName("submitButton");
...
private Widget getWidgetById(final String id) {
Widget eltToFind = null;
final Iterator<Widget> iter = RootPanel.get().iterator();
while (iter.hasNext()) {
final Widget widget = iter.next();
final Element elt = widget.getElement();
if (elt.getId() != null && elt.getId().equals(id)) {
eltToFind = widget;
break;
} // if
} // while
return eltToFind;
}
Thanks, - Dave
You can get your input element using Document.get().getElementById("submit").<InputElement>cast(), but you won't be able to get a Button widget out of it.
If you change your code to read <button type="button" id="submit" name="submit" value="Submit"> instead of <input> (the type=button part is technically not needed, but some browsers will treat it like a type=submit if you don't), then you can use Button.wrap():
Button button = Button.wrap(Document.get().getElementById("submit"));
Some of GWT widgets have static method wrap() which allows to convert DOM elements to widget instances.
Button submit = Button.wrap(DOM.getElementById("submit"));
The get() method returns the RootPanel associated with the browser element, not the widget with that name. A RootPanel is a subclass of ComplexPanel, so I think your best bet is to use the methods from ComplexPanel to iterate through the widgets and so find the one that you want that way.
Wrap creates a layer and will remove all the functionalities written previously on that button.
I used the below code to resolve the same.
Document.get().getElementById("buttonId").getStyle().setDisplay(false)

What is this error Found widget <g:ListBox class='dropdownbx' name='deleteDigits' ui:field='deletedigs'> in an HTML context

I get this error when I run my Gwt app
Found widget in an HTML context
Here is a snippet of the xml that it complains about:
<!-- ... -->
<g:HTML ui:field="localPanel">
<fieldset>
<legend>Local</legend>
<label for="btn" >BTN:</label><input type="text" ui:field="btn" class="txtbx numeric" maxlength="10" name='btn'/>
<label for="stdprt">SDT PRT:</label><input type="text" ui:field="stdprt" class="txtbx" readonly="readonly" name='stdPrt'/>
<label for="rateArea">Rate Area:</label><input type="text" ui:field="ratearea" class="txtbx" readonly="readonly" name='rateArea'/>
<br/>
<label for="deleteDigits">Delete Digits:</label><g:ListBox ui:field='deletedigs' class="dropdownbx" name='deleteDigits'/>
</fieldset>
</g:HTML>
<g:Button ui:field="submit2">Submit</g:Button>
</g:HTMLPanel>
There are certain tags (those which GWT says are make an "HTML context") that can't have widgets inside them. For instance, <g:HTML><g:Label /></g:HTML> is illegal because a only expects HTML elements and not widgets. However, if you change that to <g:HTMLPanel><g:Label /></g:HTMLPanel> it will work.
The particular snippet triggering the error in your code is the <g:ListBox ui:field='deletedigs' class="dropdownbx" name='deleteDigits'/> that's contained in the <g:HTML ui:field="localPanel">. Make that <g:HTML> into a <g:HTMLPanel> and it should all work.