Passing values between classes in Vaadin - vaadin

This might be more of a Java question, but how would you access values (say from a textfield) of a given view/class from a different class? For example if there was a TextField t1 that is in the MainView, and I wanted to get its current value for a computation in a different class. And is there a more Vaadin-specific approach here?

That can depend on the use case specifically. Since you mentioned a TextField value I assume the value is not yet stored in the DB, it's just on the UI yet -> I rule out singleton spring services.
A few ideas:
If the MainView and the different class are nested components and it's viable and not really complicated across a lot of classes... then probably passing it down the way when creating the sub-component. This is a naive solution - it can get pretty messy.
MainView() {
var t1 = new TextField();
var d = new Different(t1);
}
Fire and listen to Vaadin Component events. If you want really loose coupling, the most universal would be to use the UI instance as the event bus.
// listen in different class
ComponentUtil.addListener(attachEvent.getUI(), CloseMenuEvent.class, e -> closeMenu());
// fire change in MainView
ComponentUtil.fireEvent(ui, new CloseMenuEvent(ui))
A more specific version of number 2. is to pass the ValueChangeListener of the MainView's t1 to the different class.
MainView() {
var t1 = new TextField();
var d = new DifferentClass();
t1.addValueChangeListener(d::t1Changed)
add(t1, d);
}
Extract the common field to a third party, to a third class. Use a #UIScoped spring bean (#SpringComponent, #Service, ...) that will hold that field, and inject it to both MainView and the different class.
#Route
public class MainView extends VerticalLayout {
public MainView(Model m, Different d) {
add(m.t1, d);
}
}
#Scope(SCOPE_PROTOTYPE)
public class Different extends Component {
public Different(Model m) {
// something with m.t1
}
}
#UIScoped
public class Model {
public final TextField t1 = new TextField(); // TODO use getter
}
You could change the 4th approach by keeping String in Model and having a value change listener that updates it.

Related

Vaadin 21 flow. How to migrate CustomLayout used to have a panel with border

with vaadin 7 ( we are trying to migrate to v21, very, very, hard) we have this
CustomLayout cl1 = new CustomLayout(new ByteArrayInputStream("<fieldset><legend location='legend'></legend><div location='content'></div></fieldset>".getBytes()));
cl1.setSizeUndefined();
cl1.add(new Label(title), "legend");
cl1.add( panel, "content");
Basically is a panel with border and title-border
How we can do this in vaadin flow v21
Thanks in advance
There's a Cookbook recipe that provides an alternative for CustomLayout: https://cookbook.vaadin.com/custom-layout
Essentially, the CustomLayout replacement class extends Html in a fairly straightforward way. The add method has most of the logic:
public class CustomLayout extends Html {
private Map<String, Component> locations = new HashMap<>();
public CustomLayout(String template) {
super(template);
}
public CustomLayout(InputStream stream) {
super(stream);
}
public void add(Component child, String location) {
remove(location);
locations.put(location, child);
// Establish parent-child relationship, but leave DOM attaching to us
getElement().appendVirtualChild(child.getElement());
// Attach to the specified location in the actual DOM
getElement().executeJs("this.querySelector('[location=\"'+$0+'\"]').appendChild($1)", location,
child.getElement());
// Ensure the element is removed from the DOM when it's detached
child.addDetachListener(detachEvent -> {
detachEvent.unregisterListener();
getElement().executeJs("this.querySelector && this.querySelector('[location=\"'+$0+'\"]').lastChild.remove()", location);
// Also clear the bookkeeping
locations.remove(location, child);
});
}
public void remove(String location) {
Component oldChild = locations.remove(location);
if (oldChild != null) {
remove(oldChild);
}
}
public void remove(Component child) {
getElement().removeVirtualChild(child.getElement());
}
}
Note that it's important to do the bookkeeping with the locations Map so that client-side elements get removed too after the parent is detached.
Vaadin 10+ defines "elements" for most commonly used HTML tags, and has higher level abstractions for components built on top of those elements. It does not include an element or a component for <fieldset>. I'm not familiar with Vaadin 7, but it looks like it didn't come with it either.
There are a couple of ways to do what you want with Vaadin 10+. Here's a quick example based on extending the Component class:
#Tag("fieldset")
public class FieldSet extends Component {
private final Div enclosedComponents;
public FieldSet(String label) {
Element legend = new Element("legend").setText(label);
getElement().appendChild(legend);
enclosedComponents = new Div();
getElement().appendChild(enclosedComponents.getElement());
}
public void add(Component ... components) {
enclosedComponents.add(components);
}
}
I did not include a robust API. It would be more useful with a full compliment of add and remove methods, as well as a means to update the label.
As a point of learning 10+, know that the nature of fieldset makes this one more complicated. If this did not have to include the <legend> tag it could be far simpler, because you could simply extend Div or one of the several Layout classes and inherit a robust API.
There is a section of the documentation that outlines the various ways to solve these types of problems. I found it invaluable when I first started using Vaadin. It's not always clear when to use each of the approaches, but you'll get the feel for it.

