Borgy Manotoy
Borgy Manotoy

Reputation: 2038

Apache Wicket AjaxRequestTarget ListView component not refreshing or updating

I am really new with apache wicket (Literally) and I got a task that needs refreshing a table(ListView) via ajax after adding an item (from bootstrap modal box).

Let's say I have a page display the laboratory details (room) and a table displaying the list of laboratory devices.

To add a laboratory device, there is a button that when clicked... will display a modal box (popup) where the use can select a laboratory devices from a dropdown.

When a laboratory device is added (from popup modal), the modal will be closed/hidden, then the table component will be refreshed via ajax.

Problem:

Code for refreshing the component contentDevices seems not working. After adding the laboratory device, I can see that it is reflected in the database. But for unknown reasons, the container with table showing the list of laboratory devices is not refreshing.

I would like to refresh the container/component contentDevices after adding/removing laboratory device. The component contentDevices has a child which is a table that displays the list of devices in the laboratory.

The issue now is, I still need to refresh the page (browser refresh) inorder for the newly added laboratory device to reflect.

By the way, Device class does not have onBeforeRender(). The new instance of this class will replace the existing contentDevices.

It's been days already but I cannot figure out why it's not refreshing. This is really easy if this is just plain HTML/JS, but I am really clueless with apache wicket.

The page/html structure is something like this:

Structure or Layout:

<!-- HTML document -->
<Page>
  ...
    <!-- Content section -->
    <div id="content">
      <div id="detailsContainer">
        ...
      </div>
      <div id="devicesContainer">
        ...
        <div id="contentDevices">
          <!-- Other components (ex. table) here that needs to be refreshed after adding/removing device -->
        </div>
        ...
      </div>
      </div>
    </div>
  ...
</Page>

Code:

...

@Override
protected void onSubmit(AjaxRequestTarget target, Form<?> form) {
    super.onSubmit(target, form);

    laboratoryService.addDevice(this.laboratoryModel.getObject(), selectedDevice.getLabel(), selectedDevice.getValue());

    // Refresh laboratory model and update the list of laboratory devices from DB
    Laboratory laboratory = laboratoryService.getLaboratory(this.laboratoryModel.getObject().getId());
    this.laboratoryModel = new Model<>(laboratory);

    // Start: refreshing `contentDevices`
    WebMarkupContainer cContent = (WebMarkupContainer) getPage().get("content");
    WebMarkupContainer devicesContainer = (WebMarkupContainer) cContent.get("devicesContainer");
    Component cDevices = devicesContainer.get("contentDevices");
    cDevices.replaceWith(new Device("contentDevices", getMyPageContext(), this.laboratoryModel));

    devicesContainer.addOrReplace(cDevices);
    cContent.addOrReplace(devicesContainer);

    target.add(cContent);
    // End refreshing `contentDevices`

    // Hide bootstrap modal box
    MyUtil.hideBootstrapModalBox(target, "[data-locator=addDeviceModalBox]");

    // showCustomAlert - show success message
    target.appendJavaScript("showCustomAlert('success', 'Added laboratory device.', true);");
}

...

Device class:

public class Device extends AbstractMyPagePanel {

    private static final long serialVersionUID = 1L;

    @Autowired
    private LaboratoryService laboratoryService;

    private IModel<Laboratory> laboratoryModel;


    public Device(String id, MyPageContext myPageContext, IModel<Laboratory> laboratoryModel) {
        // `laboratoryModel` is passed until most parent class which is `org.apache.wicket.markup.html.panel.Panel`
        super(id, myPageContext, laboratoryModel);

        this.laboratoryModel = laboratoryModel;

        add(createAddDeviceButton());
        add(new AddDeviceModalBox("addDeviceModalBox", myPageContext, this.laboratoryModel));
        add(createRowsContainer());
        setOutputMarkupId(true);
    }

    private Component createAddDeviceButton() {
        return new WebMarkupContainer("addDeviceButton") {
            private static final long serialVersionUID = 1L;
            @Override
            protected void onConfigure() {
                super.onConfigure();
                setVisible(isAdmin());
            }
        };
    }

    private Component createRowsContainer() {
        WebMarkupContainer result = new WebMarkupContainer("rowsContainer") {
            private static final long serialVersionUID = 1L;
            @Override
            protected void onConfigure() {
                super.onConfigure();
                setVisible(CollectionUtils.isNotEmpty(this.laboratoryModel.getObject().getRoomDevices()));
                setOutputMarkupPlaceholderTag(true);
            }
        };

        // This part here is OK, laboratory model and devices are updated
        System.out.println("\n");
        System.out.println("CREATE-ROW-CONTAINER");
        System.out.println("---------------------------------------------");
        System.out.println("[device-count]: " + this.laboratoryModel.getObject().getRoomDevices().size());
        System.out.println("---------------------------------------------");
        System.out.println("\n");

        System.out.println("\n");
        System.out.println("LABORATORY-DEVICES");
        System.out.println("---------------------------------------------");
        for (LaboratoryDevice device : this.laboratoryModel.getObject().getRoomDevices()) {
            System.out.println("[device]: " + device.getName());
        }
        System.out.println("---------------------------------------------");
        System.out.println("\n");

        // This part here is not OK, `populateItem()` is not called
        // `rows.children` is null, but `rows.data.transientModelObject` is updated and contains the latest added laboratory device
        // Same goes when `add` is used instead of `addOrReplace`, the issue is still there
        result.addOrReplace(new ListView<LaboratoryDevice>("rows", this.laboratoryModel.getObject().getRoomDevices()) {
            private static final long serialVersionUID = 1L;
            @Override
            protected void populateItem(ListItem<LaboratoryDevice> item) {
                LaboratoryDevice device = item.getModelObject();
                item.add(new Label("name", device.getName()));
                item.add(new Label("sn", device.getSn()));
                item.add(new WebMarkupContainer("removeButton").setVisible(isAdmin()));
                item.add(new RemoveLaboratoryDeviceModalBox("removeLaboratoryDeviceModalBox",
                                                  getMyPageContext(), item.getModel()));
            }
        });
        return result;
    }

}

I know this is very basic but I could not find anything the really works in the net :(

Thanks!

Upvotes: 0

Views: 1457

Answers (1)

martin-g
martin-g

Reputation: 17533

You should check whether there are any errors:

  • errors in the server logs
  • errors in the browser's Dev Tools Console

I guess there is an JS error in the Dev Tools saying that Wicket cannot find an HTML element with id contentXYZ. If this is the case then make sure you call cContent.setOutputMarkupId(true) where cContent is instantiated, i.e. in the constructor of the page.

You do not need cDevices.setOutputMarkupId(true) because you never add cDevices to the target.

And calling setOutputMarkupId(true) in onXYZ(AjaxRequestTarget) most of the time is too late because the whole page is already rendered and the component/element needs to render an id attribute which will be used later when the Ajax response tries to find the old HTML element and replace it with the new one (created or updated in onXYZ(AjaxRequestTarget).

Upvotes: 0

Related Questions