Hiding a component in Wicket - java

I have a component inside an <a/> tag that opens a popup window on click. It's an "add to favourite" link which works on KML files. My KML file has a field named "favourite[boolean]". Now I'd like to hide or show my "add to favourite" link. The KML list is generated with a table:
public class CustomTracksAjaxDataTable<T> extends CustomAjaxDataTable<T> {
public CustomTracksAjaxDataTable(String id, List<IColumn<T>> iColumns,
ISortableDataProvider<T> tiSortableDataProvider, int rowsPerPage) {
super(id, iColumns, tiSortableDataProvider, rowsPerPage);
}
protected void onEventHandler(AjaxRequestTarget ajaxRequestTarget,
KMLFile file) {
setKMLData(file); // it just update map, dont care about it
add(new FavouriteStarIconState(file.isSaved()));
}
}
I tried to add a behavior thus:
public class FavouriteStarIconState extends AbstractDefaultAjaxBehavior {
private boolean isFavourite;
public FavouriteStarIconState(boolean isFavourite) {
super();
this.isFavourite = isFavourite;
}
#Override
protected void respond(AjaxRequestTarget target) {
if (isFavourite) {
target.appendJavascript("jQuery('.map_container_star').css(
{'display' : 'none' });");
} else {
target.appendJavascript("jQuery('.map_container_star').css(
{'display' : 'block' });");
}
}
#Override
public void renderHead(IHeaderResponse response) {
response.renderOnLoadJavascript(getCallbackScript().toString());
}
}
The part of the HTML containing the component:
<div id="map_container">
<a wicket:id="favourite_star" class="map_container_star"></a>
</div>
This isn't working. I got the same result with component.setVisible(false). How can I get hiding to work?

Well it finds out that I make a terrible mistake and put javascript appending in wrong place. AJAX request was not rendered. The proper class was CustomAjaxDataTable being extended by my class CustomTracksAjaxDataTable. I just add
new AjaxEventBehavior( "onclick" )
and override
protected void onEvent( AjaxRequestTarget ajaxRequestTarget )
and it works great now

You could use a CSS class like this
.hiddenClass
{
visibility:hidden;
}
then with AttributeModifier you add the class to the element
component.add(new AttributeModifier("class", "hiddenClass"));
or add the style directly to the style attribute
component.add(new AttributeModifier("style", "visibility:hidden;"));

Related

How to refresh label after changing color in wicket

I use Wicket 1.5
When i change color it is really changed on the page only after refreshing using F5. How to refresh it in backend?
I use this lines for changing color:
dateDescription.add(AttributeModifier.replace("style", "color:red;"));
add(dateDescription);
UPDATE #1
Now i use AJAX but still have to refresh page for changing color. Could you tell me what i did wrong?
// in page class
public class FilterUpdateBehavior extends AjaxFormComponentUpdatingBehavior {
public FilterUpdateBehavior(String event) {
super(event);
}
#Override
protected void onUpdate(AjaxRequestTarget target) {
RefreshResult result = getResult(target);
if (result.getStatus() == RefreshResultStatus.DATE_NOT_SET) {
dateIntervalFilterPanel.setAlarmDateStatus(true);
} else {
dateIntervalFilterPanel.setAlarmDateStatus(false);
}
}
}
// in date panel class
dateDescription.add(new AttributeModifier("style", new AbstractReadOnlyModel<String>() {
private static final long serialVersionUID = 1L;
#Override
public String getObject() {
String cssClass = null;
if (isAlarmDateStatus()) {
cssClass = "color:red;";
} else {
cssClass = "color:black;";
}
return cssClass;
}
}));
add(dateDescription);
UPDATE #2
public RefreshResult getResults(AjaxRequestTarget target) {
// ... somewhere here additional logic of getting particulate RefreshResult
target.add(table);
target.add(paging);
target.add(loadingPanel);
return new RefreshResult(resultType);
}
UPDATE #3 FINAL (IT HELPED ME)
I miss this code line when i change isAlarmDateStatus, now it works fine. Thanks to Andrea!
target.add(dateDescription);
your code line looks right but you must use AJAX to reflect your changes without reloading the entire page. Unfortunately Wicket 1.5 is really outdated and there are few resources online to provide you an example of AJAX support. You might try to look into the old 1.5 AJAX examples code here:
https://github.com/apache/wicket/tree/build/wicket-1.5.17/wicket-examples/src/main/java/org/apache/wicket/examples/ajax/builtin

