How insert view (child) into div of parent layout? Vaadin 10 / Flow - java

I have a component implementing RouterLayout like this:
#Tag("side-menu")
#HtmlImport(value = "src/components/side-menu.html")
public class SideMenu extends PolymerTemplate<TemplateModel> implements RouterLayout {
#Id("menu")
private PaperListBox listBox = new PaperListBox();
public SideMenu() {
listBox.addMenu(new PaperItem("tutorial", TutorialView.class));
listBox.addMenu(new PaperItem("icons", IconsView.class));
}
}
I route a view child of the parent layout
#Route(value=IconsView.VIEW_ROUTE, layout = SideMenu.class)
public class IconsView extends Div {
public static final String VIEW_ROUTE = "icons";
public IconsView() {
add(new Label("ICONS VIEW"));
}
}
But the result overwrote all the content of side-menu.html file.
side-menu.html template base format
<side-menu>
<div>App Name</div>
<div id="menu"></div>
<div id=contenido><!-- I want to show here my view Icons --></div>
</side-menu>
But the result it's
<side-menu>
<div>
<label>ICONOS VIEW</label>
</div>
</side-menu>
The expected result is:
<side-menu>
<div>App Name</div>
<div id="menu"></div>
<div id=contenido>
<div>
<label>ICONOS VIEW</label>
</div>
</div>
</side-menu>

In vaadin documentation said this:
You can add child components to templates using the Component or
Element API, but because PolymerTemplate uses the shadow DOM the
shadow tree is rendered instead of the elements children that are in
the light DOM.
This means that the template needs to have a <slot></slot> to mark
the place where the light DOM elements should be rendered.
I found a solution for this composite layout:
I only needed to be modified my template side-menu.html and add a <slot> tag like this:
<side-menu>
<div>App Name</div>
<div id="menu"></div>
<slot></slot>
</side-menu>
And then when my view is loaded, it's rendered into the <slot> tag into the template

The problem in your case is that since you have bare template without functionality and databinding and custom template model, the server side is not aware of its contents. Thus Div.add() thinks it as empty div and overwrites its content.
One approach in your case would be to modify the content via Element API, which could be something like:
public IconsView() {
label = new Label("ICONS VIEW");
getElement().appendChild(label.getElement());
}
See API spec
https://demo.vaadin.com/javadoc/com.vaadin/vaadin-core/10.0.0.rc5/com/vaadin/flow/dom/Node.html#appendChild-com.vaadin.flow.dom.Element...-
Another approach would be to extend the html template to be fully functional polymer element
There is more about that e.g. here:
https://vaadin.com/docs/v10/flow/polymer-templates/tutorial-template-list-bindings.html

Related

How to use BorderBehavior with ajax rerendering

I want to use a BorderBehavior to add additional markup around different components.
MyBorderBehavior extends BorderBehavior {
}
<wicket:border>
<div class="myBorderBehavior">
<wicket:body />
<!-- some more HTML code -->
</div>
</wicket:border>
So at some point, I add a new MyBorderBehavior to a component.
MyComponent myComponent = new MyComponent().add(new MyBorderBehavior());
But when I want to refresh myComponent via ajax
ajaxRequestTarget.add(myComponent)
The Html markup of MyBorderBehavior is drawn again without removing the already existing markup of MyBorderBehavior in the dom. As a result, the markup of MyBorderBehavior is shown twice or more often in the browser.
How can I add a border to a component which can be re-rendered with ajax?
A working solution I found so far is to remove the markup of MyBorderbehavior manually via JavaScript:
MyBorderBehavior extends BorderBehavior {
#Override
public void onComponentTag(Component component, ComponentTag tag) {
super.onComponentTag(component, tag);
IValueMap attributes = tag.getAttributes();
attributes.put("class", attributes.getString("class", "") + " hasMyBorderbehavior");
}
}
Wicket.Event.subscribe('/dom/node/removing', function(a, attributes, c, d, e) {
var component = $('#' + attributes['id']);
if (component.hasClass("hasMyBorderbehavior"))
{
component.closest(".myBorderBehavior").replaceWith(component);
}
});
But this seems to be very hacky.
There are three cases I found so far which are relevant for me:
The component with the BorderBehavior is rerendered via ajax
A parent component of the component with the BorderBehavior is rerendered via ajax
The whole page is rerendered
You can make your Behavior temporary and this will overcome the problem when re-painting with Ajax, but might break it when re-rendering the whole page.
A better solution probably is to override beforeRender of BorderBehavior and do nothing when this is an Ajax request:
#Override public void beforeRender(Component component) {
if (!RequestCycle.get().find(AjaxRequestTarget.class).isPresent()) {
super.beforeRender(component);
}
}
Same for afterRender().
The code above is for Wicket 8.x where RequestCycle.get().find(Class<T>) returns Optional<T>. If you use older version then you need to check for null instead: if (RequestCycle.get().find(AjaxRequestTarget.class) != null)

