Wicket - Add body tag attribute - wicket

I'm stuck -
I need to have a Wicket Panel be able to add a class attribute to the <body> tag of whatever page it's on.
Example usage:
Java:
add(new SpecialSidebarComponent("sidebar"));
Generated HTML:
<body class="sidebar">
...
<div id="sidebar">My Wicket Panel</div>
...
</body>
I cannot add a wicket:id and make the body a Wicket component, because this makes it very difficult to add components to a page in the big page hierarchy I have, and it still also doesn't easily allow for a Panel to modify the body attribute.
I thought BodyTagAttributeModifier may be for this, but apparently it is for something else and cannot get it to function ( Wicket: how to use the BodyTagAttributeModifier class? )
Any helpful ideas?
Update:
In looking at it, it appears the BodyTagAttributeModifier class is only for a Panel's parent tag, not the Page's <body> tag:
Example (Scala syntax):
class Home extends WebPage {
add(new Sidebar("sidebar"))
}
class Sidebar(id: String) extends Panel(id) {
add(new BodyTagAttributeModifier("class", true, new Model("layout-class"), getParent))
}
Template:
<html>
<body>
<div wicket:id="sidebar">Sidebar</div>
</body>
</html>
Rendered:
<html>
<body>
<div class="layout-class">
<h1>Hello World!</h1>
</div>
</body>
</html>
Very confusing name IMHO. Doesn't solve the issue but at least makes more sense.

I personally think the Javascript option is the cleanest for this specific case. However, your comment about add(Component...) being final leads me to believe that you might be interested in the setTransparentResolver(true) method. Here's how it works...
BasePage.html
<body wicket:id="body">
<div wicket:id="panel" />
</body>
BasePage.java
public class BasePage extends Page {
public String bodyClass = "";
public BasePage() {
super();
WebMarkupContainer bodyContainer = new WebMarkupContainer("body");
bodyContainer.setTransparentResolver(true);
bodyContainer.add(new SimpleAttributeModifier("class", new PropertyModel<String>(this, "bodyClass")));
}
}
MyPage.java (extends BasePage)
public class MyPage extends BasePage {
public MyPage() {
super();
add(new SidebarPanel("panel"));
super.bodyClass = "sidebar";
}
}
Even though you are not adding the SidebarPanel directly to the bodyContainer in the BasePage, it will still work out because of setTransparentResolver(true).
For your simple case, go with the Javascript. For the general issue of feeling constrained by subclasses not being able to fit inside containers, be aware of transparent resolving.

If you really can't give the <body> tag a wicket:id (I'll assume you don't have a BasePage that every, or almost every, other page extends in which to abstract this), it'll be not possible to know at page render time (when that <body> tag is rendered) what class to append to it, it will be simply copied as is from your HTML to the output.
You could achieve the same via javascript, however. Make your Panel implement IHeaderContributor and use IHeaderResponse.renderOnDomReadyJavscript().
public abstract class SpecialSidebarComponent(String id) extends Panel
implements IHeaderContributor {
.....
public void renderHead(IHeaderResponse response){
String javascript = "document.body.setAttribute('class', 'sidebar');";
response.renderOnDomReadyJavascript(javascript);
}
....
}

I think you were on the right track with BodyTagAttributeModifier, at least according to JavaDoc. The compilation problems in the linked article stem from the use of a non-existing Constructor...
in SpecialSidebarComponent you should be able to do this:
add(new BodyTagAttributeModifier("class", Model.of("sidebar"), this));
Can't try this right now because I'm not at my development computer...

Related

Jquery not working when click ajax button

I use jquery datepicker and it not display after I click ajax button.
Is there any way to show datepicker again after click? I use wicket 8.
BasePage.java
public class BasePage extends WebPage {
...
}
BasePage.html
<body>
...
<script src="/js/jquery.min.js"></script>
<script src="/js/jqueryui.min.js"></script>
<script src="/js/main.js"></script>
</body>
HomePage.java
public class HomePage extends BasePage {
public HomePage() {
SearchForm searchForm = new SearchForm();
Form<SearchForm> form = new Form<>(new CompoundPropertyModel<SearchForm>(searchForm))
AjaxButton btn = new AjaxButton() {
protected void onSubmit(AjaxRequest target) {
// Handle search data
...
target.add(form);
}
};
TextField<String> date = new TextField<>("searchDate");
form.add(date);
form.add(btn);
}
}
HomePage.html
<wicket:extend>
<form wicket:id="form">
<input wicket:id="searchDate" class="datepicker" />
<button wicket:id="btn">Search</button>
</form>
</wicket:extend>
main.js
$(function() {
$(".datepicker").datepicker();
...
});
After click ajax button all script in file main.js not working
Please help me.
when you update form via AJAX you replace each element inside it, which includes the input field you use with datepicker. But doing so you loose the javascript setting done by main.js when page was first loaded.
You can solve this in two ways. First, you could update only those elements that need to be refreshed, for example the component that you use to show search result (I suppose there must be such an element in your code).
The second solution, more heavier and complicated, is to make a custom TextField component that execute the datepicker javascript code each time is rendered.
An example of such solution can be found is in the user guide: https://wicket-guide.herokuapp.com/wicket/bookmarkable/org.wicketTutorial.ajaxdatepicker.HomePage
I would recommend to follow the first solution as it's more natural and simpler and requires less code.
UPDATE:
If you want to refresh the textfield another simple solution is to use target.appendJavaScript​ to reapply the datepicker plugin:
target.add("$('#" + date.getMarkupId() + "').datepicker();");
this should add the datepicker to the fresh new field.