GWT Updating Main Content using EventBus and ActivityMapper - Can GWT help me to do this cleaner?

I would like to know
Am I doing things (the following) too complicated?
Is there a better way to update the main content of an activity that allows me to bookmark the event calendar of a store via URL like #MainPlace:eventCalendar?storeId=<id>?
I'm having this ActivityMapper here
public class AppActivityMapper implements ActivityMapper {
private ClientFactory clientFactory;
private MainActivity mainActivity;
// ..
#Override
public Activity getActivity(Place place) {
if (place instanceof LoginPlace) {
return new LoginActivity((LoginPlace) place, clientFactory);
} else if (place instanceof MainPlace) {
if(this.mainActivity == null) {
this.mainActivity = new MainActivity((MainPlace) place, clientFactory);
} else {
this.mainActivity.updateMainContent(((MainPlace) place).getMainContentToken());
}
return this.mainActivity;
}
return null;
}
}
and a MainActivity that controls my MainView that is just a menu ond the left side and the main content on the right side.
I want to decouple my views like in Best Practices for Architecting GWT App which is why I'm trying to control the main content by using events that get fired as something gets clicked in my MenuView.
Therefore I am initializing some event handlers in my MainActivity that react to clicks on the buttons in my menu to delegate the update to the MainView.
public class MainActivity extends AbstractActivity implements MainView.MainPresenter {
#Override
public void start(AcceptsOneWidget panel, EventBus eventBus) {
this.mainView = this.clientFactory.getMainView();
this.mainView.setPresenter(this);
this.mainView.initialize();
this.eventBus = eventBus;
this.eventBus.addHandler(HomeClickedEvent.TYPE, new HomeClickedHandler() {
#Override
public void onHomeClicked(HomeClickedEvent event) {
goTo(new MainPlace("home"));
}
});
this.eventBus.addHandler(EventCalendarClickedEvent.TYPE, new EventCalendarClickedHandler() {
#Override
public void onEventCalendarClicked(EventCalendarClickedEvent eventCalendarClickedEvent) {
goTo(new MainPlace("eventCalendar?storeId=" + eventCalendarClickedEvent.getStoreId()));
}
});
panel.setWidget(this.mainView.asWidget());
}
#Override
public void goTo(Place place) {
this.clientFactory.getPlaceController().goTo(place);
}
#Override
public void updateMainContent(String currentMainContentToken) {
this.mainView.updateMainContent(currentMainContentToken);
}
}
this event gets fired by MenuPresenter.clickedEventCalendar() that reacts to a click on the corresponding menu entry of the MenuView:
public class MenuPresenter implements MenuView.MenuPresenter {
// ..
#Override
public void clickedEventCalendar(Long storeId) {
this.eventBus.fireEvent(new EventCalendarClickedEvent(storeId));
}
}
One of the things I really don't like is this where I append parameters to the token e.g. to display the event calendar of a store given by storeId:
#Override
public void onEventCalendarClicked(EventCalendarClickedEvent eventCalendarClickedEvent) {
goTo(new MainPlace("eventCalendar?storeId=" + eventCalendarClickedEvent.getStoreId()));
}
is there a cleaner solution for a problem like this in GWT? I don't like the fact that I'd have to parse that string in my actual event calendar. Am I using the ActivityMapper wrong or is there simply no other way to do this?
This question should really be split into several separate ones, but that's maybe something to keep in mind for the future. If you're asking one thing then it's easier to answer thoroughly and others can find the answer easier too.
Anyway, I can see a few improvements:
use EventBinder to get rid a bit of the cruft when handling and creating new events.
if you just want to let the presenter know that a button was pressed on in the view (associated with that presenter) sending a custom event over the event bus is a bit of an overkill. Depending on your needs you can expose the button in your view's interface:
public interface Display {
HasClickHandlers getButton();
}
And then just register the ClickHandler in your presenter.
Or, if you need to do something view- and presenter- related on the click, register the ClickHandler in your view and call the presenter:
// In MainView:
#UiHandler("button")
void handleClick(ClickEvent event) {
// Do some stuff with view,
// like hide a panel or change colour
panel.setVisible(false);
// Let the presenter know that a click event has been fired
presenter.onEventCalendarClicked();
}
you're right - creating MainPlace like you are proposing is wrong. You are creating the token too soon - that's what the tokenizer associated with the place is for. You should create MainPlace by passing just the storeId to the constructor - why should MainPresenter (or any other class using this place) should know how to create the token? MainPlace should look more like this:
public class MainPlace extends Place {
private final Long storeId;
public MainPlace(Long storeId) {
this.storeId = storeId;
}
public Long getStoreId() {
return storeId;
}
public static class Tokenizer implements PlaceTokenizer<MainPlace> {
#Override
public MainPlace getPlace(String token) {
return new MainPlace(Long.valueOf(token));
}
#Override
public String getToken(MainPlace place) {
return "eventCalendar?storeId=" + place.getStoreId();
}
}
}
Now, it's the Tokenizer's responisibily to create and parse the token. Just remember to register it on your PlaceHistoryMapper.

