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

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.

Related

Passing values between classes in 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.

How do I use querySelector inside a class?

I think I'm lacking in a fundamental understanding of dart, but basically what I want to do is something like this:
void main() {
new MyClass();
}
class MyClass {
MyClass() {
CanvasElement canvas = querySelector("#myCanvas");
CanvasRenderingContext2D context = canvas.context2D;
}
}
However, canvas is a null object by the time I try to get the context. How can I do this from within the class. Also, I don't want to do this:
void main() {
CanvasElement canvas = querySelector("#myCanvas");
new MyClass(canvas);
}
class MyClass {
CanvasElement canvas
MyClass(this.canvas) {
canvas = this.canvas;
CanvasRenderingContext2D context = canvas.context2D;
}
}
Because I need to be able to do this completely from within the class. Is this just not how dart works, or am I missing something?
Did you try your second example? It doesn't make a difference if you call querySelector from main() or from within a class.
Do you use Angular or Polymer?
Angular or Polymer components introduce shadowDOM. querySelector() doesn't cross shadowDOM boundaries and it therefore doesn't find elements inside an elements shadowDOM.
To query for elements inside a shadowDOM you query for the component and then you can continue the search.
querySelector("somecomponent").shadowRoot.querySelector("someothercomponent").shadowRoot.querySelector("#myCanvas");
You have to ensure that the DOM including all shadowDOMs is fully built before you can query them.
If you run your code from within a component pub your code into the onShadowRoot method (see NgComponent ready event for more details)

toObservable doesn't seem to be working

I'm using Web UI to do observable data binding. Here is the brief snippet of code I'm working with:
import 'dart:html';
import 'dart:json';
import 'package:web_ui/web_ui.dart';
import 'package:admin_front_end/admin_front_end.dart';
//var properties = toObservable(new List<Property>()..add(new Property(1, new Address('','','','','',''))));
var properties = toObservable(new List<Property>());
void main() {
HttpRequest.request('http://localhost:26780/api/properties', requestHeaders: {'Accept' : 'application/json'})
.then((HttpRequest req){
final jsonObjects = parse(req.responseText);
for(final obj in jsonObjects){
properties.add(new Property.fromJsonObject(obj));
}
});
}
In index.html, I bind properties to it's respective property in the template:
<div is="x-property-table" id="property_table" properties="{{properties}}"></div>
In the first snippet of code, I'm populating the observable properties list, but it never reflects in the UI (I've stepped through the code and made sure elements were in-fact being added). If I pre-populate the list (see the commented out line), it does display, so the binding is at least working properly. Am I doing something wrong here?
The problem is most likely that you don't have any variables or types marked as #observable. In lack of observables, Web UI relies on call to watchers.dispatch() in order to update GUI.
You have following options:
1) import watchers library and call dispatch() explicitly:
import 'package:web_ui/watcher.dart' as watchers;
...
void main() {
HttpRequest.request(...)
.then((HttpRequest req){
for(...) { properties.add(new Property.fromJsonObject(obj)); }
watchers.dispatch(); // <-- update observers
});
}
2) mark any field of your x-property-table component as observable, or just the component type, e.g.:
#observable // <-- this alone should be enough
class PropertyTable extends WebComponent {
// as an alternative, mark property list (or any other field) as observable.
#observable
var properties = ...;
NOTE:
when a collection is marked #observable, UI elements bound to the collection are updated only when the collection object itself is changed (item added, removed, reordered), not when its contents are changed (e.g. an object in the list has some property modified). However, as your original properties list is an ObservableList, #observable annotation only serves here as a way to turn on the observable mechanism. Changes to the list are queued as a part of ObservableList implementation.
I think solution 2 (#observable) is better. As far as I know, watchers is the old way to track changes and will probably be removed.

Very basic AChartEngine XY

I've been trying for hours to get something as simple as displaying a line chart based on 2 dots that I supply manually and all I get is a crash. I've tried to understand how everything works based on the demo code but it's too complex. I'm not even concerned about writing nice code with onResume() etc, I just want something to display the first time I open the activity. Once I know how to do that I'll be able to adapt and learn what I need. Here's the code I came up with:
public class StatsActivity extends Activity {
private XYMultipleSeriesDataset StatsDataset = new XYMultipleSeriesDataset();
private XYMultipleSeriesRenderer StatsRenderer = new XYMultipleSeriesRenderer();
private XYSeries StatsCurrentSeries;
private GraphicalView StatsChartView;
protected void onCreate(Bundle savedInstanceState) {
setContentView(R.layout.stats);
LinearLayout layout = (LinearLayout) findViewById(R.id.Statschart);
StatsRenderer.setAxesColor(Color.YELLOW);
String seriesTitle = "Rank";
XYSeries series = new XYSeries(seriesTitle);
series.add(5, 7); //1st series I want to add
StatsDataset.addSeries(series);
series.add(9, 1); //the 2nd one
StatsDataset.addSeries(series);
StatsCurrentSeries = series;
System.out.println(series);
XYSeriesRenderer renderer = new XYSeriesRenderer();
renderer.setColor(Color.RED);
StatsRenderer.addSeriesRenderer(renderer);
StatsChartView = ChartFactory.getLineChartView(this, StatsDataset,StatsRenderer);
layout.addView(StatsChartView);
}
}
I've been reading the docs to determine what each function does but in the end I still can't get anything to display.
Thanks!
The big thing that I struggled with is that you need a renderer for each XYSeries. You have two series here, but just one renderer - I just create/add renderers when I input data. Also, Android is mostly pass-by-reference, so you've passed the same data set in twice (i.e. your second update to the data will be mirrored "in" the MultipleSeriesDataset).

How to ensure that the view is safe of null exceptions

I have in the controller a custom DataView object that comprises 2 lists. I populate them and than pass the DataView object as model for my view. In view when displaying the data I am checking for null reference. I wonder how to write unit tests to ensure that the programmer did not forget to check for null reference in the view. I would like to test the view for the exceptions of this type.
If somewone would give me a good sample with rhino mocks testing framework I would greatly apreciated, because I use it in mostly parts.
Thanks.
While I don't think the following is ideal, it will prevent NREs:
public class ViewModel
{
public IEnumerable<Item> Items
{
get { return items ?? new List<Item>(); }
set { items = value; }
}
}

Resources