I am using Sencha's GXT 3.0 to render a ListViewCustomAppearance with a custom render defined through XTemplate (html/css). It works well when things are read only or when the interaction occurs on the list-level (ie. item select, item deselect).
How do I add mouse events to elements defined in the template's markup? My end goal is to have a clickable element inside an item in the list. The event handler would be in java.
Please let me know if this is going about it the wrong way..
ListViewCustomAppearance is useful when you want to completely restructure the child dom, not just how each child gets drawn. By making one of these, you are taking over responsibility of deciding how to handle selection (see the com.sencha.gxt.widget.core.client.ListViewCustomAppearance.onSelect(XElement, boolean) method).
This is important because ListView already knows how to manage selection! You can set and configure a ListViewSelectionModel on the ListView itself to handle a variety of interactions, and can listen there for events.
Instead, consider just making a custom Cell (probably starting from the AbstractCell class) that renders your data how you want it to look. Override the render method to specify how to append new content.
One more tip - the ListViewSelectionModel idea may not work for you, depending on what kind of data you are trying to get about user interaction. Instead, consider also overriding the onBrowserEvent method - this gives you the model object of the item that was modified, a reference to the root of the rendered content (from your render method), and the event itself. Use the event object to see what happened (test the event.getType() against a string event type). When subclassing AbstractCell, you also need to pass the event you are interested in to the superclass constructor.
Check out ActionCell as a pretty good example of how to rough this out. It is designed to be abstract enough to be reusable, but that also tends to make it a poorer example.
Related
I have tried to adhere to the mvc pattern in the java code that I have written. I want to keep the view and model independant of the controller and from each other. I am also trying my best to use interfaces to keep things modular.
My view has JButtons that I want to "bind" to specific methods in the model through the controller. I have tried two approaches to still keep the view independent of controller:
Registered the controller as an ActionListener to the view-object. Pros: The void addListener(ActionListener listener) abstract method encapsulates this behaviour in the view interface, so more buttons can be added without changing the interface. It also decouples the controller from the view, since it does not know anything about the actual buttons in the view. It is also easier to add more views or more controllers, so it follows the open-closed principle well. Con: For the controller to know the nature of the ActionEvent, I have only been able to come up with using the ActionEvent.getActionCommaned() in a switch statement, so mapping each case to its' corresponding method in the model. This seem unsafe and prone to cause irritation, since it seems like bad practice to match strings like this.
I also came up with adding public addActionListener(ActionListener) methods for each button in the view, like addGasButtonListener(ActionListener). Then doing something like view.addGasListener(e -> model.gasVehicles(view.getGasAmount())) in the controller. Pro: This seems like it reduces the uncertainty of matching strings. Cons: It means I have to add these new methods to the view interface, and each time a new button is added I have to do the same, making it difficult to add new views that does not have anything to do with some of the methods.
Main question:
I prefer the first method since it seems better to me, but I still would like to find a way to avoid matching strings, and find some nice way to do it (more similar to the rigidity of the second way of binding buttons to methdods). Is there?
Follow-up (less important) question:
I should also mention that my controller contains an inner class TimerListener implements ActionListener, that contains the loop of the app that acts on ActionEvents from the Timer. It also acts on ActionEvents from buttons if approach number one is used, which is why my follow-up question is: how would I seperate this main loop that only cares about the timer, from the switch statement used to act on button events. Since the ActionEvent.getActionCommand() is null almost all the time, it would be nice to seperate the two. Is there a approach that would let me do that?
I don't really see the value of working with ActionListener instances in the first place.
From your own example:
view.addGasListener(e -> model.gasVehicles(view.getGasAmount()))
You don't do anything with the actual event
In case you decide to use a different UI element in the future that doesn't fire ActionEvents, you will have to change the type of the listener.
I would rather expose a custom listener/callback that the controller (or model) can register on the view:
void onGasAmountChange(double gasAmount);
That being said, you might be focusing too hard on having a model, view and controller. If you look at more complicated Swing components (JList, JTable, ...), they have a view component and a model (ListModel and TableModel).
You could do something similar and create your own model for your view, and let the view call the necessary methods on the model directly (just like it happens for those Swing components).
That avoids having to write a bunch of one-liner listeners that basically delegate to the model.
I am making a simple notepad application in Java and trying to use a fluid and immutable style, for the sake of trying it out. I have found it a lot of fun and am seeing lots of the great advantages that get talked about in regards to functional programming (code clarity etc.).
But I have an issue with event handling in general. I have one class TextArea that provides a Notepad-like document like you'd expect. Then I have another class ScrollBar . They are manipulated by a master class CentralController that keeps the scroll bar and text area working together (not the real class name, it's just for this example).
So if the user presses the down arrow, CentralController simply calls TextArea.withDownArrow() and that returns a new copy of the TextArea with the cursor moved down. The problem is the Scroll Bar now needs to be moved, so the CentralController needs to know whether the TextArea got scrolled by that down arrow.
My first attempt to solve it was to return an object that contained not only the new TextArea, but also a flag saying whether the scroll needs updating. That worked well but didn't feel right because I was no longer returning the TextArea whereas you really should in 'proper' functional programming (roughly speaking).
So then I tried having a flag inside TextArea that would get set if TextArea.withDownArrow() caused scrolling. That also works well but seems wrong to have a method result stored 'globally' in the class. It also has issues where you might call withDownArrow() twice and then the flag gets overwritten with a new result.
I have read a bit about Reactive Programming and it does seem interesting, but I'm not sure of it's suitable for this situation where you have a 'child' class sending a message to a sibling.
Just to add, I am under the impression normal event handling won't work in this situation. With immutable objects when you change something you create a new object. So any objects that try to send an event to a listener will be sending to an old pointer.
Am I missing an obvious way to do this because I feel like I am? Or is it actually ok to use normal Java event handling techniques and I'm worried about nothing?
Edit: I think I have worked out a good enough solution now. Even though the class that receives events (ScrollBar) is recreated all the time, the members of that class do not get recreated. Only things that change.
So I will just have an simple event receiver method in ScrollBar, and TextArea can have a list of listeners (basically the 'normal' way of doing events with listeners).
In summary my error was thinking I needed to send an event to a instance, rather than a member of that instance.
You have to differentiate between value objects and logic objects. Value objects contain nothing but values, no logic(*). They can be immutable.
But of course a text area cannot be a value object, nor can a scrollbar be a value object, because they must contain logic. Nor can they be immutable, because they contain state. So, scratch all that. It won't work.
(*) Or at least no logic that deals with external entities, or manipulates any of their own state.
What is the best way to support two views over the same data, with both views allowing edits on the data, in presence of various listeners in the UI and the underlying model.
Example, simple approach is to bind JTable with an AbstractTableModel instance, and pass reference of AbstractTableModel instance to the other view as well.
But if the other view chooses to change the underlying abstract table model instance, and in presence of some other listeners, like table model listener and some listeners on the JTable, it gets complicated very quickly, at least for me.
Not entirely sure, but it should be possible for some combination of these objects to lead to events being raised inadvertently.
I am not aware of any patterns in swing that can help with this problem, but apparently MVC is not encouraged in Swing.
In the past, I would have chosen to ensure that only the model raises events, and that changes the view, called the passive view pattern.
Your thoughts? (Swing and application design newbie here.)
I would recommend to centralize your data in some sort of cache object in something like a list, because imagine you get more then just two views to display the same data, it can get kinda messy to carry your tablemodel all around.
So simply let your cachelist inform about your data changes (update, create, delete...) by an own implementation of propertychangelistener or just with an eventbus. All interested views can sign up at your cache for data changes and get informed automatically. So neither of the views need to know each other which keeps your code kinda simple and easy to maintain and expandable for other views.
You can totally deviate from the listening scheme and switch to an EventBus based scheme, I don't think it is hard to replicate in a Swing framework. It's a concept from the GWT framework. Your application should initialize an event bus and its reference must be passed to those "objects" which need to broadcast events. It should be a "singleton" class that passes around the same instance of the EventBus.
So the data modifiers in your model can "post" an event into the eventbus and any UI can wish to receive the broadcast. So a change in the model by one of your tables will broadcast an "update" event which is received by your other table, which inturn updates its display.
Design your callbacks well from the idea and you should be having a very simple and clean solution.
I am having issues understanding which interface I need to listen to changes in a listModel. I thought it was ListDataListener, but I can't understand the methods in it. There are 3 methods in it and this one seems to be the one I want but I can't understand the description:
contentsChanged(ListDataEvent e)
Sent when the contents of the list has changed in a way that's too complex to characterize with the previous methods.
What does it mean by "too complex"? And by "characterize with the previous methods"? Well, what does the whole thing mean? Is this the interface I want?
ListModel dispatches events to its ListDataListener listeners. It's more efficient for the list model to invoke the detailed intervalAdded and intervalRemoved methods when possible. The Listener of the list model (in this case a JList) can use these detailed changes to make minimal changes to the visual component (ie, for intervalAdded it can just add the new row instead of redrawing the whole list).
However, some changes may be too complex to be described as with just added and removed. In this case, the list model has the option of invoking contentsChanged. When JList sees contentsChanged it will most likely refetch the entire list from the list model.
Yes, that's the right listener.
The statement means that this method is the more general one, that will cover every possible change to the list content. The other ones (intervalAdded and intervalRemoved) should be used when those specific events occur.
In my practice you will always use the most general one (even with table listeners).. I guess it was supposed to be used to optimize (especially with big lists).
A tutorial on this listener can be found here.
Just a note, I'm new to MVC.
I'm trying to make my code as much decoupled and testable as possible.
I have a view with a text box and button.
I want to enable the button when a new text is entered and respects a certain criteria.
Ideally, I'd like this logic that decides if the button is enabled or not outside the view so it can be unit tested.
My understanding of MVC goes like that:
In my View I have a reference to my Controller.
In my Controller I have a reference to my Model.
In my Model I have a reference to my View.
Can you tell me if the following is a good design.
I added a boolean to the model buttonEnabled.
the sequence of event is like that:
Text is input in the text box, the text box has a listener. The listener calls a textChanged method on the Controller, the controller does the checks on whether to enable the button or not, and then sets the buttonEnabled of the Model through a setButtonEnabled accessor.
The accessor changes the value of buttonEnabled, and calls a buttonEnabledChanged() on the view (which exposes that method)
the idea is that the view is specific observer of the model, and the model is an observable which could theoretically have multiple views, and can call buttonEnabledChanged() on all of them.
Please let me know what you think.
This is a philosophical answer to a philosophical question :)
What you suggest could be correct. But the real question is if buttonEnabled is really a good candidate for your model. It's a purely gui thing and makes no sense being there. Thing that are really specific to the interface belong in the view, and nowhere else.
Now there might be a reason that the button is disabled (like, entry is not valid). Then you could just give it another name in the model (isValid). The translation from !isValid to !buttonEnabled would then become part of the controller, or even the view itself.
But I'm guessing that, in your case, the only reason to block the button when there is no content is to make it less likely for the user to send in a blank form. In that case, I would do the check in view completely (javascript if it's web), just for user convenience. In the model, just throw an exception (IllegalArgumentException seems likely) if the empty string gets there anyway.
If you're unit-testing your model, it makes a lot more sense to test if it will complain about an empty string, then to check if your model is setting buttonEnabled to false. If you really want to test gui functionality, there are solutions for that (for web, selenium comes to mind).
What you suggest is overcomplicated and, in my opinion, wrong from the standpoint of MVC.
The controller should not check whether or not to enable button, it is the task of model.
The model should not call any methods on view.
You have too specific methods. This desire to update only specific stuff, like buttonEnabledChanged() will make things overcomplicated in future, where components depend on each other through some business logic.
What you need is to bind this text box's value to model value, perhaps through the controller. So, changing text boxes value will change model's value. It should then call the update on the view. The view knows, that in the model there is some property that determines if the button should be enabled. It shouldn't be called isButtonEnabled() because it is agnostic of the view. It should be called isTextMatchingCriteria or something. Based on the value of that property, the view decides whether to enable the button or not.
This way:
Controller only controlls. It is catches and delegates, updates, but doesn't decide anything on business logic.
The model is independent of view.
View doesn't have any specific methods that can be called separately. The only thing it can is to render a correct presentation based on the current state of the model. It also specifies, what one or another state of the model mean on the screen - a disabled button or error message. The model shouldn't do that.