How to create a Link that supplies its own Markup?

I'm trying to create a link that will hide or show a part of my page. The link should be reusable and display one of two images, depending on state.
Adding the two subcomponents on every page where I use the link is kind of clunky so I wanted to create a component that behaves like a link while automatically adding its content.
This is the Link component:
public class ToggleVisibilityLink extends AjaxFallbackLink<Boolean>
{
public ToggleVisibilityLink(final String id, final IModel<Boolean> model)
{
super(id, model);
setOutputMarkupId(true);
add(new Image("collapseImage")
{
#Override
public boolean isVisible()
{
return !getModelObject();
}
});
add(new Image("expandImage")
{
#Override
public boolean isVisible()
{
return getModelObject();
}
});
}
#Override
public void onClick(final AjaxRequestTarget target)
{
setModelObject(!getModelObject());
if (target != null)
{
target.add(this);
send(this.getParent(), Broadcast.EXACT, target);
}
}
}
And this is how I currently use it in HTML (this is added to the page or panel where I use the link):
<a href="#" wicket:id="collapseExpandLink" class="collapseExpandLink">
<wicket:link>
<img src="collapse.png" wicket:id="collapseImage" class="collapseExpandImage collapse">
</wicket:link>
<wicket:link>
<img src="expand.png" wicket:id="expandImage" class="collapseExpandImage expand">
</wicket:link>
</a>
And the corresponding Java call:
add(new ToggleVisibilityLink("collapseExpandLink", new PropertyModel(this, "hidden")));
But I want to be able to skip the body inside the link as one would have to know about the internals of ToggleVisibilityLink.
I experimented with IMarkupResourceStreamProvider, using Dynamic markup in Wicket as a starting point. By googling I found another example where the poster was only able to get that to work when using a Panel, and I was able to do that as well. But I'd really like to keep the link and not package it inside a Panel, as I would not be able to style the link in the markup.
I'm also open to alternatives to encapsulate the link and its body.
I was able to get this to work using setBody(), even though I was trying to sabotage myself quite badly (I had duplicate libraries, my own incompatible jQuery library import and a custom resource versioning strategy).
Here is the current ToggleVisibilityLink:
public class ToggleVisibilityLink extends AjaxFallbackLink<Boolean>
{
static {
Application.get().getSharedResources().add("ToggleVisibilityLinkCollapse",
new MyPackageResource(ToggleVisibilityLink.class, "collapse.png"));
Application.get().getSharedResources().add("ToggleVisibilityLinkExpand",
new MyPackageResource(ToggleVisibilityLink.class, "expand.png"));
}
public ToggleVisibilityLink(final String id, final IModel<Boolean> model)
{
super(id, model);
setOutputMarkupId(true);
setEscapeModelStrings(false);
setBody(new BodyModel(model));
}
#Override
public void onClick(final AjaxRequestTarget target)
{
setModelObject(!getModelObject());
if (target != null)
{
target.add(this);
send(this.getParent(), Broadcast.EXACT, target);
}
}
private static final class BodyModel extends AbstractReadOnlyModel<String>
{
private final IModel<Boolean> model;
private BodyModel(final IModel<Boolean> model)
{
this.model = model;
}
#Override
public String getObject()
{
return this.model.getObject() ?
"<img src=\""
+ RequestCycle.get().urlFor(new SharedResourceReference("ToggleVisibilityLinkExpand"), null)
+ "\" class=\"collapseExpandImage expand\">"
:
"<img src=\""
+ RequestCycle.get().urlFor(new SharedResourceReference("ToggleVisibilityLinkCollapse"), null)
+ "\" class=\"collapseExpandImage collapse\">";
}
}
}
Where MyPackageResource is a simple Implementation of PackageResource (why is that constructor protected?).
Then one can simply add the ToggleVisibilityLink to a container:
super.add(new ToggleVisibilityLink("collapseExpandLink", new PropertyModel(this, "hidden")));
and
<a wicket:id="collapseExpandLink" class="collapseExpandLink"></a>
and get notified via Event when the link is clicked.