Wicket: Can I create the markup before initializing the java component?

html file which looks like this:
<wicket:panel>
<form wicket:id="adminUIForm">
<table wicket:id="datatable" class="datatable"></table>
<div wicket:id="institutTablePanel"></div>
</form>
</wicket:panel>
But in my Java Code I only want to instantiate the component institutTablePanel, when a row in the datatable is clicked, because the constructor looks like this:
target.add(new InstitutTablePanel("institutLabelPabel", selectedContact));
And selectedContact cannot be empty.
Is there a possibility of doing this without getting an error that it cannot find the component in my JavaCode?
Edit:
This might be helpful
#Override
protected Item<Kontakt> newRowItem(final String id, final int index,
final IModel<Kontakt> model) {
Item<Kontakt> rowItem = new Item<Kontakt>(id, index, model);
rowItem.add(new AjaxEventBehavior("onclick") {
private static final long serialVersionUID = 1L;
#Override
protected void onEvent(AjaxRequestTarget target) {
selectedKontakt = model.getObject();
target.add(new InstitutTablePanel("institutTablePanel", selectedKontakt));
}
});
return rowItem;
}
Create an empty WebMarkupContainer that is a blank div and add it to your page/parent component. Next, onClick, use addOrReplace and replace the the WebMarkupContainer with the InstitutTablePanel. Both the WebMarkupContainer and the InstitutTablePanel should have the same wicket:id
You have to target existing components in your ajax calls, therefore i recommend initializing an InstitutTablePanel on initialization of the page with the model as Panel model. Passing model objects without the wrapping model is discouraged in wicket and leads to unnecessary problems.
You can change the InstitutTablePanel to be empty when its model object is null or empty.
Also, Wicket is built around passing models to components and not plain objects which might seem unnecessary at first but when complexity rises the pattern is preventing many bugs and errors.
Edit: dont forget to use setoutputmarkupid(true) on the added panel.

Call javascript function from wicket 6, Link's "onclick ()"

I have the following java and html code:
this.leakageModel = new PropertyListView<Leakage> ( "leakage", new ArrayList<Leakage> ()) {
private static final long serialVersionUID = 1L;
#Override
protected void populateItem (final ListItem<Leakage> item) {
Link<String> brandLink = new Link<String> ("brandLink") {
private static final long serialVersionUID = -480222850475280108L;
#Override
public void onClick () {
//change another model in the page to update
//another table when the link is clicked
}
};
brandLink.add (new Label ("brand"));
item.add (brandLink);
} };
add (this.leakageModel);
html file:
<tr wicket:id="leakage" class="testClass">
<td class="testClass">
<a wicket:id="brandLink" href="#">
<span wicket:id="brand"></span>
</a>
</td>
</tr>
What I want to do is to be able to call a javascript function from inside the onClick() method.
The model update that I currently do inside the onClick method works well and updates another table on the page.
However everything I have tried to call a javascript function or change the css style has failed.
For instance:
Adding a css class:
add (new AttributeAppender("class", new Model("anotherclass"), " "));
Using an AjaxLink type instead, and a number of other things I have tried to no avail.
On a related note, my original intention is to hide all rows in the table except the one I have clicked. Maybe I can do this just from the Java code and have no need for Javascript at all, but updating the css as above doesn't work.
Any suggestions as to what am I doing wrong?
On a related note, my original intention is to hide all rows in the
table except the one I have clicked.
Instead of answering your question, I will try to provide a solution to your problem :).
It makes perfect sense to hide the table row via javascript. I would suggest doing it with Jquery as described in Hiding all but first table row with jQuery:
$("#myTbl tr:not(nth-child(3))").hide();
Now, you have to execute the above javascript snippet each time a user clicks your Wicket link. For this, you can for example create your own link class like this:
public class JavascriptLink extends Label{
public JavascriptLink(String id, String label) {
super(id, label);
add(new AttributeAppender("onclick", "...your javascript here..."));
}
}
I leave it to you to combine the jquery with the JavascriptLink to meet your requirements. It should work going in this direction.