Wicket: automatic field validation does not work

I have an integer field and when i enter a non integer value (let's say a symbolic one) the Feedback panel should be triggered automatically with default message, but it does not work, I have to call it in onError method of the form by method error().
This is the textField, that i use:
RequiredTextField<Integer> intField =
new RequiredTextField<>("intValue", integerValue,Integer.class);
this is my simple FeedBackPanel:
fragment.add(new FeedbackPanel("feedback"));
it works only when i call method error() in method onError() of the form.
Could you show us how you create your Form and the Model you use? As far as I understand it you will want to bind a Model to your Fields. My best guess is that your Model does not have a property "intValue".
You might want to (re)visit the Wicket Wiki "More on Models".
I'm not sure how you are setting your model. And once you add RequiredTextField it will not allow you empty and since you set the Integer type it will not allow characters to be entered.
I have tried some code snippet which is working perfectly and validating.
HomePage.html
<html xmlns:wicket="http://wicket.apache.org">
<body>
<form wicket:id="someForm">
<div wicket:id="feedback"></div>
<input type="text" wicket:id="requiredText">
<input type="submit" value="submit">
</form>
</body>
</html>
HomePage.Java
public class HomePage extends WebPage {
private static final long serialVersionUID = 1L;
public HomePage(final PageParameters parameters) {
super(parameters);
Form form = new Form("someForm");
form.add(new FeedbackPanel("feedback"));
IModel integerValue= Model.of("");
form.add(new RequiredTextField("requiredText",integerValue,Integer.class));
add(form);
}
}
Please let me get back to me on this incase if you need anything .

Embed google-plus in GWT

I am trying to embed Google-Plus into my GWT Application. I would like it to be embedded into a HorizontalPanel. I did read +1button developers google. I didn't find any post about this particular problem in stackoverflow. My problem might be that I don't understand how to include the js into a GUI component. I would appreciate an Example of how to add the Google+ code into a Panel.
Here is how to do it:
Documentation:
<!-- Place this tag in your head or just before your close body tag -->
<script type="text/javascript" src="https://apis.google.com/js/plusone.js"></script>
<!-- Place this tag where you want the +1 button to render -->
<g:plusone></g:plusone>
in GWT:
private void drawPlusOne() {
String s = "<g:plusone href=\"http://urltoplusone.com\"></g:plusone>";
HTML h = new HTML(s);
somePanel.add(h);
// You can insert a script tag this way or via your .gwt.xml
Document doc = Document.get();
ScriptElement script = doc.createScriptElement();
script.setSrc("https://apis.google.com/js/plusone.js");
script.setType("text/javascript");
script.setLang("javascript");
doc.getBody().appendChild(script);
}
I've personally never embedded the +1 button in GWT, but the linked article seems pretty self explanatory.
In the section "A Simple Button", it indicates that the simplest way of implementing GooglePlus integration is to add this:
<script src="https://apis.google.com/js/plusone.js" />
<g:plusone></g:plusone>
First, the <script> tag should be included in your .gwt.xml file.
Then I'd implement the <g:plusone></g:plusone> like this:
public class GPlusOne extends SimplePanel {
public GPlusOne () {
super((Element)Document.get().createElement("g:plusone").cast());
}
}
(Note that this code is untested, but it's based on the simple concept that a SimplePanel can be extended to compile as any HTML element.)
Then you'd use the new GPlusOne element wherever you'd want the button to show.
I found a better way to do it:
Follow this example to have the button work on invocation on a normal html page (you can try one here http://jsfiddle.net/JQAdc/)
<!doctype html>
<html>
<head>
<meta http-equiv="content-type" content="text/html; charset=UTF-8" />
<script src="https://apis.google.com/js/plusone.js">
{"parsetags": "explicit"}
</script>
<script type="text/javascript">
function gPlusBtn(id, params) {
/* window.alert("searching for "+ id +" with params: "+ params) */
paramsObj = eval( '('+params+')' );
gapi.plusone.render(id, paramsObj );
}
// params is here just for a reference to simulate what will come from gwt
params = '{href:"http://1vu.fr", size:"tall"}';
</script>
</head>
<body>
taken from http://jsfiddle.net/JQAdc/
<div id="gplus" />
<button onclick="gPlusBtn('gplus', params)">show!</button>
</body>
</html>
Then you can call a native method to trigger the button display on Activity start (if you're using MVP).
protected native void plusOneButton(String id, String params) /*-{
$wnd.gPlusBtn(id, params);
}-*/;
You can have multiple buttons with different urls, that's why id is left as a parameter.
NOTE: for me the raw HTML works on localhost, but the GWT version. I have to deploy to the server to be able to see the results

Replace marker in nested UIBinder Structure / Error handling with MVP

I am using DockLayoutPanel as my main panel.
Dependent of the menu I click I change the center-part of the DLP.
For example I change either to form1.ui.xml or to form2.ui.xml.
Both of these forms have a "marker" implemented to display an error message:
<g:HTMLPanel ui:field="messageblock"/>
I am following the MVP Pattern (I use EventBus for communication) and so far everything works great. The only thing I can't figure out is how to replace the content of messageblock. Or to be more concret how to get access to messageblock from my MainPresenter. The main idea behind this stuff is to bundle the error-handling in one presenter...
I am looking for something like
final Panel panel = DockLayoutPanel.get("messageblock");
panel.add(subwidget);
I appreciate every hint...
You could either make the Display responsible for rendering the error (make some interface with a renderError(Error) method) or make the Display return an HTMLPanel that something else can render the error into (some interface with a HTMLPanel getErrorPanel() method). The latter is closest to what you're talking about. Have Form1 and Form2 both implement HasErrorPanel and then call getErrorPanel().add(subWidget).
Here is the conclusion I came to. Maybe it helps someone else.
It's based on what Riley Lark recommended - Thanks to Riley btw.
RegistrationPresenter here is responsible for the registration process and shows a registration form to the user. The error-message should be displayed as close as possible to the place where the error occurred.
Without error http://www.mikemitterer.at/fileadmin/stacktrace_imagehosting/screenshot-778.jpg
An error occurred:
Error popped up http://www.mikemitterer.at/fileadmin/stacktrace_imagehosting/screenshot-780.jpg
Here now a rough description how I implemented this behavior:
public class RegistrationPresenter implements Presenter {
public interface Display extends StatusDisplay, HasMessageBlock {
Widget asWidget();
void setData(RegistrationTO registration);
}
private final Display display;
private final EventBus eventBus;
...
as you can see it's Display implements HasMessageBlock:
public interface HasMessageBlock {
void showMessage(Message message);
void hideMessage();
}
There exists a UIBinder-Widget MessageBlock (MessageBlock.ui.xml + MessageBlock.java)
(messageblock will be turned to invisible in it's constructor)
<g:HTMLPanel styleName="errorblock" ui:field="messageblock">
<div id="errorMsg" class="flurid">
<div class="row">
<div class="column width_15/16">
<h3><ui:msg key="errorblock.headline">An error occurred...</ui:msg></h3>
</div>
<div class="column orientation-right islink width_1/16">
<g:Image resource='{res.xgray}' ui:field="image" />
</div>
...
The Registration-Widget now includes MessageBlock
<g:HTMLPanel styleName="registration" ui:field="panel">
<div class="uniForm maxgrid700">
<h1>
<ui:msg key="registration.headline">Registration</ui:msg>
</h1>
<c:MessageBlock ui:field="messageblock"/>
<div class="ctrlHolder">
<p class="label">
<em></em>
<ui:msg key="registration.name">Name:</ui:msg>
</p>
...
Now, if someone fires a Message
eventbus.fireEvent(new MessageEvent(new MessageImpl(Message.MESSAGETYPE.ERROR, "Server Error Message")));
every Presenter which has "HasMessageBlock" for it's Display can process/display the message:
eventBus.addHandler(MessageEvent.TYPE, new MessageEventHandler() {
#Override
public void execute(final MessageEvent event) {
display.showMessage(event.getMessage());
}
});

GWT: how to embed widgets in Anchor with UIBinder

I'd like to use the following in UIBinder, so that I can programmatically set the href of the link in my code.
<g:HTMLPanel>
<g:Anchor ui:field="link">
<g:InlineLabel ui:field="firstName"/>
<g:InlineLabel ui:field="lastName"/>
</g:Anchor>
</g:HTMLPanel>
When I try this I get:
ERROR: Found widget in an HTML context Element <g:InlineLabel ui:field='firstName'> (:7).
How can I embed widgets inside an anchor? Previously I've resorted to using:
<a id="myAnchor">
etc...
</a>
And then manipulating the DOM in my code to set the HREF, but that's ugly. Is there a better way?
The class below acts exactly like a SimplePanel (i.e., you can put an widget in it), but uses an "a" instead of a "div". If you need more widgets just put another panel in it.
import com.google.gwt.user.client.DOM;
import com.google.gwt.user.client.ui.SimplePanel;
public class Link extends SimplePanel {
public Link() {
super(DOM.createAnchor());
}
private void setHref(String href) {
getElement().setAttribute("href", href);
}
private String getHref() {
return getElement().getAttribute("href");
}
public void setTarget(String frameName) {
getElement().setAttribute("target", frameName);
}
}
It is better to use a Panel (Flow or Horizontal) and add click handlers to the panel to simulate a link. Anchor, Button and similar widgets will not allow child tags inside them.