Skip to main content

What's new in version 24.11?

ยท 5 min read
webforJ Team
webforJ Development Team

cover image

webforJ version 24.11 is live and available for development. Learn more about what main features and fixes are included in this release.

As always, see the GitHub release overview for a more comprehensive list of changes. Highlighted below are some of the most exciting changes:

New features and enhancements ๐ŸŽ‰โ€‹

The following new components, features, and enhancements to various existing behavior have been introduced in this release:

Using prefix and suffix slotsโ€‹

In order to streamline the use of many webforJ components, additional API methods have been added which allow utilization of prefix and suffix slots in various components.

These slots can be utilized in many different ways to best suit an app's behavior, such as adding icons, labels, loading spinners, clear/reset functionality, avatar/profile pictures, and many other useful behaviors.

The following components have had API methods added to support this behavior:

To utilize these slots, getter and setter methods for prefix and suffix slots have been added. Below is an example for a Button:

FlexLayout layout = FlexLayout.create().horizontal().build()
.setMargin("var(--dwc-space-m)");

Button contact = new Button("Contact us", ButtonTheme.PRIMARY);
contact.setPrefixComponent(FeatherIcon.MAIL.create());
contact.onClick(e -> {
showMessageDialog("Message has been sent!", "Sent Message");
});

layout.add(contact);

Frame mainFrame = new Frame();
mainFrame.add(layout);

Icon overhaulโ€‹

With the addition of more fine-grained control over the slots of various components, the API of one of the most common use cases for slots has also been overhauled; icons now have a more streamlined and intuitive API for use within webforJ apps.

Use the new Icon class to create an icon that can be added to a layout, or to a slot in a supporting component:

FlexLayout layout = FlexLayout.create().horizontal().build()
.setMargin("var(--dwc-space-m)");

Frame mainFrame = new Frame();

Icon image = FeatherIcon.IMAGE.create();
Icon video = FeatherIcon.VIDEO.create();
Icon music = FeatherIcon.MUSIC.create();

layout.add(image, video, music);
mainFrame.add(layout);

Icons across multiple collectionsโ€‹

The new update allows developers to mix and match icons from different collections. With support for the icon libraries included by default with webforJ - Feather, Tabler, and FontAwesome - you can now select the perfect icon for your needs without being constrained to a single collection. This is especially useful for applications that require a variety of visual styles or specific icon designs.

To do this, use the factory methods provided to select the icon pool:

FlexLayout layout = FlexLayout.create().horizontal().build()
.setMargin("var(--dwc-space-m)");

Frame mainFrame = new Frame();

Icon image = FeatherIcon.IMAGE.create();
Icon video = TablerIcon.create("video");
Icon music = FontAwesomeIcon.create("music", FontAwesomeIcon.Variate.SOLID);

layout.add(image, video, music);
mainFrame.add(layout);

Adding a custom icon poolโ€‹

Icon Pools, which allows developers to organize and manage icons more effectively. Icons can be stored in a centralized directory or a context path and accessed through a pool, simplifying the icon management process. You can create icon pools from directories using a full directory path, or a context path as shown below:

IconPoolBuilder.fromDirectory("app-pool", "context://icons");

FlexLayout layout = FlexLayout.create().horizontal().build()
.setMargin("var(--dwc-space-m)");

Frame mainFrame = new Frame();

Icon bell = new Icon("bell", "app-pool");
Icon dribbble = new Icon("dribbble", "app-pool");

layout.add(bell, dribbble);
mainFrame.add(layout);

For developers looking for more integration of their custom icon pools, custom icon factories can now be created. This allows for defining reusable icon sets within an app, which makes creating your app more consistent and helps reduce maintenance across different components and modules.

public enum AppPoolIcon implements IconFactory {
BELL, DRIBBBLE;

public Icon create() {
return new Icon(String.valueOf(this), this.getPool());
}

@Override
public String getPool() {
return "app-pool";
}

@Override
public String toString() {
return this.name().toLowerCase(Locale.ENGLISH).replace('_', '-');
}
}

Icon buttonโ€‹

Finally, the IconButton class has been added, and is ideal for actions that are best represented with just an icon, such as notifications or alerts.

FlexLayout layout = FlexLayout.create().horizontal().build().setMargin("var(--dwc-space-m)");

IconButton bell = new IconButton(FeatherIcon.BELL.create());
bell.onClick(e -> showMessageDialog("You have a new message!", "Ding Dong!"));

layout.add(bell);

Frame mainFrame = new Frame();
mainFrame.add(layout);

Void asynchronous JavaScript executionโ€‹

With 24.11, it's now possible to asynchronously call a specified JavaScript function or execute provided JavaScript code without returning a result to the server. This can be done on a specific Element, or within the entire app by utilizing the Page instance.

Frame mainFrame = new Frame();
/* Executes async JS at the page level, independent of a specific element */
Page.getCurrent().executeJsVoidAsync("{console.log('Printing information to the console')}");

/* Adds a click listener to the button which can be invoked programmatically */
Element button = new Element("button", "Display User");
button.addEventListener("click", e -> {App.console().log("Button Clicked");});

/* Will programmatically click the button */
button.callJsFunctionVoidAsync("click");
mainFrame.add(button);

Using the Page for HTML event registrationโ€‹

In addition to using the Page class to execute JavaScript, support has been added to allow HTML events to be registered directly through the Page instance, without needing to use a specific Element as a bridge. This is particularly useful when an event should execute page-wide, broadening the scope of capability.

PageEventOptions pageEventOptions = new PageEventOptions();
pageEventOptions.addData("clientX", "event.clientX");
pageEventOptions.addData("clientY", "event.clientY");
ListenerRegistration<PageEvent> r1 = Page.getCurrent().addEventListener("click", e -> {
int x = (int) e.getData().get("clientX");
int y = (int) e.getData().get("clientY");

console().log("x: " + x + ", y: " + y);
}, pageEventOptions);