How can I get add an icon with text to a menu item in GWT?
The following does not work:
<ui:with field='res' type='my.package.MyResources' />
<g:MenuItem text="test"><g:Image resource="{res.myIcon}" /></g:MenuItem>
Resulting error:
Not allowed in an HTML context: <g:Image resource='{res.myIcon}'>
public interface MyResources extends ClientBundle {
#Source("myIcon.png")
ImageResource myIcon();
}
The MenuItem allows only HTML or plain text as its content. So you cannot use an Image widget, but you can very well use an <img> element and retrieve the image URL from the ImageResource referenced by <ui:with> using getSafeUri() (you can call no-arg methods in UiBinder templates). In your case:
<g:MenuItem>
<img src="{res.myIcon.getSafeUri}"/><span>Your text here</span>
</g:MenuItem>
Or programmatically, using a simple template:
public interface MyTemplate extends SafeHtmlTemplates {
#Template("<img src=\"{0}\" /><span>{1}</span>")
SafeHtml createItem(SafeUri uri, SafeHtml message);
}
instantiated via:
MyTemplate template = GWT.create(MyTemplate.class)
and used like so:
new MenuItem(template.createItem(
yourResources.myIcon().getSafeUri(),
SafeHtmlUtils.fromString("Your text here")));
Please try this
<g:MenuItem>
<div class="className"/> MyMenuWithImg
</g:MenuItem>
That css class have the background image.
I ended up extending the GWT MenuItem class in a way that one can supply an ImageResource from ui-binder:
class MyMenuItem extends MenuItem {
#Uresstructor
public resMenuItem(String text, ImageResource res) {
super(SafeHtmlUtils.fromString(text));
ImageResourceRenderer renderer = new ImageResourceRenderer();
setHTML(renderer.render(res) + " " + text);
}
}
Usage:
<ui:with field='res' type='path.to.MyIconResources' />
<g:MenuBar>
<my:MyMenuItem text="test" icon="{res.myIcon}" />
</g:MenuBar>
If anyone comes up with a better idea (now or in the future) please comment accordingly.
Related
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
Lets say there are two scala templates in view folder
file1.scala.html
container.scala.html
Now I want to pass first template into 2nd template (container.scala.html) from controller. Like:
public class Application extends Controller {
static Result isItPossible()
{
Result theFile=ok(file1.render());
return ok(container.render(theFile));
}
}
Is it possible? If it is, How I can do it?
You can pass a rendered template to the container template. container would need to have some Html parameter:
container.scala.html:
#(content: Html)
<p>Here's my content: #content </p>
And from within the controller:
public class Application extends Controller {
return ok(container.render(file1.render()));
}
It's worth to mention that you don't need to combine your wrapping containers in the controller, as template engine has an ability to use Layouts for it (as described in docs). In that case you can use it like that:
container.scala.html
#()(content: Html)
<p>Here's my content: #content </p>
file1.scala.html
#container() {
<b>this is content of <i>file1</i> template</b>
}
controller
public class Application extends Controller {
static Result itIsPossible() {
return ok(file1.render());
}
}
in my current project i've faced a problem of customizing IndicatingAjaxLink in wicket, is there any solution to change standart gif image to my own?
For example we have following listeneer
add(new IndicatingAjaxLink("closeReceivedBillspanel") {
public void onClick(AjaxRequestTarget art) {
// some timeconsuming calculations
}
});
as user clicks this link, the gif with loading appears, and i want to change this gif, is there any solution for this problem?
Have your page implements the IAjaxIndicatorAware interface
public class BasePage extends WebPage implements IAjaxIndicatorAware {
public BasePage(final PageParameters parameters) {
// Home link
AjaxLink<Page> homeLink = new AjaxLink<Page>("homeLink") {
private static final long serialVersionUID = 1L;
#Override
public void onClick(AjaxRequestTarget target) {
setResponsePage(HomePage.class);
}
};
add(homeLink);
}
#Override
public String getAjaxIndicatorMarkupId() {
return "indicator";
}
This way, you can set, in the html, any image you want to display when the loading appears by changing the image in the "img" tag
<div id="indicator" style="display: none;">
<div class="indicator-content">
Please wait... <wicket:link><img src="images/loading.gif" width="16" height="16" alt="loading" /></wicket:link>
</div>
</div>
Create yoru own custom class like, (copy whats inside IndicatingAjaxLink and update)
public class MyIndicatingAjaxLink<T> extends AjaxLink<T> implements IAjaxIndicatorAware {
private final MyAjaxIndicatorAppender indicatorAppender = new MyAjaxIndicatorAppender();
.
//rest of the code is same as IndicatingAjaxLink class
.
}
Also you need a custom AjaxIndicatorAppender within your customIndicatingAjaxLink and you need to override below method of indicatorAppender to return path of your custom image
protected CharSequence getIndicatorUrl()
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?)
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>