Setting a custom onClick on one of the widgets in a GWT DisclosurePanel's caption

I have the following ui situation:
<g:DisclosurePanel width="100%" ui:field="disclosurePanel">
<g:customHeader>
<g:HorizontalPanel width="100%" ui:field="tableRow">
<g:cell width="16px" horizontalAlignment="ALIGN_CENTER">
<g:Image url="images/plus-icon.gif" ui:field="icon"></g:Image>
</g:cell>
<g:cell width="20%">
<g:Label ui:field="productName"></g:Label>
</g:cell>
<g:cell>
<g:Anchor ui:field="info"><ui:msg>More info...</ui:msg></g:Anchor>
</g:cell>
</g:HorizontalPanel>
</g:customHeader>
<g:VerticalPanel width="100%" ui:field="details">
<!-- details panel here -->
</g:VerticalPanel>
</g:DisclosurePanel>
And I would like to bind an event handler method to the Anchor info. However every widget I have in the header opens and closes the disclosurepanel, even if I hook something on the info by:
#UiHandler("info")
public void onInfoClicked(ClickEvent event)
{
// do something custom, but do not open/close the disclosurepanel
}
I hope that this can be achieved without making a custom composite or stuff. Can you help me?
DisclosurePanel's header is private inner class ClickableHeader:
private final class ClickableHeader extends SimplePanel {
private ClickableHeader() {
// Anchor is used to allow keyboard access.
super(DOM.createAnchor());
Element elem = getElement();
DOM.setElementProperty(elem, "href", "javascript:void(0);");
// Avoids layout problems from having blocks in inlines.
DOM.setStyleAttribute(elem, "display", "block");
sinkEvents(Event.ONCLICK);
setStyleName(STYLENAME_HEADER);
}
#Override
public void onBrowserEvent(Event event) {
// no need to call super.
switch (DOM.eventGetType(event)) {
case Event.ONCLICK:
// Prevent link default action.
DOM.eventPreventDefault(event);
setOpen(!isOpen);
}
}
}
assuming your code:
#UiField
DisclosurePanel p;
//call this somewhere once on widget creation to
//prevent header's default click handler
private void myInit()
{
p.getHeader().unsinkEvents(Event.ONCLICK);
}
#UiHandler("info")
public void onInfoClicked(ClickEvent event)
{
//trigger "standard" click handler if you want
if(someCondition) {
//convert GwtEvent descendant to NativeEvent descendant;
p.getHeader().onBrowserEvent(event.getNativeEvent().<Event> cast());
}
// do something custom, but do not open/close the disclosurepanel
}
Well it works as it is designed. Each element you put inside the DisclosurePanel has a click handler which opens/ closes it. So inside your header there should be only images and or text, basically onyl elements with no atteched logic. I would consider arranging your html elements different, so the link isn't inside the header...
If you really, really have to put it inside the header, you can add this to your Anchor click event:
disclosurePanel.setOpen(!disclosurePanel.isOpen());
This will restore the previous state of the DisclosurePanel. The good part is, that it is so fast, that you don't even see the DisclosurePanel opening/closing, the bad part is, that this is really bad design....
Addition:
the DisclosurePanel uses Anchors to be displayed. An anchor allowes per definition only block elements, so you shouldn't use it like this at all! (See Is putting a div inside a anchor ever correct?)

How to disable / change style of wicket button link in onClick()

