I have a Task Flow with two views: listOfClients and newClient. Shown here:
listOfClients view has a table which I want to sort before rendering it. To do it, I want to create a SortEvent with the table as source (as shown in the docs, section 29.4.4 ), but I cannot access the table before rendering the view.
I call the method queueSortIdEvent in a pageFlow-scoped managed bean but findComponent cannot find the table (returns null ). Tried also view-scoped, same result.
My code in the bean is:
public UIComponent findComponent(final String id) {
FacesContext context = FacesContext.getCurrentInstance();
UIViewRoot root = context.getViewRoot();
final UIComponent[] found = new UIComponent[1];
root.visitTree(new FullVisitContext(context), new VisitCallback() {
#Override
public VisitResult visit(VisitContext context, UIComponent component) {
if(component.getId().equals(id)){
found[0] = component;
return VisitResult.COMPLETE;
}
return VisitResult.ACCEPT;
}
});
return found[0];
}
public void queueSortIdEvent(){
SortCriterion sc = new SortCriterion("ClientId", true);
List<SortCriterion> listSC = new ArrayList<>();
listSC.add(sc);
SortEvent new = new SortEvent(findComponent("t1"), listSC); // the table id is "t1"
new.queue();
}
Is there a way to queue the event before rendering the view?
Note: findComponent function works fine in other parts of the code, got it from here
My JDeveloper version is 12.1.3
What JDeveloper version are you using?
The findComponent won't work because nothing has been rendered yet. So your component isn't available (= doesn't exists yet) at that moment.
I think there is an easier way to do sorting.
On your page/fragment, go to 'Bindings', select your iterator (middle column), click on Edit (pencil icon) and set the sorting you want in the 'Sort criteria' tab.
Related
I am quite new to Wicket. I am adding a model to a sub-panel(ChartPanel) from a main panel (MainPanel) on a button click.
MainPanel.java
On button click, I am re-adding the chartPanel after I change its model. Following is the code I am using in the buttonClick of the MainPanel. Here the onRenderAnnotations event is generated on some click in the UI.
#OnEvent
public void onRenderAnnotations(RenderAnnotationsEvent aEvent)
{
LOG.trace("clicked on the annotation");
renderChart( aEvent.getRequestHandler());
}
private void renderChart(IPartialPageRequestHandler aRequestHandler)
{
MultiValuedMap<String, Double> recommenderScoreMap = getLatestScores(aRequestHandler);
Map<String,String> curveData = new HashMap<String,String>();
LearningCurve learningCurve = new LearningCurve();
for (String recommenderName : recommenderScoreMap.keySet()) {
String data = recommenderScoreMap.get(recommenderName).stream().map(Object::toString)
.collect(Collectors.joining(", "));
curveData.put(recommenderName,data);
learningCurve.setCurveData(curveData);
learningCurve.setMaximumPointsToPlot(MAX_POINTS_TO_PLOT);
}
chartPanel.setDefaultModel(Model.of(learningCurve));
// to avoid the error, A partial update of the page is being rendered
try {
aRequestHandler.add(chartPanel);
}
catch (IllegalStateException e) {
LOG.warn("Not updating the chart. " + e.toString());
setResponsePage(getPage());
}
}
ChartPanel.java
After this in the chartPanel, I want to use the updated model to add component inside the chartpanel. What would be the best way to do that?
I want to do something like this in the class ChartPanel:
#Override
protected void onRender()
{
super.onModelChanged();
LearningCurve newLearningCurve = getModel().getObject();
requestTarget = ???
String js = createJavascript(newLearningCurve);
requestTarget.prependJavascript(js);
}
My question is, in the above code how to get the request target since it is not an ajax request neither do I get it in the arguments. Should I use some other function where I also get a requestTarget. But I want it to be called every time the model of ChartPanel is updated from anywhere.
Pardon my ignorance. I have been trying for a few days but I am still stuck. I tried to explain it enough but if any information is missing, please comment and I will add it right away.
Thanks.
You should override renderHead() instead:
#Override
public void renderHead(IHeaderResponse response)
{
super.renderHead(response);
response.render(OnLoadHeaderItem.forScript(
createJavascript(newLearningCurve)));
}
This way your chart will be shown correctly regardless whether it was added due to an AjaxRequest or simply when the page is rerendered.
We want to achieve an RCP application which may have multiple windows (MWindow)
for distinct data. The Windows must be independent (unlike the Eclipse IDE new
window menu entry), but it must be possible to copy & paste, drag & drop things from
one window into another one. Imagine an application like Word where you can
have multiple documents open. We tried various approaches, but it is quiet
difficult to find out the right e4 way:
1. Creating a new E4Application for each window
Our first approach was to create and run a complete new E4Application for each
new window. But this sounds not to be the right e4 way. Also it is buggy: Key
bindings does not work correct and also the LifecycleManager is called for each new
application and therefor for each new window, which should not be.
E4Application application = new E4Application();
BundleContext context = InternalPlatform.getDefault().getBundleContext();
ServiceReference<?> appContextService = context.getServiceReference(IApplicationContext.class);
IApplicationContext iac = (IApplicationContext) context.getService(appContextService);
IWorkbench workbench = application.createE4Workbench(iac, display);
final E4Workbench implementation = (E4Workbench) workbench;
implementation.createAndRunUI(workbench.getApplication());
This seems not the right approach to do it.
2. The Eclipse IDE approach
In the Eclipse IDE you can go to the menu and click Window -> New Window which
will open a complete new top level window. But it is synchronized: Open the
same text file in both windows and editing it in the first one will alter it in
the other one too. Albeit we tried that approach by simply copy and pasting it
from org.eclipse.ui.actions.OpenInNewWindowAction#run():
// Does not work because we do not have the RCP3 workbench in RCP4.
final IWorkbench workbench = PlatformUI.getWorkbench();
final IWorkbenchWindow workbenchWindow = workbench.getActiveWorkbenchWindow();
final IWorkbenchPage activePage = workbenchWindow.getActivePage();
final String perspectiveId;
if (activePage != null && activePage.getPerspective() != null) {
perspectiveId = activePage.getPerspective().getId();
} else {
perspectiveId = workbenchWindow.getWorkbench().getPerspectiveRegistry().getDefaultPerspective();
}
workbenchWindow.getWorkbench().openWorkbenchWindow(perspectiveId, null);
It looks like that the Eclipse IDE uses the RCP3 compatibility layer. We didn't
found a way to obtain the IWorkbench object. Neither by
PlatformUI#getWorkbench(), nor via the application context, nor the bundle
context.
3. Clone the main window
We stumbled upon Opening multiple instances of an MTrimmedWindow complete with perspectives etc
n-mtrimmedwindow-complete-with-perspectives-etc and did a lot of trial and
error and came up with this muddy code:
class ElementCloningBasedCreator {
EModelService models = ...; // injected
MApplication app = ...; // injected
public void openNewWindow() {
MWindow originWindow = (MWindow) models.find("the.main.window.id", app);
MWindow newWindow = (MWindow) models.cloneElement(originWindow, null);
MPerspectiveStack newPerspectiveStack =
(MPerspectiveStack) models.find(the.main.perspective.stack.id, newWindow);
newPerspectiveStack.setParent((MElementContainer) newWindow);
addTo(app, newWindow);
// Clone the shared elements. If we don't do that the rendering somewhere
// deep in the rabbit hole throws assertion erros because the recurisve
// finding of an element fails because the search root is null.
for (final MUIElement originSharedElement : originWindow.getSharedElements()) {
final MUIElement clonedSharedElement = models.cloneElement(originSharedElement, null);
clonedSharedElement.setParent((MElementContainer) newWindow);
newWindow.getSharedElements().add(clonedSharedElement);
}
cloneSnippets(app, originWindow, newPerspectiveStack, newWindow);
newWindow.setContext(createContextForNewWindow(originWindow, newWindow));
newWindow.setToBeRendered(true);
newWindow.setVisible(true);
newWindow.setOnTop(true);
models.bringToTop(newWindow);
}
#SuppressWarnings({ "rawtypes", "unchecked" })
private void addTo(MElementContainer target, MUIElement child) {
child.setParent(target);
target.getChildren().add(child);
}
/**
* Clone each snippet that is a perspective and add the cloned perspective
* into the main PerspectiveStack.
*/
private void cloneSnippets(MApplication app, MWindow originWindow,
MPerspectiveStack newPerspectiveStack, MWindow newWindow) {
boolean isFirstSnippet = true;
for (MUIElement snippet : app.getSnippets()) {
if (ignoreSnippet(snippet)) {
continue;
}
String snipetId = snippet.getElementId();
MPerspective clonedPerspective =
(MPerspective) models.cloneSnippet(app, snipetId, originWindow);
findPlaceholdersAndCloneReferencedParts(clonedPerspective, newWindow);
addTo(newPerspectiveStack, clonedPerspective);
if (isFirstSnippet) {
newPerspectiveStack.setSelectedElement(clonedPerspective);
isFirstSnippet = false;
}
}
}
private boolean ignoreSnippet(MUIElement snippet) {
return !(snippet instanceof MPerspective);
}
private void findPlaceholdersAndCloneReferencedParts(MPerspective clonedPerspective, MWindow newWindow) {
List<MPlaceholder> placeholders =
models.findElements(clonedPerspective, null, MPlaceholder.class, null);
for (MPlaceholder placeholder : placeholders) {
MUIElement reference = placeholder.getRef();
if (reference != null) {
placeholder.setRef(models.cloneElement(placeholder.getRef(), null));
placeholder.getRef().setParent((MElementContainer) newWindow);
}
}
}
}
This code does not really work and we really need some hints/advices how to do
it right, because of the lack of official documentation. The questions open are:
Do we need to clone the shared objects and if not how do we prevent the
errors during rendering)?
We only saw code where the cloned elements are
added to the parent via getChildren().add(), but we found out that the
children din't get the parent automatically and it is null though. Is it the
right pattern to add the parent to the child too?
We have the deep feeling
that we are doing it not right. It looks way too complicated what we do here. Is
there a simpler/better approach?
You can use the EModelService cloneSnippet method to do this.
Design your MTrimmedWindow (or whatever type of window you want) in the Snippets section of the Application.e4xmi. Be sure that the To Be Rendered and Visible flags are checked. You may need to set the width and height bounds (and you may want to set the x and y position as well).
Your command handler to create the new window would simply be:
#Execute
public void execute(EModelService modelService, MApplication app)
{
MTrimmedWindow newWin = (MTrimmedWindow)modelService.cloneSnippet(app, "id of the snippet", null);
app.getChildren().add(newWin);
}
So I am experiencing some odd behaviour using Vaadin 7 and the ComboBox component. Essentially, what is happening is that when it first renders the form, it appears to neither have selected the null selection nor any of the items added.
I have attempted to recreate this behaviour with the following code and this demonstrates the issue.
#Override
protected void init(VaadinRequest request) {
final FieldGroup binder;
FormLayout form = new FormLayout();
form.setMargin(true);
setSizeFull();
setContent(form);
final Label output = new Label();
form.addComponent(output);
ComboBox box = new ComboBox("My Dropdown");
final PropertysetItem fields = new PropertysetItem();
fields.addItemProperty("country", new ObjectProperty<String>(""));
binder = new FieldGroup(fields);
binder.bind(box, "country");
box.addItem("aus");
box.setItemCaption("aus", "Australia");
box.addItem("uk");
box.setItemCaption("uk", "United Kingdom");
box.setRequired(true);
box.setImmediate(true);
box.setRequiredError("Country is required field");
Button submit = new Button("Submit", new ClickListener() {
#Override
public void buttonClick(ClickEvent event) {
try {
binder.commit();
output.setValue((String) fields.getItemProperty("country").getValue());
}
catch (CommitException e) {
Notification.show("fail!");
}
}
});
form.addComponent(box);
form.addComponent(submit);
}
By default the ComboBox has allow null selection set to true. So there is a blank entry, which represents a null value selection. However the ComboBox's value when first rendered neither represents the null selection nor one of the items but an empty string.
So when I load the form and click the button the outcome is neither a failure, which it should be because I haven't selected anything yet, nor one of my selections.
This is causing an issue for me in a more advanced UI application but very much the same thing going on here.
Could anybody enlighten me as to what is happening here?
Many thanks,
Joe
So when I load the form and click the button the outcome is neither a
failure, which it should be because I haven't selected anything yet,
nor one of my selections.
Combobox is not empty as you think. It has default property value, that you set as empty string:
fields.addItemProperty("country", new ObjectProperty<String>(""));
Thus form pass validation, because empty string is also a value and empty string != null.
Change this row:
fields.addItemProperty("country", new ObjectProperty<String>(""));
to:
fields.addItemProperty("country", new ObjectProperty<String>(null, String.class));
box.setNullRepresentation("-- Select Country --");
I need the URL for a component in wicket. When I use a page it works properly, but when using panel it does not work.
public final class ImageP extends Panel {
public ImageP(String id) {
super(id);
List<Mapp> list = Mapp.loadall(); //load image from database
final Mapp asr = list.get(0);
ByteArrayResource resource = new ByteArrayResource("image/jpeg", asr.getImage());
Image image = new Image("img", resource);
add(image);
System.out.println(getRequestCycle().urlFor(image, IResourceListener.INTERFACE));
}
}
This code does not work and throws an exception, but when I use page instead of panel getRequestCycle().urlFor(image, IResourceListener.INTERFACE) it works properly.
I bet you've got the following exception:
java.lang.IllegalStateException: No Page found for component [Component id = img]
It's because RequestCycle object internally calls getPage() method of the component that's first parameter of the urlFor() method with the following signature:
urlFor(Component component, RequestListenerInterface interface)
In case of calling method urlFor() in the constructor of a panel it's impossible to get page of a panel's child because panel isn't attached to page yet. So Wicket throws "a nice exception".
To fix that problem you just can move your code to the onBeforeRender() method of the panel. Something like that:
#Override
protected void onBeforeRender() {
//
// ... init resource ...
//
Image image = new Image("img", resource);
addOrReplace(image);
System.out.println(getRequestCycle().urlFor(image, IResourceListener.INTERFACE));
super.onBeforeRender();
}
P.S. I also assume that you're using Wicket 1.4 or earlier because there's no RequestCycle.urlFor(component, listener) method in Wicket 1.5 and later. So I think neither your question nor my answer doesn't make sense in that case.
In my RCP application, I have a View with a TreeViewer for Navigation on the left side and a Folder for my views on the right side. The Perspective looks like this:
public void createInitialLayout(IPageLayout layout) {
layout.setEditorAreaVisible(false);
layout.setFixed(false);
layout.addStandaloneView(NavigationView.ID, false, IPageLayout.LEFT, 0.7f, layout.getEditorArea());
right = layout.createFolder("right", IPageLayout.RIGHT, 0.3f, "com.my.app.views.browser.navigation");
layout.getViewLayout(WallpaperView.Id).setCloseable(false);//dummy view to keep the folder from closing
layout.getViewLayout(WallpaperView.Id).setMoveable(false);
right.addView(WallpaperView.Id);
//add some placeholders for the potential views
right.addPlaceholder(DefaultAdminView.ID+":*");
}
I would like to open different views, based on what the user selects in the navigation tree. Figured that wouldn't be to hard. My Navigation Tree view:
tree = new TreeViewer(composite);
tree.setContentProvider(new BrowserNavigationTreeContentProvider());
tree.setLabelProvider(new BrowserNavigationTreeLabelProvider());
tree.setInput(UserProfileAdvisor.getProject());
//register Mouselistener for doubleclick events
tree.addDoubleClickListener(new IDoubleClickListener(){
#Override
public void doubleClick(DoubleClickEvent event) {
TreeSelection ts = (TreeSelection) event.getSelection();
Object selectedItem = ts.getFirstElement();
String viewId = DefaultAdminView.ID;
//set viewId depending on the selectedItem.class
try {
PlatformUI.getWorkbench().getActiveWorkbenchWindow().getActivePage().showView(viewId, String.valueOf(++viewCounter), IWorkbenchPage.VIEW_ACTIVATE);
} catch (PartInitException e) {
ILogHelper.error("The view for the selected object could not be opened", e);
}
}
});
This seems to work fine. There's just one tiny problem:
I need to pass the object (let's say the selectedItem) to my view somehow, in order to let the user interact with its content. How do I do that?
I've seen some examples where some of my colleagues wrote an own View which they placed on the right side. Then they added a CTabFolder, instantiated the views and added them manually. Is there a smarter solution?
Create a new interface, giving it a method like accept( Object parameter ) and make your views implement it.
Then, when you do PlatformUI.getWorkbench().getActiveWorkbenchWindow().getActivePage().showView(viewId, String.valueOf(++viewCounter), IWorkbenchPage.VIEW_ACTIVATE) the method showView returns an IViewPart. Cast this return to your interface and call the accept method.
Use the SelectionService for that, please refer to Eclipse RCP let two views communicate
Implement the SelectionProvider in your "Navigation" and in the opened view you can ask for the selected object from the selection-service (see article)
HTH Tom