Filter tree in Vaadin - java

I want to hide leafs from vaadin tree according to text in some editbox (on text change).
ie if text in editbox is "ab" i want to show only leafs with text starting with "ab".
and if text is empty, i want to show all leafs.
How can i make that?

You will have to filter the data container which is attached to the tree.
A new Filter API was introduced in version 6.6.0 which allows you to create custom Filters. I haven't tried the new API yet, but in your case it should work like this:
textField.addListener(new FieldEvents.TextChangeListener() {
void textChange(FieldEvents.TextChangeEvent event) {
// Remove existing filter (if any).
// This is OK if you don't use any other filters, otherwise you'll have to store the previous filter and use removeContainerFilter(filter)
dataContainer.removeAllContainerFilters();
// Create a new filter which ignores case and only matches String prefix
SimpleStringFilter filter = new SimpleStringFilter(propertyId, event.getText(), true, true);
// Add the new filter
dataContainer.addContainerFilter(filter);
}
});
in which textField is your "editbox", dataContainer is the data container which is attached to your tree and properyId is the property id of the container field which contains the text you want to filter.
Note that the above code is untested because I currently can't access the appropriate development tools.

Related

Filtering ObservableList with CheckBox in JavaFX

I'm building a log reader with JavaFX as a side project and have gotten to a dead end when trying to implement filtering on the TableView.
What I have is a few CheckBoxes (LHS of the picture) that will basically act as the filters for what the TableView will display:
Once the Submit button is clicked, a background thread is opened to read the and parse the files. Once the operation terminates, the result of each log read is inserted into the global ObservableList<Log>:
public class Test_Filters extends Application {...
private ObservableList<LogTest> logs = FXCollections.observableArrayList();
...}
What I'm having trouble with is how to deal with:
A situation where more than one filter CheckBox is checked.
A situation where the CheckBox is unchecked.
For 1., I was wondering what's the best way to deal with this. Let's say I have x filters selected. This would mean that I have to basically filter out x values from the ObservaleList:
logTable.setItems(logTable.getItems().filtered(log -> !log.getSource().equals(checkBox.getText())));
You can use JavaFX's FilteredList, which accepts a Predicate. You can update the predicate on each filter, combining them as you want.
FilteredList<LogTest> items = new FilteredList<>(originalItems);
tableView.setItems(items);
... on update of filter UI items
Predicate<LogTest> containsFoo = i -> i.getName().contains("foo");
Predicate<LogTest> isSevere = i -> i.getLevel() == Level.SEVERE;
Predicate<LogTest> filter = containsFoo.or(isSevere);
items.setPredicate(filter);
If you want to show all records again simply set the predicate to null:
items.setPredicate(null);
Using the FilteredList you don't need to re-read the log records, as the filter is applied immediately on the existing items.

smartgwt add blank row in ComboBoxItem or allow user to set blank value

I am trying to add blank row with null value in ComboBoxItem of smartgwt which is already bind to data source coming from database.
I have tried changes in service layer, controller layer and database too, but that was not recommended by my senior.
All I need to do changes on UI layer only.
Take a look at the Special Values ComboBox SmartGWT showcase demo. I think it does what you want.
Here is an extract taken from that demo's code (although I recommend you to look at it and study the code to see what's best in your case):
LinkedHashMap<String,String> hashMap = new LinkedHashMap<String,String>();
hashMap.put("**EmptyValue**", "None");
hashMap.put("-1", "Not Applicable");
ComboBoxItem comboBoxItem = new ComboBoxItem();
comboBoxItem.setName("filteredCombo");
comboBoxItem.setTitle("Choose an item (ComboBox)");
comboBoxItem.setAddUnknownValues(false);
comboBoxItem.setOptionDataSource(ItemSupplyXmlDS.getInstance());
comboBoxItem.setDisplayField("itemName");
comboBoxItem.setValueField("itemID");
comboBoxItem.setPickListWidth(300);
comboBoxItem.setPickListFields(skuField, itemNameField);
comboBoxItem.setSpecialValues(hashMap);
comboBoxItem.setSeparateSpecialValues(true);

Programmatically render template area in Magnolia CMS