Customizing IndicatingAjaxLink in wicket

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()

GWT RootLayoutPanel - Problem rendering page from second to first (first works fine)

Sorry if this was already answered before. I did a little searching and found nothing that could solve my problem. I created an application with Spring Roo, then converted to a GWT app.
All the code generated by Spring Roo is only for CRUD. Now i want to add a Calendar for make appointments, so i need to move to another page.
I´ve added this code to
ScaffoldDesktopShell.java()
public ScaffoldDesktopShell() {
initWidget(BINDER.createAndBindUi(this));
startButton.addClickHandler(new ClickHandler() {
#Override
public void onClick(ClickEvent event) {
RootLayoutPanel.get().add(new NovoPainel());
}
});
}
...
Then created a new UIbinder, called it NovoPainel() and added this code:
public NovoPainel() {
initWidget(uiBinder.createAndBindUi(this));
botao.addClickHandler(new ClickHandler() {
#Override
public void onClick(ClickEvent event) {
RootLayoutPanel.get().clear();
RootLayoutPanel.get().add (new ScaffoldDesktopShell());
}
});
}
Everything goes fine moving from my root panel to NovoPainel, but when i need to go back to rootPanel the page doesn´t render correctly.
EX: Doesn´t show ** ValuePicker ** to click on left panel and render on center.
This is my RootPanel
and this image is when navigate from rootPanel to NovoPainel
and finally this one is returning from NovoPainel to RootPanel
You have to integrate with Roo generated architecture so that you can still benefit from Roo scaffolding.
Roo generated code hides most of behavior in _Roo_Gwt classes and it is because GWT doesn’t currently support ITDs. So changes have to be made in derived classes by overriding methods from _Roo_Gwt class.
To navigate application use Places, ActivityMapper and ActivitiManager (you can find good read on #Thomas Broyer posterous and GWT help).
If you take a look in ScaffoldDesktopShell.ui.xml - page is devided in three main areas.
ApplicationMasterActivities class is responsible for master area.
masterActivityManager.setDisplay(shell.getMasterPanel());
proxyListPlacePicker in ScaffoldDesktopApp.init() generates place change event with apropriate ProxyListPlace.
public void onValueChange(ValueChangeEvent<ProxyListPlace> event) {
placeController.goTo(event.getValue());
}
ApplicationMasterActivities class creates appropriate Activity in Master area by checking EntityProxy type contained in ProxyListPlace object.
public Activity getActivity(Place place) {
if (!(place instanceof ProxyListPlace)) {
return null;
}
ProxyListPlace listPlace = (ProxyListPlace) place;
return new ApplicationEntityTypesProcessor<Activity>() {
#Override
public void handlePet(PetProxy isNull) {
setResult(new PetListActivity(requests, ScaffoldApp.isMobile() ? PetMobileListView.instance() : PetListView.instance(), placeController));
}
#Override
public void handleOwner(OwnerProxy isNull) {
setResult(new OwnerListActivity(requests, ScaffoldApp.isMobile() ? OwnerMobileListView.instance() : OwnerListView.instance(), placeController));
}
}.process(listPlace.getProxyClass());
}
Navigation is created by listing all EntityProxy's in ScaffoldApp class
protected HashSet<ProxyListPlace> getTopPlaces() {
Set<Class<? extends EntityProxy>> types = ApplicationEntityTypesProcessor.getAll();
HashSet<ProxyListPlace> rtn = new HashSet<ProxyListPlace>(types.size());
for (Class<? extends EntityProxy> type : types) {
rtn.add(new ProxyListPlace(type));
}
return rtn;
}
To output meaningfull name in navigation menu they are rendered using ApplicationListPlaceRenderer
public String render(ProxyListPlace object) {
return new ApplicationEntityTypesProcessor<String>() {
#Override
public void handlePet(PetProxy isNull) {
setResult("Pets");
}
#Override
public void handleOwner(OwnerProxy isNull) {
setResult("Owners");
}
}.process(object.getProxyClass());
}
So you have to create new Activity.
public class SomeActivity extends Composite implements Activity{
private static SomeActivityUiBinder uiBinder = GWT
.create(SomeActivityUiBinder.class);
interface SomeActivityUiBinder extends UiBinder<Widget, SomeActivity> {
}
private AcceptsOneWidget display;
public SomeActivity() {
initWidget(uiBinder.createAndBindUi(this));
}
#Override
public String mayStop() {
return null;
}
#Override
public void onCancel() {
onStop();
}
#Override
public void onStop() {
this.display.setWidget(null);
}
#Override
public void start(AcceptsOneWidget panel, EventBus eventBus) {
this.display = panel;
this.display.setWidget(this);
}
}
<!DOCTYPE ui:UiBinder SYSTEM "http://dl.google.com/gwt/DTD/xhtml.ent">
<ui:UiBinder xmlns:ui="urn:ui:com.google.gwt.uibinder" xmlns:g="urn:import:com.google.gwt.user.client.ui">
<g:HTMLPanel>
Hello world!
</g:HTMLPanel>
</ui:UiBinder>
Create appropriate EntityProxy. It is only to obey ProxyListPlace mechanism.
public interface SomeEntityProxy extends EntityProxy {
}
Create SomeActivity in A
#Override
public Activity getActivity(Place place) {
if (!(place instanceof ProxyListPlace)) {
return null;
}
Activity activity = super.getActivity(place);
if (activity == null) {
ProxyListPlace listPlace = (ProxyListPlace) place;
if (SomeEntityProxy.class.equals(listPlace.getProxyClass())) {
activity = new SomeActivity();
}
}
return activity;
}
Add place to navigation in ScaffoldApp or override getTopPlaces in derived class.
rtn.add(new ProxyListPlace(SomeEntityProxy.class));
Set correct menu rendering text in ApplicationListPlaceRenderer
#Override
public String render(ProxyListPlace object) {
String label = super.render(object);
if(label == null) {
if (SomeEntityProxy.class.equals(object.getProxyClass())) {
label = "Some activity";
}
}
return label;
}
Code in GitHub.
GWT 2.1 introduced new classes that implements the Model-View-Places pattern (MVP). This pattern (and the GWT 2.1 concepts) are heavily based on best practices from developers who have build scalable GWT-based applications, so many people are migrating in this direction.
Roo generates a GWT 2.1 application; all of its navigational code is built on top of Activities and Places. The reason I bring this up is it sounds like you are attempting to side-step a lot of this navigational framework to implement your own. I'm not sure, but I believe your problem is coming from the fact that the MVP code is getting confused as a result.
My recommendation would be to work through the GWT MVP article linked above first. Do it completely separate of Roo, because the application that Roo generates is more complex. Once you have a good handle on it, go back through the Roo-generated application and it will likely make more sense.
You can create two div tags in your Porject.html file respectively with id firstdivtag_id1 and seconddivtag_id2.
Display first page by using
RootPanel.get("firstdivtag_id1").add(Panel1);
And then to switch over to another panel use
RootPanel.get("seconddivtag_id2").add(Panel2);

Categories