In a Wicket app, I have a bunch of <button> elements to which I'm attacking a Link component. Now in the onClick() method of the component I want to disable or change the style of the button. How can I do that? Calling setEnabled(false) has no effect.
Repeated uses of onClick() are operating on the same object in memory. If you're not using Ajax, you can still maintain some state in an anonymous subclass of Link. Then, you can use onBeforeRender() and onComponentTag() to change how it is displayed each time.
Link<Void> link = new Link<Void>("myLink") {
private String customCSS = null;
private boolean customEnabled = true;
public void onClick() {
if (/* test to determine disabled */) {
customCSS = "disabled";
customEnabled = false;
} else {
customCSS = null;
customEnabled = true;
}
}
#Override
protected void onComponentTag(ComponentTag tag) {
super.onComponentTag(tag);
if (customCSS != null)
tag.put("class", customCSS);
}
#Override
public boolean isEnabled() {
return super.isEnabled() && customEnabled;
}
};
AttributeModifiers (or other behaviors) aren't good for this case because, if you add them in the onClick() method, they will begin stacking on the same link for each click - since they are maintained as part of the Link's state.
Your Link can keep track of all manner of state, allowing your onClick() method to enable/disable/change/etc with repeated clicks.
You can also override onBeforeRender(), isVisible(), and other methods that are run each time the link is displayed on the page. The constructor, onConfigure(), and others are run just once, regardless of how many times you click the button.
I don't think this is an entirely good idea in Wicket. Of course it could be done by trickery, but it's far simpler to either:
Override the isEnabled() method to return a value derived from the model of the form/component.
Attach an AttributeModifier when you create the component, and use a model for it which returns a value derived as above.
Whichever you choose, the principle is to let Wicket "pull" rendering information in rather than pushing it explicitly.
The answer provided by Michael Borgwardt is nearly correct.
The problem is that you use Link. Disabled Links use <span> instead of
<a>/<button> and are surrounded with <em> by default. Using Button
component will set 'disabled' attribute in the element.
I would like to add, that you need to use HTML button element instead of <a> (link). Original answer can be counfusing, because Link and Button also exist in Wicket.
I think AjaxCallDecorator should be the class you need to use to disable/change style of the button.
The problem is that you use Link. Disabled Links use <span> instead of <a>/<button> and are surrounded with <em> by default.
Using Button component will set 'disabled' attribute in the element.
Take a look at SimpleAttributeModifier and AttributeAppender. Depending on your actual requirements one of those should do the trick. SimpleAttributeModifier adds or replaces an attribute of any HTML-Tag that has a prepresentation in wicket (replaces the css class), while AttributeAppender appends to the attributes (adds another css class). This should work for enabling/disabling buttons as well but I haven't tried that.
Example:
Label label = new Label("id", "Some silly text.")
add(label);
label.add(new SimpleAttributeModifier("class", "my-css-class");
For Ajax you'll have to add the component to the target as well.
More detailed example:
Java code:
import org.apache.wicket.behavior.AttributeAppender;
import org.apache.wicket.behavior.SimpleAttributeModifier;
import org.apache.wicket.markup.html.WebMarkupContainer;
import org.apache.wicket.markup.html.WebPage;
import org.apache.wicket.markup.html.form.Button;
import org.apache.wicket.markup.html.form.Form;
import org.apache.wicket.markup.html.link.Link;
import org.apache.wicket.model.Model;
public class DemoPage extends WebPage {
public DemoPage() {
Form form = new Form("form");
add(form);
final WebMarkupContainer wmc = new WebMarkupContainer("greenText");
form.add(wmc);
form.add(new Link("redLink"){
#Override
public void onClick() {
wmc.add(new SimpleAttributeModifier("class", "redText"));
}});
final Button boldButton = new Button("boldButton"){
#Override
public void onSubmit() {
wmc.add(new AttributeAppender("class", true, new Model<String>("boldText"), " "));
}};
form.add(boldButton);
Link disabler = new Link("buttonDisabler") {
#Override
public void onClick() {
boldButton.add(new AttributeAppender("disabled", true, new Model<String>("disabled"), " "));
}
};
form.add(disabler);
}
}
corresponding HTML:
<html>
<head>
<style>
.redText {
color: red;
}
.greenText {
color: green;
}
.boldText {
font-weight: bold;
}
</style>
</head>
<body>
<form wicket:id="form">
<div class="greenText" wicket:id="greenText">This is Green.</div><br />
Make it red<br />
<input type="submit" wicket:id="boldButton" value="Make it bold" /><br />
Disable the button
</form>
</body>
</html>

Categories