I am using Magnolia CMS 5.4 and I want to build a module that will render some content of a page and expose it over REST API. The task is simple but not sure how to approach it and/or where to start.
I want my module to generate partial template or an area of a template for a given reference, let's say that is "header". I need to render the header template/area get the HTML and return that as a response to another system.
So questions are: is this possible at all and where to start?
OK after asking here and on Magnolia forum couldn't get answer I dug in the source code and found a way to do it.
First thing the rendering works based on different renderers and those could be JCR, plain text or Freemarker renderer. In Magnolia those are decided and used in RenderingEngine and the implementation: DefaultRenderingEngine. The rendering engine will allow you to render a whole page node which is one step closer to what I am trying to achieve. So let's see how could this be done:
I'll skip some steps but I've added command and made that work over REST so I could see what's happening when I send a request to the endpoint. The command extends BaseRepositoryCommand to allow access to the JCR repositories.
#Inject
public setDefaultRenderingEngine(
final RendererRegistry rendererRegistry,
final TemplateDefinitionAssignment templateDefinitionAssignment,
final RenderableVariationResolver variationResolver,
final Provider<RenderingContext> renderingContextProvider
) {
renderingEngine = new DefaultRenderingEngine(rendererRegistry, templateDefinitionAssignment,
variationResolver, renderingContextProvider);
}
This creates your rendering engine and from here you can start rendering nodes with few small gotchas. I've tried injecting the rendering engine directly but that didn't work as all of the internals were empty/null so decided to grab all construct properties and initialise my own version.
Next step is we want to render a page node. First of all the rendering engine works based on the idea it's rendering for a HttpServletResponse and ties to the request/response flow really well, though we need to put the generated markup in a variable so I've added a new implementation of the FilteringResponseOutputProvider:
public class AppendableFilteringResponseOutputProvider extends FilteringResponseOutputProvider {
private final FilteringAppendableWrapper appendable;
private OutputStream outputStream = new ByteArrayOutputStream();
public AppendableFilteringResponseOutputProvider(HttpServletResponse aResponse) {
super(aResponse);
OutputStreamWriter writer = new OutputStreamWriter(outputStream);
appendable = Components.newInstance(FilteringAppendableWrapper.class);
appendable.setWrappedAppendable(writer);
}
#Override
public Appendable getAppendable() throws IOException {
return appendable;
}
#Override
public OutputStream getOutputStream() throws IOException {
((Writer) appendable.getWrappedAppendable()).flush();
return outputStream;
}
#Override
public void setWriteEnabled(boolean writeEnabled) {
super.setWriteEnabled(writeEnabled);
appendable.setWriteEnabled(writeEnabled);
}
}
So idea of the class is to expose the output stream and still preserve the FilteringAppendableWrapper that will allow us the filter the content we want to write. This is not needed in the general case, you can stick to using AppendableOnlyOutputProvider with StringBuilder appendable and easily retrieve the entire page markup.
// here I needed to create a fake HttpServletResponse
OutputProvider outputProvider = new AppendableFilteringResponseOutputProvider(new FakeResponse());
Once you have the output provider you need a page node and since you are faking it you need to set the Magnolia global env to be able to retrieve the JCR node:
// populate repository and root node as those are not set for commands
super.setRepository(RepositoryConstants.WEBSITE);
super.setPath(nodePath); // this can be any existing path like: "/home/page"
Node pageNode = getJCRNode(context);
Now we have the content provider and the node we want to render next thing is actually running the rendering engine:
renderingEngine.render(pageNode, outputProvider);
outputProvider.getOutputStream().toString();
And that's it, you should have your content rendered and you can use it as you wish.
Now we come to my special case where I want to render just an area of the whole page in this case this is the Header of the page. This is all handled by same renderingEngine though you need to add a rendering listener that overrides the writing process. First inject it in the command:
#Inject
public void setAreaFilteringListener(final AreaFilteringListener aAreaFilteringListener) {
areaFilteringListener = aAreaFilteringListener;
}
This is where the magic happens, the AreaFilteringListener will check if you are currently rendering the requested area and if you do it enables the output provider for writing otherwise keeps it locked and skips all unrelated areas. You need to add the listener to the rendering engine like so:
// add the area filtering listener that generates specific area HTML only
LinkedList<AbstractRenderingListener> listeners = new LinkedList<>();
listeners.add(areaFilteringListener);
renderingEngine.setListeners(listeners);
// we need to provide the exact same Response instance that the WebContext is using
// otherwise the voters against the AreaFilteringListener will skip the execution
renderingEngine.initListeners(outputProvider, MgnlContext.getWebContext().getResponse());
I hear you ask: "But where do we specify the area to be rendered?", aha here is comes:
// enable the area filtering listener through a global flag
MgnlContext.setAttribute(AreaFilteringListener.MGNL_AREA_PARAMETER, areaName);
MgnlContext.getAggregationState().setMainContentNode(pageNode);
The area filtering listener is checking for a specific Magnolia context property to be set: "mgnlArea" if that's found it will read its value and use it as an area name, check if that area exists in the node and then enable writing once we hit the area. This could be also used through URLs like: https://demopublic.magnolia-cms.com/~mgnlArea=footer~.html and this will give you just the footer area generated as an HTML page.
here is the full solution: http://yysource.com/2016/03/programatically-render-template-area-in-magnolia-cms/
Just use the path of the area and make a http request using that url, e.g. http://localhost:9080/magnoliaAuthor/travel/main/0.html
As far as I can see there is no need to go through everything programmatically as you did.
Direct component rendering

