In Eclipse RCP, I am creating views for the Perspective using IPageLayout.addView(...)
But this way I don't have a reference to the view. Therefore I don't know how I can tell ViewA to update ViewB.
What's the best pattern to use here?
Besides what VonC has mentioned above, you can also use ISourceProviderListener if the changes you need are not triggered by selection.
Have ViewB implements ISourceProviderListener
Create an implementation of ISourceProvider and register it in the services
Have ViewA get the ISourceProvider and update it to trigger the changes in ViewB
Read the documentation on those interfaces along with IServiceLocator and ISourceProviderService to get better idea how it all plays out.
You can also see this Lars Vogel's tutorial which has some example how to use the ISourceProvider
You have the different communication paradigm summarize in the IBM article
To make a view capable of listening to selection changes, a view must implement the ISelectionListener interface and must register itself with the workbench page
Using the IAdaptable interface: A class that implements IAdaptable has the capability to dynamically return certain types of adapters that can then be used to retrieve further information.
property change listener paradigm
Regarding the first approach, the article details:
A smarter way to consume UI selections is to register the consumer views as listeners to specific view parts. As you can see in the example below, the view ID of the source view part is mentioned as a parameter during registering a selection listener.
getSite().getPage().addSelectionListener("SampleViewId",(ISelectionListener)this);
This approach will eliminate the redundant callbacks to the consumer view that would otherwise occur if that view were registered as a nonspecific listener.
The code snippet in Listing 2 shows the createPartControl() method of a view that creates a JFace TableViewer and adds it as a selection provider to the workbench site. This code enables any UI selection changes in the TableViewer to propagate to the page and finally to the interested consumer views.
Listing 2. Setting up a selection provider
public void createPartControl(Composite parent) {
// Set up a JFace Viewer
viewer = new TableViewer(parent, SWT.MULTI | SWT.H_SCROLL | SWT.V_SCROLL);
viewer.setContentProvider(new ViewContentProvider());
viewer.setLabelProvider(new ViewLabelProvider());
viewer.setSorter(new NameSorter());
viewer.setInput(getViewSite());
// ADD the JFace Viewer as a Selection Provider to the View site.
getSite().setSelectionProvider(viewer);
}
You will find a similar approach in the RCP tutorial for eclipse3.5 (update February, 4th 2010)
There are different ways for view and plugin communications: eventbroker, listener etc..
EvenBroker (e4) Implementation:
Use eventbroker to send message (string) between views and plugins.
Sender Side:
#Inject
private IEventBroker eventBroker;
private static final String STATUS ="status";
eventBroker.send(STATUS, "status test message..");
Receiver Side:
#Inject
private IEventBroker eventBroker;
private static final String STATUS ="status";
#Inject #Optional
public void getEvent(#UIEventTopic(STATUS) String message) {
... //call method
}
Related
My Vaadin 14's mainpage is a MainView with the root Route.
MainView is used as a "template" for the other View (with layout = MainView.class) so I see it more like a "abstract" view that should not be initialized by itself and is only used for the other views as layout.
Now the issue: If a user accesses the MainView the BeforeEnterEvent is called AFTER the constructor. This may lead to exceptions thrown because the user is not authenticated yet and the constructor executes stuff like building the Tabs already.
Is there a way to prevent the user from accessing the route of the MainView or an event that is executed before the constructor is called? Accessing the View is only allowed if the user is authenticated.
#Route("")
public class MainView extends AppLayout implements BeforeEnterObserver {
public MainView() {
super();
// Creates all the Tabs that are used in the MainView, may throw exception if the user calls the URL of this View before authenticated
setupView();
}
...
#Override
public void beforeEnter(BeforeEnterEvent event) {
// Reroute to Login if User is NOT authenticated
}
}
#Route(value = "foo", layout = MainView.class)
public class OtherView {
Update:
The fix is released as experimental feature in Vaadin 14.2.
The issue with instances being created too early was actually closed a few hours ago. It will take some time before it's released.
That being said, an instance method can't possibly be called before the constructor, so it does not fix your particular case.
I would suggest moving your view setup code to onAttach. If you only want to run the setup code once, you can use AttachEvent#isInitialAttach to only execute your code on the first attach.
Once the issue I linked above is released, you can have the code in the constructor, but the instance that has the observer method will still be created before beforeEnter is called, just not the child view instances.
To not have any views created, you can add listener directly to the UI using UI#addBeforeEnterListener as soon as the UI is created, utilizing a UI init listener. Again, only when the fix has been released.
Your code may have a security issue, as described in the tutorial series for spring security with Vaadin. it is explained how to secure Views in the VaadinServiceInitListener instead.
But the proposed solution also adds a beforeEnterListener to the Views, so I don't think your problem is resolved with this.
A solution to your problem may be to throw a custom Exception (let's call it NotAuthorizedException for further reference) in the constructor of MainView if the user is not authorized. Then you let your LoginView implement HasErrorParameter<NotAuthorizedException>
I was able to fix it temporarely with an additional check for authentication. It may not be the best solution but it works for now. In the future #Tazavoo's answer should be implemented.
public MainView() {
super();
if (!isAuthenticated())
return;
setupView();
}
I'm doing a Swing app in Java with a database. I got different JButton which delete, add or modified row in a JTable.
I'm stuck because I don't know where I can manage my action listener.
I have a class named DaoClef where I will execute an SQL statement, send my table model etc...
I got a view where all my button are set but I don't know what to do in my controller.
For now I got this in the view:
btnNew.addActionListener(new ActionListener() {
#Override
public void actionPerformed(ActionEvent arg0) {
// check for selected row first
if (t.getSelectedRow() != -1) {
// remove selected row from the model
String sqlMaj = "DELETE FROM clefs WHERE IdClef = ?";
try (PreparedStatement pst = Connect.getConnection().prepareStatement(sqlMaj) )
{
pst.setInt(1, (int) t.getValueAt(t.getSelectedRow(), 0));
pst.executeUpdate();
t.addNotify();
}
catch (SQLException e)
{
}
}
}
});
But it's not good because the SQL statement need to be execute in a method in the controller.
Can you give me some clues?
From an MVC perspective, the issue here is that the Model and the View are mixed together. Since the Controller is supposed to act as an intermediary between the two, it has no role here.
The View, presenting the GUI (windows, buttons, text fields, etc.) to the user, should (ideally, more on this later) tell the Controller when a delete button has been pressed and its corresponding id. Then the Controller should pass this info to the Model which, in turn, will carry out the communication with the database using its connection and prepared statements.
Here's a good explanation on the MVC pattern:
Model-View-Controller: Does the user interact with the View or with the Controller?
Note (or maybe omit) that there's some controversy about different takes on the MVC pattern as shown in the different answers here.
There is also a good answer on the topic of MVC in Swing here:
Java and GUI - Where do ActionListeners belong according to MVC pattern?
Although the example given may be a little complex as the author tries to more strictly separate View and Controller. I'd go on and read the comments as well since they add further clarification.
I tried finding a simpler example but most didn't include action listeners. Here's a couple that do:
a) https://gist.github.com/Sammy30/7ebc606e7bb76cefac0f
b) http://www.fredosaurus.com/notes-java/GUI/structure/40mvc.html
The action listeners get added from the Controller unlike the first example linked. It's simpler but has the disadvantage of having Controller and View intertwined, which doesn't allow switching the latter easily (i.e., without modifying the Controller), which is one of the advantages of MVC.
The steps would be:
Preparation:
The View and Model get created (the Model may set its connection to the database now)
The Controller gets created with instances of the View and Model as properties
The Controller gets ready by setting action listeners in the View (or tells the View to set them, in which case the View should contact the Controller whenever an event happens)
Usage:
The user presses a button in the View
The Controller notices the button press through its action listener (or the View tells the Controller which button has been pressed)
The Controller decides what to do with the event. In this case, tell the Model that a record needs to be deleted
The Model receives the order and modifies the database, and returns a value with the updated rows count
The Model closes the ResultSet, Statement, and Connection
We have a FormEditor containing four pages: three FormPages and fourth page is XTextEditor as a source page.
Whenever user makes any changes (e.g. changing value in text box) on FormPages, we change EMF model content inside XTextDocument.modify() method as given below:
xtextEditor.getDocument().modify(new IUnitOfWork.Void<XtextResource>() {
#Override
public void process(XtextResource state) throws Exception {
IParseResult parseResult = state.getParseResult();
Assert.isNotNull(parseResult);
EObject rootASTElement = parseResult.getRootASTElement();
if (rootASTElement instanceof MyModel) {
XyzType t = ((MyModel) rootASTElement).getXyzType();
t.setName(name); <- ‘name’ is the new value entered on FormPage text box
}
}
});
Now, we want to get notifications in FormPages, whenever EMF model gets changed when user makes some changes on source page i.e. XTextEditor.
We tried adding IXtextModelListener and IXtextDocumentContentObserver to IXtextDocument; but these get called for every character entered in XTextEditor.
Our requirement is to get notifications only when values in EMF model get changed (and not for text formatting e.g. when whitespace is inserted/removed).
Can somebody please provide some pointers?
Regards,
Akhil
You can use the EMF Client Platform (ECP), which adds an implementation of an Observer Bus to an EMF model.
They implement an own validation service which does what you described:
ecp.view.validation
This is the validation service, which monitors the domain model and
calculates validation errors.
The validation service already uses the Observer Bus of ECP to register to EMF change events. The Observer Bus itself is implemented as an EContentAdapter listening to every change of the model. It already filters the change events and provides them following the Observer Bus pattern to the event bus which you can register. There you only get the events you registered to and not all events as for the EContentAdapter which you have to filter for yourself.
I think they mainly use it to validate models to show results in their EMF Forms GUI. However, you can use the services also standalone.
I need some widgets that share the same code base, thus I introduced an abstract class providing these shared members and methods and created an implementation which adds additional functionality. but every time I wanted to add this to a VStack I got the following error:
java.lang.AssertionError: A widget that has an existing parent widget may not be added to the detach list
in order to verify if this is b/c of the class hierarchy I created the same in a method, but I'm still receiving the same error. the code below should describe what I want to achieve.
public class Test extends VStack {
// constructor that adds button and a clickhandler for the button
// that will call addComplex()
public void addComplex() {
HStack stack = new HStack();
stack.setHeight("22px");
stack.addMember(new IButton("remove"));
stack.addMember(new ListBox());
DynamicForm form = new DynamicForm();
form.setFields(new TextItem());
stack.addMember(form);
addMember(stack);
}
}
when I just call
addMember(new Label(""));
I get no error.
furthermore, considering that the stuff in the method addComplex is in a separate class, and I add a new instance in the constructor of this Test-class, no error is thrown. the error is only thrown when I want to add the HStack via a button click
why can't I add this HStack to my VStack?
Update
it was b/c of the ListBox, which is a GWT component, and no SmartGWT component. this was the detail I missed.
can anybody tell me: why has a GWT widget an existing parent, and a SmartGWT widget does not? or is the error message just bogus?
the problem was that ListBox is from GWT, not SmartGWT. replacing it with the appropriate SmartGWT widget it works like a charm.
I have found, as a general rule:
Thou shalt not mix GWT and SmartGWT if thou seeketh functionality beyond the basic displaying of thine widgets.
I ran into this same exception when attempting to hide/show a DynamicForm or a VLayout that contained some plain GWT widgets... The page loaded fine, but I ran into trouble when I tried to add some hide/show interaction functionality...
In project I use MVP pattern. I have 2 view and 2 corresponding presenters. From "Presenter2" i want to get selected value in "View1". What is the best way to do it? I know that better to use event bus. But so i must to create 2 events and 2 event handlers (1st event will rise when Presenter2 need selected value from View1, and it will be handled in Presenter1. 2nd event will rise in Presenter1 (like: new selectedValueEvent(value) to notificate Presenter2 about selected value. So Presenter2 will handle selectedValueEvent(value) and get value).
If the point when the presenter needs to get the selected value is when the user makes an action you won't get around using an event. (Altough, maybe both presenters could react to the same event so don't need to use two different ones?)
If it is known when the presenter needs to get the value (a defined step in a workflow), you could to it like this:
Keep a reference to the views in the ClientFactory:
public class ClientFactoryImpl implements ClientFactory {
private static final EventBus eventBus = new SimpleEventBus();
/* The views */
private static final SampleView sampleView = new SampleView();
....
public ClientFactoryImpl(){
eventBus.addHandler(ReleaseAddedEvent.type, sampleView);
....
}
// getter and setters
}
So in the Presenter you can get a reference to the view: SampleView view = MyEntryPoint.getClientFactory().getSampleView(); and then you can just call a method from the view which returns the selected value.