toObservable doesn't seem to be working - dart

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.

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.

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 can I draw a image loaded from an asset on a Canvas object

I'm toying with Flutter and I have implemented a custom-drawn widget using CustomPainter class.
Primitives are quite well documented. However, it seems that there's Image (the widget) and Image (the data structure), because if I istantiate an Image (the widget) I cannot pass it to the Canvas method drawImage because it needs another type of Image.
Problem is, I cannot wrap my head on how to load a resource into an Image data structure.
Has anyone tackled this problem?
[edit] Thanks to rmtmckenzie I solved it this way:
rootBundle.load("assets/galaxy.jpg").then( (bd) {
Uint8List lst = new Uint8List.view(bd.buffer);
UI.instantiateImageCodec(lst).then( (codec) {
codec.getNextFrame().then(
(frameInfo) {
bkImage = frameInfo.image;
print ("bkImage instantiated: $bkImage");
}
}
);
});
});
A working solution:
import 'dart:async';
import 'dart:typed_data';
import 'dart:ui' as ui;
import 'package:flutter/services.dart' show rootBundle;
/// Load [Image] from asset path.
/// https://stackoverflow.com/a/61338308/1321917
Future<ui.Image> loadUiImage(String assetPath) async {
final data = await rootBundle.load(assetPath);
final list = Uint8List.view(data.buffer);
final completer = Completer<ui.Image>();
ui.decodeImageFromList(list, completer.complete);
return completer.future;
}
To do this, you need to load the image directly, and then instantiate it.
I'll leave the loading part up to you - you're either going to have to do an Http request directly or use AssetBundle.load; or you could possibly use a NetworkImage/AssetImage (which both inherit ImageProvider - see the docs which contain an example).
If you take the loading directly route, you can then instantiate an image (in the form of a codec -> frame -> image) using instantiateImageCodec.
If you take the other route, you have to listen to streams etc as it is done in the docs, but you should get an Image directly.
Edit:
Thanks to the OP who has included the working code in his question. Here is what worked for him:
rootBundle.load("assets/galaxy.jpg").then( (bd) {
Uint8List lst = new Uint8List.view(bd.buffer);
UI.instantiateImageCodec(lst).then( (codec) {
codec.getNextFrame().then(
(frameInfo) {
bkImage = frameInfo.image;
print ("bkImage instantiated: $bkImage");
}
}
);
});
});

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)

Programmatic custom element broken in dart polymer 0.9.5?

The earlier solution for programmatic custom element creation in polymer 0.8.5 seems to be broken in polymer 0.9.5.
If we modify the standard click-counter example to use programmatic element creation, like so:
main() {
Logger.root.level = Level.ALL;
Logger.root.onRecord.listen((LogRecord rec) {
print('${rec.loggerName}: ${rec.level.name}: ${rec.time}: ${rec.message}');
});
initPolymer();
var clickCounter = new Element.tag('click-counter');
document.body.children.add(clickCounter);
}
the on-click events are correctly invoking the {{increment}} method, but the {{count}} value is not updated in the HTML.
Polymer code should be run from
import "package:polymer/polymer.dart";
main() {
initPolymer().run(() {
// code here works most of the time
Polymer.onReady.then((value) {
// some things must wait until onReady callback is called
// for an example look at the discussion linked below
});
});
}
simple tooltip working in dartium, not as javascript

Resources