how to initialise ViewModel in createFragment of FragmentStateAdapter?

I have a ExerciseDetailFragment that contains an ExerciseFragmentStatePagerAdapter to show a bunch of SupportUsFragmentCard. These cards can be swiped horizontally by the user.
In my ExerciseFragmentStatePagerAdapter I have the following code:
fun createFragment(position: Int): Fragment {
val exercise = exercises[position]
val card = SupportUsFragmentCard()
card.setExercise(exercise) <---- this is my question/problem
return card
}
As you can see the pager adapter just instantiates the SupportUsFragmentCard and for this the exercise needs to be passed along (the card displays some exercise information) via the setExercise.
The SupportUsFragmentCard looks like this:
open class SupportUsFragmentCard : RootFragment() {
...
val viewModel: SupportUsViewModel by viewModels()
...
fun setExercise(exercise: Exercise?) {
viewModel.setExercise(exercise) <---- passes on the exercise to the viewModel
}
It’s implementation passes along the exercise to the underlying viewModel. Which in its turn encapsulates this into a LiveData (on which the SupportUsFragmentPlain has observers but I omitted this code as its not the issue):
class SupportUsViewModel() : ViewModel() {
//interface to the view
fun getExercise(): LiveData<Exercise?> = exercise
fun setExercise(execise: Exercise?) {
exercise.value = execise. <--- updates the Livedata object
}
//underlying live data mutable values
private val exercise = MutableLiveData<Exercise?>(null)
}
When this code is executed, it crashes with exception:
IllegalStateException: Can't access ViewModels from detached fragment
I think because the createFragment ends up updating the viewModel without actually already being on screen. I also feel that this way of working does not respect the MVVM architecture.
What is the correct way in this scenario, using MVVM and LiveData to initialise the SupportUsViewModel with an Exercise in the ExerciseFragmentStatePagerAdapter's createFragment function?

Dart. Late initialize final variables

Is there way to late initialize for final variables. The problem is many values initialized with entry point to the class, which is not constructor. Hence they cannot be final right now. But in scope of particular class they will not be changed. For ex.
Controller controller;
double width;
void setup(final itemWidth) {
controller = MyController();
width = itemWidth;
}
Could it be possible? Right now I see only solution as a annotation. You might think it's for visual effect. But in fact it helps to avoid unpredictable flow during testing.
It is now possible to late initialize variables. For more information see Dart's documentation. The text below is copied from Dart's documentation:
Late final variables
You can also combine late with final:
// Using null safety:
class Coffee {
late final String _temperature;
void heat() { _temperature = 'hot'; }
void chill() { _temperature = 'iced'; }
String serve() => _temperature + ' coffee';
}
Unlike normal final fields, you do not have to initialize the field in its declaration or in the constructor initialization list. You can assign to it later at runtime. But you can only assign to it once, and that fact is checked at runtime. If you try to assign to it more than once — like calling both heat() and chill() here — the second assignment throws an exception. This is a great way to model state that gets initialized eventually and is immutable afterwards.

Actionscript, objects and event listeners

Say I have an object
var my_obj = new Object();
my_obj['key'] = value;
Is there a way to add an event listener to the value, like
my_obj['key'].addEventListener(Blah Blah);
I have a long(ish) list of buttons on the timeline (the layout is so different from section to section that just makes more sense to do on the timeline rather than trying to build the layouts via actionscript).
button1 = plays "frame label 1"
button2 = plays "frame label 2"
and so on....so I was just thinking of storing everything in an array
obj["button1"] = "framelabel1"
arr.push(obj);
Then I could just have one event handler for all of the buttons and use target to get the frame label value...
Yes, you can do exactly what you're asking in the exact way you've mentioned, here's an example:
var value:Sprite = new Sprite();
var my_obj = new Object();
my_obj['key'] = value;
So calling:
my_obj['key'].addEventListener(Event.ENTER_FRAME, _onEnterFrameHandler);
is exactly the same as calling:
value.addEventListener(Event.ENTER_FRAME, _onEnterFrameHandler);
If the value is an IEventDispatcher or extends EventDispatcher you can add a listener to it.
The question is rather obscure to me. My guess is that you need something triggered every time a value is set. If this is the case, then you have to create a custom class, and declare a getter-setter property there.
For instance:
package
{
import flash.display.Sprite;
public class TestAccessor extends Sprite
{
private var someVarValue:uint = 0;
public function TestAccessor()
{
super();
}
public function get someVar():uint
{
return someVarValue;
}
public function set someVar(value:uint):*
{
someVarValue = value;
// this is the place where someVar is set.
// do whatever else you like here,
// you may choose to dispatch an event from here if you need.
}
}
}
Back in AS1-AS2 era we had watch() and addProperty() for that purpose, but these times are long since gone. For good. :)