play.data.Form - How best to fill default form values

Trying to add a filter form to a standard crud 'list' page.
// set filter defaults
ImageFilter defaultFilter = new ImageFilter();
defaultFilter.setYear(currentYear);
defaultFilter.setColor(user.getFavouriteColor());
// get filter for view
Form<ImageFilter> form = Form.form(ImageFilter.class).fill(defaultFilter).bindFromRequest();
ImageFilter filter = form.get()
bindFromRequest() alone gets a form with the filters a user specified, but on first load the fields have no selected value
fill(defaultFilter) alone does provide a form with the default filters selected
chained in this way the behaviour is just identical to the behaviour of the one called last.
What is the appropriate way to set the defaults?
Ideally, on the index page:
the default filter selection when not given any parameters
if requested with ?year=2010&color=blue those filters show on the form
if requested with ?year=2010 the form shows 2010 and the user's favourite color
Check if default values are present then go with the flow.
If not present, add set it in filter or render on UI using querystring params.
You can achieve what you want by splitting your code into two actions.
In the first one you fill a form with the default filter and display it to a user.
public static Result show() {
ImageFilter defaultFilter = new ImageFilter();
defaultFilter.setYear(currentYear);
defaultFilter.setColor(user.getFavouriteColor());
Form<ImageFilter> form = Form.form(ImageFilter.class).fill(defaultFilter);
return ok(index.render(form));
}
In the second action you simply bind form values from the request. If user changes some values form will be filled with them along with unchanged ones set as the defaults.
public static Result handle() {
Form<ImageFilter> form = Form.form(ImageFilter.class).bindFromRequest();
ImageFilter filter = form.get();
return ok("TODO handle form");
}
Edit
If you want to fill missing fields with the defaults after user input without showing them in the view before submit I'm afraid you'll have to do it manually after binding.

Items decorations in a TreeViewer

I have the following problem:
I'm preparing an editor in Eclipse and one of the tab contains TreeViewer to show items in the tree. Each item has a name and a value, which is editable.
The problem I need to indicate to user that value is incorrect (e.g. exceeds a given range). My idea is to decorate incorrect cells with a warning or error icon which will be shown also after editing is complete.
Does anybody have an idea how to decorate items in the tree? I was experimenting with ControlDecoration class but without success.
Thanks in advance,
Marcin
PS. I'm limited to Eclipse 3.4
There are two ways that this can be done. If your TreeViewer displays objects that are instances of EObject (generated by EMF. If your don't understand this part, skip to the next paragraph :)), you can change these EObject's "XyzItemProvider" so that their "getImage" method return a decorated image instead of the "plain" image... and that's it for EMF objects, nothing else needs to be changed.
If you're displaying "classic" Java Objects, you'll have to change your TreeViewer's LabelProvider in order to decorate the Image. This is done through the TreeViewer#setLabelProvider() method.
What you will need then is "how to decorate an Image", which is done through code such as this :
public class MyLabelProvider extends DecoratingLabelProvider {
public Image getImage(Object element) {
Image image = super.getImage(element);
List<Object> images = new ArrayList<Object>(2);
images.add(image);
images.add(<Image of the decorator>);
labelImage = new ComposedImage(images); // This will put the second of the "images" list (the decorator) above the first (the element's image)
return decoratedImage;
}
[...]
}
You then need to give your tree viewer this label provider :
TreeViewer treeViewer = new TreeViewer(...);
treeViewer.setLabelProvider(new MyLabelProvider(new LabelProvider()); // new LabelProvider()... or your previous label provider if you have one.

Categories