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)
Related
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.
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
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.
Iv been trying to pass arguments through an addEventListener event in actionscript such as...
target.addEventListener("pComp", rakeSoil(target));
but i get errors.
Iv tried to google but no luck :/
Thanks for replying if you do :)
The target is already passed as part of the event, either event.currentTarget or event.target will be what you want.
If you want something else passed, create a custom event. Add the property to the custom event.
Try adding an additional method as your event listener:
target.addEventListener ("pComp", targetListener);
...
private function targetListener (event:Event):void {
rakeSoil (event.currentTarget);
}
How this is what you want:
{
var target:EventDispatcher = ...;
Function rakeSoil = function (e:Event):void
{
// handle target
}
target.addEventListener("pComp", rakeSoil);
}
rakeSoil is a first class function(or closure), when event is dispatched, it will be invoked, and you can access 'target' in it.
EDIT:
Have a look at Closure (computer science)
I have always found anonymous functions to be more trouble than they are worth. I would simply follow the standard event handler code layout. It's more formal and takes a little more effort up front, but there is no ambiguity and it is far more readable when you return to it a year from now (reduces head-scratching duration):
// Target extends EventDispatcher
private var target:Target;
public function listenToTarget();
{
target = new Target();
target.addEventListener("pComp", pCompHandler);
}
private function pCompHandler(event:Event):void
{
target.rakeSoil();
}
Although, now that I look at it more closely, why are you having this object do something that Target should be capable of handling internally on its own?
So I have an Application Sandbox HTMLLoader object which I create in AIR and simply want to call ActionScript methods from JavaScript. In Flash, this is accomplished through our trusty ExternalInterface.addCallback() function. However in AIR, things are quite a bit different, and I just can't seem to get it to work.
Here is a simplified overview of my project:
My AIR (ActionScript) main:
public class Main extends Sprite {
public var _as3Var:String = "testing";
public function as3Function():void
{
trace("as3Function called from Javascript");
}
public function Main() {
NativeApplication.nativeApplication.addEventListener(InvokeEvent.INVOKE, onInvoke);
}
protected function onInvoke(e:InvokeEvent):void {
NativeApplication.nativeApplication.removeEventListener(InvokeEvent.INVOKE, onInvoke );
var app = new App();
addChild(app);
app.init(new ExternalContainer(), e.currentDirectory, e.arguments);
}
}
And this is how I create my HTMLLoader object:
{
_html = new HTMLLoader();
_html.useCache = false;
_html.runtimeApplicationDomain = ApplicationDomain.currentDomain;
_html.load(new URLRequest("sandbox/AirRoot.html"));
_html.width = 800;
_html.height = 600;
App.ref.addChild(_html);
}
And at last, here is my snippet of JavaScript in my AirRoot.html file which is trying to call the public method as3Function() declared in my Main class:
Exposed.testAs3 = function()
{
air.trace("Exposed.testAs3 called"); /* This works fine. */
air.trace("runtimeVersion:"); /* This works fine. */
air.trace(air.NativeApplication.nativeApplication.runtimeVersion); /* This works fine. */
air.trace("seeing if I can get to AS3 params..."); /* This works fine. */
/* This doesn't work - get the following error: TypeError: Value undefined does not allow function calls. */
air.NativeApplication.nativeApplication.as3Function();
}
What am I missing?
OK, I am going to answer my own question. I promise this was not a ploy to gain more reputation points, but I was seriously confused today but have now found the appropriate answers and documentation - which is usually the main problem to many an engineer's question...
Anyway, the answer:
The AIR HTMLLoader object contains a magical property, HTMLLoader.window, which is a proxy to the JavaScript window object. So setting HTMLLoader.window = AS3Function; is one way - or in relation to my previously included example (assuming I setup a static property called Main which pointed to the Main class):
_html.window.as3Function = Main.as3Function;
And now in JavaScript I can just call as3Function as:
<script>
window.as3Function();
</script>
Another interesting property is the JavaScript "window.htmlLoader" object. It is a proxy to the AS3 HTMLLoader parent object, in my case, the _html object. From this you can access things in relation to the _html object from JavaScript.
I'm not sure if this is a change in the new version of AIR, but you no longer need to reference the window in the javascript call, you can just do this:
<script>
as3Function();
</script>