Has-A Relationship

public class Elevator ()
{
Button firstFloorbutton = ButtonFactory.getButtonInstance(this, 1);
Button secondFloorbutton = ButtonFactory.getButtonInstance(this, 2);
Button thirdFloorbutton = ButtonFactory.getButtonInstance(this, 3);
Button fourthFloorbutton = ButtonFactory.getButtonInstance(this, 4);
Fan fan1 = FanFactory.getFanInstance(this);
Light light1 = LightFactory.getInstance(this);
private void goUp()
{
.....
}
private void goDown()
{
......
}
.............
}
============================
public class Button()
{
Elevator E;
int floor;
public button (Elevator E, int floor )
{
this.E = E;
this.floor = floor;
}
public void buttonPressed()
{
//logic to determine which floor the elevator is currently at and determine whether to go up or down to reach "this.floor"
E.goUp(); // if need to go up
else
E.goDown() // if need to go down
}
}
==========================
public class ButtonFactory()
{
public Button getButtonInstance(Elevator E, int floor)
{
Button b =new Button(E, floor);
return b;
}
}
==================
public class FanFactory(){ .................}
=====================
public class LightFactory() { ........... }
==========================
What kind of relationship exist between the Elevator and Button class?
According to Kathy and Bert (SCJP) page 92 : HAS-A relationship are based on usage rather than inheritance. In other words, class A HAS-A B if code in class A has a reference to an instance of class B.
In my example Elevator class code have a reference to a instance of Button and Button have a reference to instance of Elevator class.
Can anyone please clarify on this.
The Elevator has a button. Actually, it has four, but with each of those buttons, there's a has-a relationship.
Has-a is a somewhat informal term used to refer to two more formal kinds of relationship: association and aggregation. In both cases, one party in the relationship has a pointer to the other, but they're distinguished by semantics: in an association relationship, the first party knows about the other, but doesn't completely dominate it (think you and a colleague, or a boss, or a subordinate), whereas in an aggregation relationship, the latter party is part of the former party, and has no independent existence (think you and your liver). In this case, i'd say the Button is more specifically on the subordinate end of an aggregation relationship with the Elevator, not merely an association relationship.
Other examples of association might be a Customer and a Salesman, or a Department and an Employee. Of aggregation, an Order and and OrderLine, or a Structure and a Component. Interesting corner cases are a Category and a Product, and an Order and an Invoice.
One practical consequence of the kind of relationship is object lifetime: in an association, if the first object dies, the second may live, but in an aggregation, it will die. Think about your Elevator: if you deleted one (or removed it from your live data structures and let it be garbage collected, at least), would you want the Buttons to survive?
It is a Has-A relationship, which is a simple way of remember the composition model. Button class is composed of Elevator class; i.e. Button class has a Elevator class.

Resources